Skip to main content

This documentation is for an older version of this product. See the latest version of this content.Opens in a new tab

SQL テーブルのストレージ・レイアウトの選択

Note:

InterSystems IRIS 2022.2 で列指向ストレージが試験的な機能として初めて利用できるようになりました。現在は、シャード・テーブルでの使用を除き、2023.1 から本稼働で使用できるように全面的にサポートされています。シャード・テーブルでの使用のサポートは、今後のリリースで提供される予定です。この機能の試験バージョンを使用していたお客様は、2023.1 にアップグレードした後、すべての列指向のテーブル・データを再読み込みする必要があります。

InterSystems IRIS® では、ここに示すようなリレーショナル・テーブルは論理的な抽象概念です。データの基礎的な物理ストレージ・レイアウトを反映したものではありません。

Table with 4 empty rows and columns OrderID, OrderDate, Customer, Priority, Status, TotalAmount

InterSystems IRIS の低レベル・データ構造であるグローバルに固有の柔軟性を使用して、、またはその両方のどれにデータを保存するかを指定できます。データのサイズおよびクエリとトランザクションの性質によっては、適切なストレージ・レイアウトを選択することにより、クエリのパフォーマンスやトランザクションのスループットが大幅に向上することがあります。

ストレージ形式の選択によって、クエリの作成方法と INSERT などの他の SQL 文の作成方法が変わることはありません。ストレージ形式は、シャーディングなどの他のストレージ・オプションと補完的な関係にあります。大規模なテーブルでは、シャーディングによってクエリのパフォーマンスをさらに改善できる可能性があります。どのストレージ・レイアウトでも、テーブルにインデックスを定義してパフォーマンス上の利点をさらに得ることができます。詳細は、"ストレージ・レイアウト上のインデックス" を参照してください。

行ベースのストレージ形式と列ベース (列指向) のストレージ形式を以下の表に示します。

行ストレージ (既定) 列指向ストレージ (2022.2 時点での試験的機能)

プライマリ・データは 1 つのグローバルに格納されます。データの各行は、さまざまなデータ型の要素に対応したリスト・エンコーディングを使用して、別々のグローバル添え字で格納されます。一般的に、トランザクションは個別の行を変更し、行単位で格納されているデータを効率的に処理しますが、分析クエリの処理は遅くなることがあります。

プライマリ・データは、列ごとに 1 つのグローバルに格納されます。64,000 個のデータ要素から成るシーケンスがそれぞれ別々のグローバル添え字で格納されます。データ型が同じ要素の格納に最適化されたベクトル・エンコーディングを使用してデータがエンコードされます。一般的に分析クエリの実行は高速ですが、トランザクションの処理は遅くなることがあります。

Table with data separated by row
Table with data separated by column

以下の場合に行ストレージを使用します。

  • トランザクションのデータをリアルタイムで処理するオンライン・トランザクション処理 (OLTP)。

  • データの頻繁な挿入、更新、削除。

  • 行全体を一括して選択し、迅速に実体化するクエリ。

以下の場合に列指向ストレージを使用します。

  • 特定の列のデータをフィルタ処理して集計し、分析クエリを実行するオンライン分析処理 (OLAP)。

  • 更新、挿入、削除の頻度が低いデータ、または LOAD DATA の使用などによってこれらの各操作が一括で実行されるデータ。

ストレージ・レイアウトの選択は厳正に科学的な作業ではありません。さまざまなレイアウトを試し、クエリ・テストを何回も実行して最適なレイアウトを探し出す必要がある作業です。ストレージ・レイアウトを行とするか列指向とするかを判断する作業の概要と、各レイアウトの選択事例は、動画 "What Is Columnar Storage?Opens in a new tab" を参照してください。

Note:

現在のところ、データに定義したストレージ・レイアウトは、データを再読み込みしない限り、変更できません。

行ストレージ・レイアウト

InterSystems IRIS では、InterSystems SQL DDL 言語または永続クラスでテーブルを定義するときに、ストレージ・レイアウトは既定で行ストレージになります。

DDL を使用した行ストレージ・テーブルの定義

InterSystems SQL DDL で CREATE TABLE コマンドを使用すると、既定でテーブルが行ストレージ・レイアウトで定義されます。CREATE TABLE には、オプションの WITH STORAGETYPE = ROW 節が用意されていて、列定義の後で指定できますが、省略してもかまいません。以下の 2 つの構文は同等です。

CREATE TABLE table (   column type,   column2 type2,   column3 type3) 
CREATE TABLE table (   column type,   column2 type2,   column3 type3) WITH STORAGETYPE = ROW

以下の CREATE TABLE コマンドでは銀行トランザクションのテーブルが作成されます。このテーブルには、口座番号、トランザクションの日付、トランザクションの説明、トランザクション金額、トランザクション・タイプ ("預金"、"引き出し"、"振り込み") の各列があります。WITH STORAGETYPE 節を省略しているので、このテーブルは既定の行ストレージになります。

CREATE TABLE Sample.BankTransaction (
  AccountNumber INTEGER,
  TransactionDate DATE,
  Description VARCHAR(100),
  Amount NUMERIC(10,2),
  Type VARCHAR(10))

永続クラスを使用した行ストレージ・テーブルの定義

DDL で作成するテーブル同様に、永続クラスを使用して作成するテーブルでも、既定で行ストレージ・レイアウトが使用されます。必要に応じて、値 "row" または空文字列 ("") を指定した STORAGEDEFAULT パラメータを定義できますが、どちらも省略してかまいません。以下の各構文は同等です。

Parameter STORAGEDEFAULT = "row"; 
Parameter STORAGEDEFAULT = ""; /* Or can be omitted entirely */ 

次の永続クラスは、前のセクションで作成した DDL 定義のテーブルと同様の行ストレージ・テーブルの定義を示しています。USEEXTENTSET パラメータを指定することで、テーブル・ストレージが、より効率的な一群のグローバルに編成されます。ビットマップ・エクステント・インデックスによって、エクステント・セットにあるすべての ID のインデックスが作成されます。これにより、カウントなどの操作の効率が向上します。DDL コマンドを使用してテーブルを定義すると、InterSystems SQL によってこれらの設定が自動的に適用され、投影された永続クラスに追加されます。詳細は、"永続クラスの作成によるテーブルの定義" を参照してください。

  Class Sample.BankTransaction Extends %Persistent [ DdlAllowed ]
  {
  Parameter USEEXTENTSET = 1;

    Property AccountNumber As %Integer;
    Property TransactionDate As %Date;
    Property Description As %String(MAXLEN = 10);
    Property Amount As %Numeric(SCALE = 2);
    Property Type As %String(VALUELIST = ",Deposit,Withdrawal,Transfer");

    Index BitmapExtent [ Extent, Type = bitmap ];
  }
Note:

パラメータ名 STORAGEDEFAULT にある DEFAULT は、このクラスにストレージ定義エントリを生成するときに、このパラメータ値が既定で使用されることを意味します。USEEXTENTSET や DEFAULTGLOBAL など、ストレージに影響する大半のクラス・パラメータと同様に、ストレージを生成するとき、クラスを初めてコンパイルするとき、または新しいプロパティを追加するときにのみ、この値が考慮されます。ストレージの XData ブロックに保存されたストレージ定義がすでに存在するクラスでは、これらのパラメータを変更しても、クラス・エクステントについて格納されているデータも含め、そのストレージには何の影響もありません。

行ストレージの詳細

行ストレージによるテーブルでは、すべてのデータが 1 つのグローバルに格納されます。添え字付きの各グローバルには、テーブルの各列の値から成る 1 行のデータを収めた $LIST 値が格納されます。$LIST データ型は、さまざまなデータ型の要素を格納し、空文字列 (' ') と NULL 値を効率的にエンコードします。このような特徴によって $LIST はデータの行の格納に適した型になっています。各列にはさまざまな型の値があり、NULL 値が存在することもあるからです。

前のセクションで取り上げた BankTransaction テーブルに、以下のトランザクション・レコードがあるとします。

SELECT AccountNumber,TransactionDate,Description,Amount,Type FROM Sample.BankTransaction
AccountNumber TransactionDate Description Amount Type

10001234

2022年2月22日

預金口座への預金

40.00

預金

10001234

2022年3月14日

ベンダへの支払い

-20.00

引き出し

10002345

2022年7月30日

当座預金への振り替え

-25.00

振り込み

10002345

2022年8月13日

預金口座への預金

30.00

預金

必要に応じて、管理ポータルで [システムエクスプローラ][グローバル] の順にクリックすることで、グローバル・ストレージ構造を検査できます。続いて、テーブルが置かれたネームスペースで [SQLテーブル名を表示] を選択して、テーブルに対応するデータ/マスタ・グローバルを探し出すことができます。詳細は、"グローバルの管理" を参照してください。以下のコードは、InterSystems IRIS の大半の標準テーブルで見られるデータ/マスタ・グローバルの例です。

^BankT    = 4
^BankT(1) = $lb(10001234,66162,"Deposit to Savings",40,"Deposit")
^BankT(2) = $lb(10001234,66182,"Payment to Vendor ABC",-20,"Withdrawal")
^BankT(3) = $lb(10002345,66320,"Transfer to Checking",-25,"Transfer")
^BankT(4) = $lb(10002345,66334,"Deposit to Savings",30,"Deposit")
  • ^BankT はグローバルの名前です。ここに示した名前は説明を目的としたものにすぎません。DDL を使用して作成したテーブル、または USEEXTENTSET=1 パラメータを指定して永続クラスで作成したテーブルでは、^EW3K.Cku2.1 のような名前で、より効率的なハッシュ化したグローバルが生成されます。永続クラス・テーブルで USEEXTENTSET=1 を指定していない場合、グローバルの名前は ^TableNameD の形式になります。SQL テーブルの投影された永続クラスでは、Storage クラス・メンバの <DataLocation> 要素にグローバルが格納されます。例えば以下のようにします。

    Storage Default
    {
    ...
    <DataLocation>^BankT</DataLocation>
    ...
    }
    
  • 最上位グローバル・ノードの値は、テーブルで最大の添え字です。この例では 4 になります。この整数の添え字は行 ID として使用されます。

  • 各グローバルの添え字は、1 行にある各列の値を収めた $LIST です。各要素値の順序はストレージ定義で定義され、普通は列の順序と一致しています。この例では、各行の 2 番目の要素は TransactionDate 列に相当し、そのデータは $HOROLOG 形式 (1840 年 12 月 31 日からの経過日数) で格納されています。

行ストレージによる分析クエリ処理

分析処理では行ストレージが効率面で不利であることを確認するために、BankTransaction テーブルにあるすべてのトランザクション金額の平均サイズを求めるクエリを実行してみます。

SELECT AVG(ABS(Amount)) FROM Sample.BankTransaction

このクエリに関連する値は Amount 列のみです。しかし、このクエリでこれらの値にアクセスするには、各行の全体をメモリに読み込む必要があります。InterSystems IRIS で読み込むことができるストレージの最小単位はグローバル・ノードであるからです。

Four 1-by-5 rows in blue. Element 4 of each row is in purple.

クエリが各行に個別にアクセスしているかどうかを確認するには、クエリ実行プランを分析します。管理ポータルで、[システムエクスプローラ][SQL][プラン表示] の順に選択します。または、SQL コマンドの EXPLAIN query を使用します。このクエリに "Read master map ... looping on IDKEY" のような文があると、テーブルの行にある各列の値がクエリに関連するかどうかに関係なく、各行が読み込まれます。

行ストレージでテーブルに対する分析クエリのパフォーマンスを改善するには、頻繁に使用するフィールドにインデックスを作成し、WHERE 節でフィルタ処理します。詳細は、"ストレージ・レイアウトのインデックス" を参照してください。

行ストレージによるトランザクション処理

トランザクション処理では行ストレージが効率的であることを確認するために、BankTransaction テーブルに新しい行を挿入してみます。

INSERT INTO Sample.BankTransaction VALUES (10002345,TO_DATE('01 SEP 2022'),'Deposit to Savings',10.00,'Deposit')

既存のどの $LIST グローバルもメモリに読み込むことを必要とせずに、INSERT 操作で新しい $LIST グローバルを作成します。

Five 1-by-5 rows. The first 4 rows are white. The last row is purple.

列指向ストレージ・レイアウト

列指向ストレージは、2022.2 での試験的機能です。InterSystems SQL DDL 言語または永続クラスを使用して、テーブル全体を列指向ストレージを持つものとして定義できます。

DDL を使用した列指向ストレージ・テーブルの定義

InterSystems SQL DDL で列指向ストレージによるテーブルを定義するには、CREATE TABLE で列定義の後に WITH STORAGETYPE = COLUMNAR を使用します。

CREATE TABLE table (   column type,   column2 type2   column3 type3) WITH STORAGETYPE = COLUMNAR

このコマンドによって、それまでに実行した口座トランザクションの履歴データを収めた TransactionHistory テーブルが作成されます。このテーブルには、"行ストレージ・レイアウト" のセクションで作成した BankTransaction テーブルと同じ列があります。

CREATE TABLE Sample.TransactionHistory (
  AccountNumber INTEGER,
  TransactionDate DATE,
  Description VARCHAR(100),
  Amount NUMERIC(10,2),
  Type VARCHAR(10))
WITH STORAGETYPE = COLUMNAR

永続クラスを使用した列指向ストレージ・テーブルの定義

永続クラスを使用して列指向ストレージ・テーブルを作成するには、STORAGEDEFAULT パラメータの値を "columnar" に設定します。

Parameter STORAGEDEFAULT = "columnar" 

次の永続クラスは、前のセクションで作成した DDL 定義のテーブルと同様の列指向ストレージ・テーブルを定義しています。"永続クラスを使用した行ストレージ・テーブルの定義" で作成した BankTransaction テーブルと同様に、このテーブルでは USEEXTENTSET パラメータとビットマップ・エクステント・インデックスが定義されます。この設定の詳細は、"永続クラスの作成によるテーブルの定義" を参照してください。

  Class Sample.TransactionHistory Extends %Persistent [ DdlAllowed, Final ]
  {
    Parameter STORAGEDEFAULT = "columnar";
    Parameter USEEXTENTSET = 1;

    Property AccountNumber As %Integer;
    Property TransactionDate As %Date;
    Property Description As %String(MAXLEN = 10);
    Property Amount As %Numeric(SCALE = 2);
    Property Type As %String(VALUELIST = "-Deposit-Withdrawal-Transfer");

    Index BitmapExtent [ Extent, Type = bitmap ];
  }

任意のテーブルを列指向として宣言できます。ただし、既定のストレージ・レイアウトとして列指向を使用するテーブルでは、Final クラス・キーワードまたは NoExtent クラス・キーワードを指定し、その直下のすべてのサブクラスを明示的に Final として定義する必要があります。

すでに説明したように、新しいテーブルまたは列のストレージ定義を生成するときに InterSystems IRIS でどのストレージ・タイプを使用するかを STORAGEDEFAULT パラメータで指定します。ただし、列のタイプによっては、列指向ストレージでの使用に最適化されたベクトル・データ型へ適切にエンコードされない列があります。このようにエンコードされない列として、シリアル、配列、リストなどがあります。このようなデータ型を列指向ストレージで使おうとするとコンパイル・エラーが発生します。以下の場合は、InterSystems IRIS によって自動的に行ストレージに戻されます。

  • 列タイプに列指向ストレージとの互換性がない場合。この互換性がないタイプの例として、ストリーム配列リストがあります。

  • 列指向ストレージに適切に収まらない列タイプの場合。この例として、13 文字以上の文字列があります。この場合は、列に STORAGETYPE = COLUMNAR 節を設定して、ストレージ・タイプをオーバーライドできます。詳細は、"混合ストレージ・レイアウト" のセクションを参照してください。13 文字以上の文字列に列指向ストレージを使用する必要がある例として、文字列のカーディナリティが低い場合が挙げられます。これは、対応できる文字列値がきわめて限られていることを意味します。すべての文字列が異なる場合は、行ストレージの使用が適切です。

列指向ストレージの詳細

グローバル構造

列指向ストレージによるテーブルでは、各列のデータセットが別々のグローバルに格納されます。列グローバルのそれぞれでは、行の値要素のデータ型がすべて同じで、64,000 行ごとに別々の添え字に "チャンク化" されます。これは、$BIT 値の格納方法と同様です。例えば、100,000 行で構成するテーブルでは、列グローバルごとに 2 つの添え字があります。1 番目の添え字には先頭の 64,000 行の値が格納され、2 番目の添え字には、残りの 36,000 行の値が格納されます。InterSystems IRIS では、特殊なベクトル・エンコーディングを使用して、同じデータ型のデータが効率的に格納されます。

前のセクションで定義した TransactionHistory テーブルに以下のレコードがあるとします。

SELECT AccountNumber,TransactionDate,Description,Amount,Type FROM Sample.TransactionHistory
AccountNumber TransactionDate Description Amount Type

10001234

2022年2月22日

預金口座への預金

40.00

預金

10001234

2022年3月14日

ベンダへの支払い

-20.00

引き出し

10002345

2022年7月30日

当座預金への振り替え

-25.00

振り込み

10002345

2022年8月13日

預金口座への預金

30.00

預金

必要に応じて、管理ポータルで [システムエクスプローラ][グローバル] の順にクリックすることで、グローバル・ストレージ構造を検査できます。このテーブルが置かれたネームスペースで [SQLテーブル名を表示] を選択して、テーブルに対応するグローバルを探し出すことができます。詳細は、"グローバルの管理" を参照してください。

データ/マスタ・グローバルは、各行の添え字を格納し、行の操作に関与するデータの参照に使用されます。データは列ごとに別々のグローバルに格納されるので、各添え字の行は空です。以下のコードはデータ/マスタ・グローバルの例です。

^THist = 4

^THist はグローバルの名前です。ここに示した名前は説明を目的としたものにすぎません。DDL を使用して作成したテーブル、または USEEXTENTSET=1 パラメータを指定して永続クラスで作成したテーブルでは、^EW3K.B3vA.1 のような名前で、より効率的なハッシュ化したグローバルが生成されます。永続クラス・テーブルで USEEXTENTSET=1 を指定していない場合、グローバルの名前は ^TableNameD の形式になります。SQL テーブルの投影された永続クラスでは、Storage クラス・メンバの <DataLocation> 要素にグローバルが格納されます。例えば以下のようにします。

Storage Default
{
...
<DataLocation>^THist</DataLocation>
...
}

このテーブルには、各列に 1 つずつ、合計で 5 つの追加のグローバルがあり、その名前は ^THist.V1^THist.V2 のような形式になっています。このグローバルのそれぞれには、1 つの列の各行の値がベクトル・エンコーディングで格納されています。これは、同じ型の値で機能して、スパース・データを効率的にエンコードするように設計された内部データ型です実際のエンコーディングは内部処理ですが、[グローバル] ページと ZWRITE などの情報コマンドによって、判読できる形式で以下の情報が提供されます。

  • データの型

  • 列にある、NULL ではない要素の数

  • ベクトルの長さ

このテーブルの行数は 64,000 未満なので、各列グローバルにある添え字は 1 つのみです。ここに示した各グローバルのデータは、読みやすくするために切り詰めています。

^THist.V1(1) = {"type":"integer", "count":4, "length":5, "vector":[,10001234,...]}
^THist.V2(1) = {"type":"integer", "count":4, "length":5, "vector":[,66162,...]}
^THist.V3(1) = {"type":"string", "count":4, "length":5, "vector":[,"Deposit to Savings",...]}
^THist.V4(1) = {"type":"decimal", "count":4, "length":5, "vector":[,40,...]}
^THist.V5(1) = {"type":"string", "count":4, "length":5, "vector":[,"Deposit",...]}

この列グローバルを 200,000 行のテーブルで作成すると、要素が 64,000 個の 3 つの添え字と要素が 8,000 個の 1 つの添え字にデータが分散します。列には NULL 値が存在するので、要素の数はグローバルの長さよりも小さくなります。

^MyCol.V1(1) = {"type":"integer", "count":63867, "length":64000, "vector":[,1,1,1,,...]}
^MyCol.V1(2) = {"type":"integer", "count":63880, "length":64000, "vector":[1,1,1,,1,...]}
^MyCol.V1(3) = {"type":"integer", "count":63937, "length":64000, "vector":[1,1,1,2,2,...]}
^MyCol.V1(4) = {"type":"integer", "count":7906, "length":8000, "vector":[1,1,1,,2,...]}

列指向ストレージによる文字列照合

既定で列指向ストレージを使用するテーブルに文字列型のフィールドを定義すると、別途指定しない限り、照合EXACT として定義されます。フィールドの MAXLEN が 12 文字を超えても文字列型のフィールドには EXACT 照合が使用されるので、そのフィールドは行ストレージに戻されます。

列指向ストレージによる分析クエリ処理

分析処理では列指向ストレージが効率的であることを確認するために、TransactionHistory テーブルにあるすべてのトランザクション金額の平均サイズを求めるクエリを実行してみます。

SELECT AVG(ABS(Amount)) FROM Sample.TransactionHistory

列指向ストレージでは、このクエリを実行すると Amount 列のグローバルのみがメモリにロードされ、その列のデータを使用して平均値が計算されます。他の列のデータはメモリにロードされないので、データが行単位で格納されている場合よりも効率的なクエリになります。また、最適化されたベクトル・エンコーディングは、個々の値に対してではなく、ベクトル全体に対して一度で効率的に実行される専用のベクトル化演算を備えています。例えば、すべての要素の和をベクトル内部で計算する方法は、要素を 1 つずつ加算する方法よりもはるかに高速です。特に、行データを保持している $list から値をその都度抽出する必要がある場合に顕著です。このようなベクトル化演算の多くは、低レベルの SIMD (単一命令、多重データ) チップセット最適化を活用しています。

Five 4-by-1 columns. Column 4 is purple. All other columns are white.

クエリ実行プランを分析することで、列指向ストレージの効率が活用されるクエリであるかどうかを確認できます。管理ポータルで、[システムエクスプローラ][SQL][プラン表示] の順に選択します。または、SQL コマンドの EXPLAIN query を使用します。"列指向インデックスの読み取り"、"ベクトル演算の適用"、"列指向データ/インデックス・マップ" などの文がクエリ・プランにあれば、そのクエリは列指向グローバルのデータにアクセスします。

列指向ストレージによるトランザクション処理

トランザクション処理では列指向ストレージが効率面で不利であることを確認するために、TransactionHistory テーブルに新しい行を挿入してみます。

INSERT INTO Sample.TransactionHistory VALUES (10002345,TO_DATE('01 SEP 2022'),'Deposit to Savings',10.00,'Deposit')

すべての列グローバルにわたって行データが分散しているので、挿入を実行するには、INSERT 操作でこれらのグローバルごとに最後のチャンクをメモリにロードする必要があります。

Five 5-by-1 columns in blue. The last element of each column is purple.

列指向ストレージ・レイアウトへのデータ挿入はメモリ効率の点で劣ることから、挿入の実行頻度を低くするか、LOAD DATA コマンドの使用などによって一括で挿入します。InterSystems IRIS には、列指向テーブルに対する INSERT をメモリにバッファリングしたうえでディスクにチャンクを書き込む最適化が用意されています。

混合ストレージ・レイアウト

さらに高い柔軟性を目指し、行ストレージと列指向ストレージが混在するテーブルを定義できます。このようなテーブルでは、テーブルの全体的なストレージ・タイプを指定したうえで、特定のフィールドを、それとは異なるストレージ・タイプを持つものとして設定します。

頻繁に集計するフィールドのように分析クエリの対象とする列が少ないトランザクションベースのテーブルで混合ストレージが効果的です。大部分のテーブル・データを行単位で格納したうえで、頻繁に集計する列を列指向形式で格納できます。フィルタ処理やグループ化などが適用されずに、通常は行レベルのクエリ結果にそのまま返される列も行ストレージに適していることがあります。これにより、クエリ結果に追加する前の列を実体化するコストを削減できます。この方法で 1 つのテーブルに対してトランザクション・クエリと分析クエリを実行できます。

DDL を使用した混合ストレージの定義

InterSystems SQL DDL で混合ストレージを持つテーブルを定義するには、CREATE TABLE コマンドで個々の列に WITH STORAGETYPE = ROW 節または WITH STORAGETYPE = COLUMNAR 節を指定します。

以下の構文で作成されるテーブルは、既定である行ベースのストレージ・レイアウトになりますが、3 番目の列では列指向ストレージを使用しています。

CREATE TABLE table (   column type,   column2 type2   column3 type3 WITH STORAGETYPE = COLUMNAR)

以下の構文で作成されるテーブルは列ベースのストレージ・レイアウトになりますが、3 番目の列は行レイアウトで格納されます。

CREATE TABLE table (   column type,   column2 type2   column3 type3 WITH STORAGETYPE = ROW) WITH STORAGETYPE = COLUMNAR

以下の CREATE TABLE コマンドで作成する BankTransaction テーブルでは、Amount 列のデータのみに列指向ストレージが使用され、他のすべてのデータは行レイアウトで格納されます。

CREATE TABLE Sample.BankTransaction (
  AccountNumber INTEGER,
  TransactionDate DATE,
  Description VARCHAR(100),
  Amount NUMERIC(10,2) WITH STORAGETYPE = COLUMNAR,
  Type VARCHAR(10))

永続クラスを使用した混合ストレージ・テーブルの定義

永続クラスを使用して混合ストレージによるテーブルを作成するには、個々のプロパティに STORAGEDEFAULT パラメータを指定します。このパラメータに有効な値は "columnar""row" (既定値) です。

Property propertyName AS dataType(STORAGEDEFAULT = ["row" | "columnar"]) 

以下の永続クラスは、列指向ストレージ・テーブルの定義を示しています。このテーブルは、前のセクションで作成した DDL 定義のテーブルと同等です。

  Class Sample.BankTransaction Extends %Persistent [ DdlAllowed, Final ]
  {
    Parameter STORAGEDEFAULT = "columnar";
    Parameter USEEXTENTSET = 1;

    Property AccountNumber As %Integer;
    Property TransactionDate As %Date;
    Property Description As %String(MAXLEN = 100);
    Property Amount As %Numeric(SCALE = 2, STORAGEDEFAULT = "columnar");
    Property Type As %String(VALUELIST = "-Deposit-Withdrawal-Transfer");

    Index BitmapExtent [ Extent, Type = bitmap ];
  }

混合ストレージの詳細

グローバル・ストレージ

混合ストレージによるテーブルでは、以下のような複数のグローバル・ストレージ構造の組み合わせを使用します。

  • 行ストレージ・レイアウトによるデータは、データ/マスタ・グローバルに $list 形式で格納されます。

  • 列指向ストレージ・レイアウトによるデータは、それぞれ別々のグローバルにベクトル・エンコーディングで格納されます。

以下の BankTransaction テーブルを考えます。

  Class Sample.BankTransaction Extends %Persistent [ DdlAllowed ]
  {
    Parameter STORAGEDEFAULT = "row";
    Parameter USEEXTENTSET = 1;

    Property AccountNumber As %Integer;
    Property TransactionDate As %Date;
    Property Description As %String(MAXLEN = 100);
    Property Amount As %Numeric(SCALE = 2, STORAGEDEFAULT = "columnar");
    Property Type As %String(VALUELIST = "-Deposit-Withdrawal-Transfer");

    Index BitmapExtent [ Extent, Type = bitmap ];
  }

このテーブルは、"永続クラスを使用した混合ストレージ・テーブルの定義" で取り上げた例とほとんど同じですが、Amount 列には列指向ストレージを使用し、他のすべての列には行ストレージを使用していることがわかります。テーブル・データの論理抽象化のこの例は、"行ストレージの詳細" と "列指向ストレージの詳細" で取り上げたテーブルと同じです。

SELECT AccountNumber,TransactionDate,Description,Amount,Type FROM Sample.BankTransaction
AccountNumber TransactionDate Description Amount Type

10001234

2022年2月22日

預金口座への預金

40.00

預金

10001234

2022年3月14日

ベンダへの支払い

-20.00

引き出し

10002345

2022年7月30日

当座預金への振り替え

-25.00

振り込み

10002345

2022年8月13日

預金口座への預金

30.00

預金

管理ポータルの [グローバル] ページに、このデータがどのように格納されているかが表示されます。データ/マスタ・グローバルには行のデータが格納されます。この形式は、"行ストレージの詳細" で取り上げた形式に類似していますが、Amount 列のデータが存在しません。

^BankT    = 4
^BankT(1) = $lb(10001234,66162,"Deposit to Savings","Deposit")
^BankT(2) = $lb(10001234,66182,"Payment to Vendor ABC","Withdrawal")
^BankT(3) = $lb(10002345,66320,"Transfer to Checking","Transfer")
^BankT(4) = $lb(10002345,66334,"Deposit to Savings","Deposit")

このテーブルには、Amount 列のデータを格納する別のグローバルがあります。この形式は、"列指向ストレージの詳細" で取り上げた形式と同等です。

^BankT.V1(1) = {"type":"decimal", "count":4, "length":5, "vector":[,40,-20,-25,30]}

このテーブルのメタデータには、列の順序に関する情報が記述されています。InterSystems IRIS では、この情報を使用して、行グローバルと列グローバルに格納したデータを使用するリレーショナル・テーブルを作成します。

文字列の照合

列指向ストレージを使用する (テーブルの既定ストレージの使用または特定フィールドに対する明示的な設定によって指定) すべての文字列型フィールドは、ストレージ・レイアウトが混在した状態にするときに、EXACT 照合を備えるように定義されます。

混合ストレージによる分析クエリ処理

分析クエリの効率は、アクセスするデータに左右されます。前のセクションで作成した BankTransaction テーブルを考えます。このテーブルでは、Amount 列にのみ列指向ストレージを使用しています。トランザクション金額すべての平均サイズを求めるクエリは、Amount 列にのみアクセスするので効率的です。

SELECT AVG(ABS(Amount)) FROM Sample.BankTransaction

以下の図では、Amount 列が、行単位で格納されている他の列から分離されています。

Four 1-by-4 rows in white. One 4-by-1 column in purple.

しかし、行として格納されている他の列の集計も実行するクエリでは、目に見えるパフォーマンス上の利点はなくなります。

混合ストレージによるトランザクション処理

データの頻繁な更新と挿入を必要とする混合ストレージ・テーブルでは、行ストレージのみのテーブルよりもパフォーマンスが低下することが考えられますが、列指向ストレージのみのテーブルほどの低下ではありません。例えば、Amount 列にのみ列指向ストレージを使用した BankTransaction テーブルに新しい行を挿入するとします。

INSERT INTO Sample.BankTransaction
  VALUES (10002345,TO_DATE('01 SEP 2022'),'Deposit to Savings',10.00,'Deposit')

INSERT 操作では既存のどの行グローバルもロードされませんが、新しい Amount 値を挿入するには、Amount 列グローバルの最後のチャンク全体をメモリにロードする必要があります。データによっては、トランザクション向けとアナリティクス向けに別々のテーブルを維持するよりも、トランザクション処理によるこのオーバーヘッドを受け入れる方が望ましい場合もあります。

Five 1-by-5 columns in white, the last one in purple, and one 1-by-5 column in blue, the last element in purple.

ストレージ・レイアウトのインデックス

選択したストレージ形式のタイプによって、テーブルに対するインデックスの定義が阻害されることはありません。インデックスから得られる利点は、ストレージ形式によって異なります。

行ストレージ・レイアウトのインデックス

"行ストレージによる分析クエリ処理" のセクションで説明したように、行ストレージ・レイアウトを使用したテーブルでは、その列に対するフィルタと集計の操作が低速になることがあります。このようなテーブルにビットマップ・インデックスまたは列指向インデックスを定義すると、このような分析操作のパフォーマンスの向上が期待できます。

ビットマップ・インデックスは、指定したインデックス付きのデータ値に対応する一連の ID 値を表す一連のビット文字列を使用します。この形式は高い比率で圧縮されているので、検索する行の数を削減できます。また、ブーリアン論理を使用してさまざまなビットマップ・インデックスを結合できるので、複数のフィールドやフィールド値を扱うフィルタの効率化を図ることができます。ビットマップの使用の詳細は、"ビットマック・インデックス" を参照してください。

以前のセクションで取り上げた BankTransaction テーブルを使用して、Type 列に以下のビットマップ・インデックスを作成するとします。

CREATE TABLE Sample.BankTransaction (
  AccountNumber INTEGER,
  TransactionDate DATE,
  Description VARCHAR(100),
  Amount NUMERIC(10,2),
  Type VARCHAR(10))

CREATE BITMAP INDEX TypeIndex
ON Sample.BankTransaction(Type)

それに続き、いずれかのトランザクション・タイプに基づいて行を制限する集計クエリを実行するとします。

SELECT AVG(ABS(Amount)) FROM Sample.BankTransaction WHERE Type = 'Deposit'

ビットマップ・インデックスを使用することで、以下の図のように、選択したトランザクション・タイプの行のみでクエリが繰り返されます。

Four 1-by-5 rows. Rows 1 and 4 are blue, with element 4 purple. Rows 2 and 3 are white.

ただし、この図でわかるように、ビットマップ・インデックスを使用して適格な行を探し出した後、必要な要素は行ごとに 1 つの要素のみであるにもかかわらず、行全体を取得し続ける必要があります。数百万行ものテーブルになると、フィルタで抽出された行であっても、パフォーマンス上の深刻なコストになることがあります。

代替策として、頻繁にクエリの対象となる列に列指向インデックスを定義します。列指向インデックスには、"列指向ストレージの詳細" で説明したものと同じベクトル化列データが格納されます。このインデックスを使用すると、追加したインデックスのストレージが必要にはなるものの、行ストレージ・テーブルに対する分析クエリのパフォーマンスが向上します。

InterSystems SQL DDL を使用して列指向インデックスを定義するには、次のように CREATE INDEXCREATE COLUMNAR INDEX 構文を使用します。

CREATE COLUMNAR INDEX indexName ON table(column) 

永続クラスに列指向インデックスを定義するには、次のように、定義するインデックスに type = columnar キーワードを指定します。

Index indexName ON propertyName [ type = columnar ] 

もう一度 BankTransaction テーブルを使用して、以下のように Type 列にビットマップ・インデックス、Amount 列に列指向インデックスをそれぞれ作成します。

CREATE TABLE Sample.BankTransaction (
  AccountNumber INTEGER,
  TransactionDate DATE,
  Description VARCHAR(100),
  Amount NUMERIC(10,2),
  Type VARCHAR(10))

CREATE BITMAP INDEX TypeIndex
ON Sample.BankTransaction(Type)

CREATE COLUMNAR INDEX AmountIndex
ON Sample.BankTransaction(Amount)

ここでは、以前に取り上げた集計クエリで両方のインデックスを組み合わせて使用し、クエリの対象になるデータのみにアクセスします。まず、TypeIndex ビットマップ・インデックスに基づいて、アクセスすべき行を以下のクエリで探し出します。続いて、AmountIndex 列指向インデックスに基づいて、これらの行の Amount 値にアクセスします。

SELECT AVG(ABS(Amount)) FROM Sample.BankTransaction WHERE Type = 'Deposit'

Four 1-by-5 rows in white and a 4-by-1 column in blue, with row 1 and 4 in purple.

列指向ストレージ・レイアウトのインデックス

列指向ストレージ・テーブルの主要な目的が集計操作とフィルタ操作であるならば、インデックスの追加によって得られるパフォーマンス面の利点は多くありません。例えば、ビットマップ・インデックスでフィルタ処理のパフォーマンスは向上しますが、そのインデックスで必要になる追加のストレージとインデックスに起因する取り込みのオーバーヘッドに見合うほどの利点ではありません。クエリの負荷に高度に選択的なフィールドや一意のキーが関係している場合は、通常のインデックスを定義する方に価値があることも考えられます。このようなインデックスを定義する価値があるかどうかを判断するには、クエリの試験とトレードオフの分析が必要です。インデックスの定義の詳細は、"インデックスの定義と構築" を参照してください。

行ストレージと列指向ストレージの推奨できるアプリケーション

テーブルで使用するストレージを列指向または行単位のどちらとするかを明確に判断する公式を InterSystems では用意していませんが、InterSystems SQL スキーマの構造を定義する際に効果的と考えられる一般的な指針はあります。一般的には以下の指針に従います。

  • InterSystems IRIS SQL テーブルの行数が 100 万を下回っていれば、列指向ストレージを検討する必要はありません。ベクトル化ストレージの利点によって小規模なテーブルに大きな変化が見られる可能性は低いといえます。

  • InterSystems SQL を活用するアプリケーションやトランザクション処理アプリケーションなどのオブジェクトには、既定の行単位ストレージ・レイアウトを使用します。アプリケーションやプログラム・トランザクションに発行される大半のクエリでは、限られた数の行を取得または更新します。また、集計機能を使用することはほとんどありません。このようなケースは、列指向ストレージとベクトル化クエリ処理が提供する利点の対象外です。

  • このようなアプリケーションで運用分析を採用している場合、分析クエリのパフォーマンスが不十分であれば列指向インデックスを追加します。このような場合は、数量や通貨のように集計に使用される数値フィールドを探すか、タイムスタンプのように、カーディナリティが高く、範囲条件に使用されるフィールドを探します。列指向インデックスをビットマップ・インデックスと組み合わせて使用すると、マスタ・マップや通常のインデックス・マップからの過剰な読み取り操作の発生を防止できます。

  • 分析の事例で InterSystems IRIS SQL スキーマをデプロイしている場合は列指向ストレージ・レイアウトを使用します。このような事例として、スター・スキーマやスノーフレーク・スキーマなどの正規化されていないテーブル構造、ビットマップ・インデックスと一括取り込みの多用などがあります。列指向ストレージから多大な利点が得られるのは、複数の行にわたって値を集計する場合の分析クエリです。列指向テーブルを定義している場合、列指向ストレージに適切に収まらない列は、自動的に行単位ストレージに戻されます。このような列として、ストリーム、長い文字列、シリアル・フィールドなどがあります。InterSystems IRIS SQL では混合テーブル・レイアウトが全面的にサポートされていて、クエリ・プランの適格な部分にはベクトル化クエリ処理が使用されます。列指向テーブルではビットマップ・インデックスを省略できます。このようなテーブルでは値が制限されているからです。

上記の推奨事項は、データ関連の要因とアプリケーションを実行する環境による影響を受けます。したがって、代表的な設定でさまざまなレイアウトを試験して、最良のパフォーマンスが得られるレイアウトを判断することをお勧めします。

FeedbackOpens in a new tab