メインコンテンツへスキップ

添付の実体(sys_attachment_doc)はbase64で分割保存


    ServiceNow でインシデントやナレッジに添付したファイルの「中身そのもの」は、どこに、どんな形で保存されているのか。添付のメタ情報(ファイル名・サイズ・種類・添付先)は sys_attachment(添付ファイルのメタ) に入ります。実際のバイト列は sys_attachment_doc(Attachment Document)という別テーブルに保存されます。base64 でテキスト化したうえで、複数のチャンク(断片)に分割して格納します。

    PDI(Personal Developer Instance / Zurich / OOTB)の実レコードで、1ファイルが何行のチャンクになるのかを確認しました。あわせて、チャンクの順番を表す position と本体の長さを表す length に何が入るのか、圧縮率でチャンク数がどう変わるのかも確認しています。メタ側(sys_attachment)の構造は分かったが、実体側の分割の仕方は見たことがない、という管理者・開発者向けの内容です。

    添付の保存は「メタ(sys_attachment)」と「実体(sys_attachment_doc)」の2テーブルに分かれています。この記事が扱うのは実体側です。メタ側のフィールド(table_name / content_type / size_bytes など)は、対になる記事「添付ファイルはどこに保存されるか(sys_attachment)」で扱っています。あわせて読むと役割分担がつかめます。


    画像

    この記事で分かること

    • 添付の実体は sys_attachment_doc に base64 チャンクで分割保存されること

    • 1つの添付が複数の doc 行(position 0,1,2…)になり、メタの約7倍の行数になること

    • sys_attachment_doc の固有フィールド(sys_attachment / position / length / data)と、compressed はここに無いこと

    • 大きなファイルが gzip → 圧縮後サイズで分割 → base64 され、PDI 実機の82MB CSV が42チャンクになること

    • 圧縮率が低い(zip など)とチャンク数が増えること(CSV 42 vs ZIP 106)

    • 極小ファイルでも doc が2行になり、先頭に長さ16の固定チャンクが入ること

    この記事の内容は ServiceNow の PDI(Personal Developer Instance)Zurich(ズーリッヒ)リリースの OOTB(初期設定)で確認した個人メモです。本番環境やカスタマイズ済み環境では、件数・分布・チャンクサイズが異なります。なお、添付の中身(data 列の base64 本体)は復元・表示しておらず、長さ(length)と順番(position)のみを確認しています。


    sys_attachment_doc とは(添付の実体テーブル)

    sys_attachment_doc(画面ラベルは Attachment Document)は、添付ファイルのバイト列そのものを保持するテーブルです。ファイルがそのまま1レコードに入るのではなく、base64 でテキスト化したうえで一定サイズのチャンクに分割し、1チャンク=1行として積まれます。

    メタ側の sys_attachment が「1ファイル=1行の台帳」なのに対し、sys_attachment_doc は「1ファイル=複数行の中身」です。両者は sys_attachment_doc.sys_attachment(参照)でつながります。


    画像

    今回の PDI(Zurich / OOTB)での件数は次のとおりです(確認日 2026-06-29)。

    • sys_attachment(メタ) … 2,858 件

    • sys_attachment_doc(実体チャンク) … 20,473 件

    件数は 2026-06-29 時点のものです。対になる添付メタの記事は 2026-06-21 の確認なので、同じテーブルでも件数が少しずれます。

    実体(doc)のほうがメタの約7倍(平均すると1添付あたり約7.16チャンク)あります。この比率そのものが、1つのファイルが複数のチャンクに分割保存されている構造を示しています。テーブルの継承関係(どちらも super_class が空の独立テーブル)は、対になる添付メタの記事で確認しています。


    sys_attachment_doc のフィールド

    sys_attachment_doc が持つ固有フィールドは、メタ側に比べてかなりシンプルです。中心は sys_attachment(親への参照)・position(順番)・length(長さ)・data(本体) の4つです。


    画像

    実機でフィールド定義を見ると、この4項目+sys列だけであることが分かります(先頭の <YourInstance> は自分のインスタンスに置き換えてください)。

    https://<YourInstance>/sys_dictionary_list.do?sysparm_query=name=sys_attachment_doc^elementISNOTEMPTY


    画像

    sys_attachment_doc には「圧縮されているか(compressed)」がありません。「圧縮後のサイズ(size_compressed)」「チャンクの分割単位(chunk_size_bytes)」も同様です。これらはすべてメタ側の sys_attachment が持つフィールドです。実際、sys_attachment_doc に対して compressed 列で集計しようとすると、ServiceNow は HTTP 403 を返しました。これは「その列は読めない(権限がない)」という意味です。実体側は並び順(position)と長さ(length)と本体(data)だけを持ち、圧縮やサイズの判断材料はメタ側にある、という役割分担です。

    なお、dict 上では data の最大長は 4000 と表示されますが、実際のチャンクの length は90万を超えるものもあります(後述)。表示上の定義値と実体の格納長は一致しません。


    1ファイルが複数チャンクに分かれる仕組み

    ServiceNow が添付ファイルを sys_attachment_doc に保存する流れは、次のとおりです。ファイルを gzip 圧縮し、圧縮後のバイト列を一定サイズで分割し、各断片を base64 でテキスト化して、1チャンクずつ position 順に保存する。


    画像

    これを今回の PDI で実測した最大の添付(82MB の CSV)で確認しました。

    • file_name … upgrade_manifest.csv / content_type … text/csv

    • size_bytes(元)… 82,668,286 バイト(約82MB)

    • size_compressed(gzip後)… 28,149,218 バイト(約28MB) / compressed … true

    • chunk_size_bytes … 700000

    • sys_attachment_doc のチャンク数 … 42 件(position 0〜41)

    冒頭のヒーロー画像が、この添付の実体チャンク群です。length 列を見ると、position 0 から 39 までがすべて 933,336 で揃っています。700000 バイトのチャンクを base64 化すると約 4/3 に膨らむため(700000 × 4 ÷ 3 ≈ 933,333)、計算とほぼ一致します。末尾の position 40(183,196)と 41(15,764)だけが端数です。先頭から固定サイズで切っていき、最後に余りが入る、という素直な分割になっています。

    82MB を 700KB チャンクで単純に割れば約118チャンクになるはずですが、実際は42チャンクでした。元サイズではなく gzip 圧縮後(28MB)を 700000 バイトで分割しているためです(28,149,218 ÷ 700000 ≈ 40.2 → フル41本+端数1本=42)。チャンク数は「元ファイルの大きさ」ではなく「圧縮後の大きさ」で決まる。実体側を見て初めて分かる点です。


    圧縮率が低いとチャンク数は激増する

    「チャンク数は圧縮後サイズで決まる」を裏づける対照例が、同じくらいのサイズの zip ファイルです。zip はすでに圧縮済みなので、gzip をかけてもほとんど縮みません。

    • 82MB CSV … 元 82MB → 圧縮後 28MB → 42チャンク

    • 74MB ZIP … 元 74MB → 圧縮後 73MB(ほぼ縮まない)→ 106チャンク

    ファイルサイズはむしろ zip のほうが小さいのに、チャンク数は CSV の2.5倍になりました。zip が「圧縮の効かないデータ」だからです。実体側の行数を見るだけで、そのファイルがよく圧縮できるテキスト系か、すでに圧縮済みのバイナリかを、ある程度推測できます。


    画像

    下は、その74MB ZIP の実体チャンク群です。同じ要領で position 順に並びますが、行数は 106 まで伸びています。


    画像

    なお、圧縮そのものをしない添付(compressed=false、例:巨大な js.map)も存在し、その場合 size_compressed は空でした。圧縮するか・どのくらい縮むかはメタ側(sys_attachment)が握り、その結果が実体側のチャンク数に効いてきます。メタ側の compressed / size_compressed の全体傾向(PDI では約98.6%が compressed=true)は、対になる添付メタの記事で扱っています。

    メタ側でファイルのサイズや圧縮設定を確認するときは、sys_attachment の一覧やレコードフォームを見ます。

    https://<YourInstance>/sys_attachment_list.do?sysparm_query=ORDERBYDESCsize_bytes


    画像

    一方、実体側の sys_attachment_doc を1行開くと、構造はとてもシンプルです。sys_attachment(どの添付か)・position(何番目のチャンクか)・length(このチャンクのバイト数)・data(base64 本体)の4つです。これだけで1チャンクを表します。下は最小級のチャンク(length が 8)のレコードフォームです。


    画像

    極小ファイルでも doc は2行になる

    逆に、とても小さいファイルではどうなるでしょうか。85バイトや151バイトの添付を実機で見ると、いずれも sys_attachment_doc は2行ありました。

    file_name    size_bytes  チャンク数  position / length
    ------------------------------------------------------
    ...xlsx.txt  85          2           position 0 = 16 / position 1 = 92
    image        151         2           position 0 = 16 / position 1 = 208
    .modelInfo   213         2           position 0 = 16 / position 1 = 212

    共通しているのは、position 0 の length がどれも16で、本体は position 1 に入っている点です。先頭に長さ16の固定チャンクが置かれ、そのあとに実データのチャンクが続く形です。82MB の CSV のような大きいファイルにはこの先頭16チャンクは見られなかったため、これは小さいファイル側で観測したパターンです。この先頭16文字チャンクが何を表すか(ヘッダかマーカーか)は内部仕様であり、今回の検証範囲外として断定はしません。ただ実務上は、「どんなに小さい添付でも sys_attachment_doc は1行とは限らない(最低でも2行になり得る)」とだけ覚えておくと、件数を数えるときに戸惑いません。


    メタ(sys_attachment)と実体(sys_attachment_doc)の役割分担

    ここまでを役割分担として整理すると、添付の調査やスクリプトが一気に見通しよくなります。

    • sys_attachment(メタ) … 1ファイル=1行の台帳です。「そのファイルが何で、どこに付いていて、どう圧縮・分割されるか」を記録します。

    • 主なフィールド(メタ側前半): file_name / content_type / size_bytes / size_compressed

    • 主なフィールド(メタ側後半): compressed / chunk_size_bytes / table_name / table_sys_id

    • sys_attachment_doc(実体) … 1ファイル=複数行の中身です。sys_attachment / position / length / data だけを持ち、「base64 チャンクを正しい順番で並べる」ことに専念します。

    添付を追うときは、まず「いまメタ(台帳)を見ているのか、実体(中身)を見ているのか」を意識します。ファイル名・サイズ・圧縮・添付先を知りたいなら sys_attachment を見ます。チャンクの並びや本体の長さを知りたいなら sys_attachment_doc を、sys_attachment 参照でつないで見ます。テーブルとフィールドの調べ方そのものは「ServiceNow のテーブルとフィールドを実機で確認する:sys_db_object と sys_dictionary」が土台になります。


    実機で確認するときに見るべきURL

    最後に、今回見た画面を自分のインスタンスで開くためのURLをまとめます(<YourInstance> は置き換えてください)。

    # 特定添付のチャンク群(position 順)
    https://<YourInstance>/sys_attachment_doc_list.do?sysparm_query=sys_attachment=<添付のsys_id>^ORDERBYposition
    
    # sys_attachment_doc の固有フィールド
    https://<YourInstance>/sys_dictionary_list.do?sysparm_query=name=sys_attachment_doc^elementISNOTEMPTY
    
    # 添付メタ(サイズ降順。size_compressed / compressed / chunk_size_bytes を見る)
    https://<YourInstance>/sys_attachment_list.do?sysparm_query=ORDERBYDESCsize_bytes

    実体側の一覧を開くときは、data 列(base64 本体)は中身が極端に長く、表示の必要もないため、列から外して position / length / sys_attachment を中心に見るのがおすすめです。


    まとめ

    • 添付の実体は sys_attachment_doc に base64 チャンクで分割保存される。1ファイル=複数行で、PDI では doc がメタの約7倍(20,473 / 2,858、平均約7.16チャンク)。

    • 固有フィールドは sys_attachment / position / length / data とシンプル。compressed / size_compressed / chunk_size_bytes はメタ側にあり、doc 側には無い。

    • 保存は gzip → 圧縮後サイズを 700000 バイトで分割 → base64。82MB CSV は圧縮後28MBで42チャンク、各 base64 長は933,336、末尾だけ端数(15,764)。

    • チャンク数は元サイズでなく圧縮後サイズで決まる。だから既圧縮の 74MB ZIP は106チャンクと、CSV より激増する。

    • 極小ファイルでも doc は2行になり、先頭に長さ16の固定チャンクが入る(内部的な意味は検証範囲外)。

    添付の中身がどこにどう入っているかが分かると、対になる添付メタ(sys_attachment)の記事とあわせて、「メタ(台帳)→実体(チャンク)」という添付の全体像がつながって見えてきます。プラットフォームが裏でデータをどう持つか、という意味では「監査と履歴(sys_audit)」のような他のシステムテーブルの読み方とも地続きです。

    関連記事

     
     
    ServiceNowのPDI(Personal Developer Instance)を実機で観察して、Noteに記事を書いています 実装・運用・教育の現場視点で淡々と記録中です リクエストがあればその記事も書きます 個人の観察日記です

    あなたへのおすすめ