概要
Tsurugi 1.13.0 では、インデックス構造の格納効率を改善し、特にセカンダリインデックスを使用するテーブルのメモリ使用量を削減しました。
Tsurugi はテーブルとインデックスのデータを、キーを8バイトずつ区切った階層構造でメモリ上に格納します。
従来のバージョンでは、他のキーと区別できた後の残りも8バイトごとに1階層ずつ格納しており、長いキーほどメモリを多く使っていました。
これはカラムの値の後ろに主キーが続くセカンダリインデックスや、UUIDなどの長い文字列の主キーで顕著でした。
本バージョンではこの残りの部分をまとめて格納するよう改善しました。
例えば、2つのセカンダリインデックスを持つテーブルを使用するベンチマーク (Phone-Bill) では、メモリ使用量 (RSS) が約130GBから約62GBへと半分以下に削減されています。
また、Tsurugi 1.13.0 では GROUP BY 句に任意の式を指定できるようになりました。
従来のバージョンでは GROUP BY 句にはカラムのみを指定でき、式の結果でグループ化するには、サブクエリーで式の結果を列として取得する必要がありました。
本バージョンでは GROUP BY EXTRACT(YEAR FROM ts) のように GROUP BY 句に式を直接記述できます。
Tsurugi 1.12.0 で対応した EXTRACT 関数と組み合わせることで、年ごとや月ごとといった時系列データの集計を、より簡潔なSQLで記述できます。
その他、本バージョンではいくつかの重要な不具合修正や安定性の向上を行っています。
前バージョンを利用するすべてのユーザーは本バージョンへのアップグレードを強く推奨します。
機能追加と改善
機能追加と改善 - SQL
GROUP BY句に任意の式を指定できるよう対応GROUP BY句の要素として、カラムに加えて関数呼び出しや演算などの式を指定できるようになりました。SELECT EXTRACT(YEAR FROM ts) AS y, COUNT(*) AS cnt FROM tbl GROUP BY EXTRACT(YEAR FROM ts);
GROUP BY句に指定した式は、SELECT句,HAVING句,ORDER BY句から参照できます。式の一部として参照することもできます。SELECT a + 1, (a + 1) * 2, COUNT(*) FROM tbl GROUP BY a + 1 HAVING a + 1 > 2 ORDER BY a + 1;
SELECT句などから参照する式は、GROUP BY句に指定した式と同じ形で記述する必要があります。例えばGROUP BY a + 1に対してSELECT 1 + aは同じ式とみなされず、エラー (column must be aggregated) となります。
Note
本バージョンでは、GROUP BY 句の式について以下の制約があります。
- 以下は
GROUP BY句の要素として指定できません。- リテラル (例:
GROUP BY 1) - プレースホルダー
- 集約関数を含む式 (例:
GROUP BY a + COUNT(*)) BLOB型,CLOB型の式
- リテラル (例:
- 以下は
GROUP BY句の要素として指定できますが、SELECT句などから参照できません。- サブクエリーを含む式
BETWEEN述語,IN述語を含む式- 単純
CASE式 (例:CASE a WHEN 1 THEN ... END) NULLIF式
これらの式でグループ化した結果を参照したい場合は、従来と同様に、サブクエリーで式の結果を列として取得し、その列を GROUP BY 句に指定してください。
SELECT g, COUNT(*)
FROM (SELECT CASE a WHEN 1 THEN 'one' ELSE 'other' END AS g FROM tbl) AS q
GROUP BY g;
Important
本バージョンより、GROUP BY 句で COLLATE を指定するとエラー (collation in GROUP BY is not yet supported) となります。
従来のバージョンでは GROUP BY 句に指定した COLLATE はエラーにならず、無視されていました。
機能追加と改善 - Transaction
- インデックス構造の格納効率を改善し、テーブルとインデックスのメモリ使用量を削減
テーブルの行とインデックスのエントリは、キーを先頭から8バイトずつ区切った階層構造でメモリ上に格納されます。従来のバージョンでは、キーの途中で他のキーと区別できるようになった後も、残りの部分を8バイトごとに1階層ずつ格納していたため、長いキーほど多くのメモリを使っていました。本バージョンでは、この残りの部分をまとめて格納するよう改善しました。
テーブル本体やインデックスごとの削減の効果は、キーの先頭に近い位置で他のキーと区別でき、残りの部分が長いほど大きくなります。
- UUIDやメールアドレス、タイムスタンプのように値の重複が少ないカラムのセカンダリインデックスでは、メモリ使用量が大きく削減されます。セカンダリインデックスのキーはカラムの値の後ろに主キーをつなげたものなので、カラムの値や主キーが長いほど効果は大きくなります。UUIDなどの長い文字列を主キーに持つテーブルでは、テーブル本体のメモリ使用量も削減されます。
- 一方、フラグや状態コードのように同じ値が多いカラムのセカンダリインデックスでは、末尾の主キーまで区別できないため、メモリ使用量はほとんど変わりません。日時のカラムでも、同じ時刻の行が多ければ効果は小さくなります。例えば、主キーが
BIGINTのテーブル本体のようにキーが8バイトに収まる場合は、削減の余地はほぼありません。
ベンチマーク Phone-Bill では、データベースのメモリ使用量 (RSS) が約130GBから約62GBへと半分以下になりました。このベンチマークの通話履歴テーブルは、電話番号や通話開始日時からなる平均約32バイトの主キーを持ち、通話開始日時などのセカンダリインデックスが作成されています。通話開始日時はほぼ一意で、その後ろに長い主キーが連結されるため、本バージョンの改善が大きく効きます。
その他、単一のインデックス構成による効果例を以下に示します。主キーが
BIGINTの300万行のテーブルに、値の傾向が異なるカラムでセカンダリインデックスを1つずつ作成し、エントリ1件あたりのメモリ使用量を測定したものです。インデックスを作成したカラムの値 1.12.0 1.13.0 削減率 UUID形式の文字列 ( CHAR(36))2,148 bytes 582 bytes 73% メールアドレス形式の文字列 ( VARCHAR(64))1,436 bytes 587 bytes 59% 1秒刻みで増加するタイムスタンプ ( TIMESTAMP)1,134 bytes 513 bytes 55% 10桁のコード、乱数 ( CHAR(10))1,123 bytes 523 bytes 53% 連番 ( BIGINT)811 bytes 512 bytes 37% 金額、乱数 ( DECIMAL(12,2))722 bytes 514 bytes 29% 28日間の範囲でランダムなタイムスタンプ ( TIMESTAMP)832 bytes 683 bytes 18% 1000種類の値 ( BIGINT)481 bytes 482 bytes 0% コード、10種類の値 ( INT)448 bytes 449 bytes 0%
機能追加と改善 - Client
- Tsurugi Python DB-API
- SQLの実行計画を取得する
Cursor.explainメソッドを追加explainメソッドの引数はexecuteメソッドと同様です。詳細は以下のドキュメントを参照してください。
- Tsurugiとの通信中にPythonのGIL (Global Interpreter Lock) を解放するよう変更
- 従来のバージョンでは通信中もGILを保持していたため、複数のスレッドから呼び出しても各スレッドは順に実行されていました。本バージョンより、通信中に他のスレッドが実行できるようになり、マルチスレッドのアプリケーションで処理が並列に実行されます。
Connection.commit,Connection.rollbackの呼び出し時に、トランザクションを明示的にクローズするよう変更- 従来のバージョンでは、デストラクター内で新しいスレッドを起動してトランザクションを破棄していました。本バージョンより、
commit,rollbackの呼び出し時にリソースを解放します。
- 従来のバージョンでは、デストラクター内で新しいスレッドを起動してトランザクションを破棄していました。本バージョンより、
- SQLの実行計画を取得する
- Tsurugi MCPサーバー
queryツールが保持するリソースの数の上限を指定する起動オプション--query-pool-sizeを追加queryツールは、結果が応答サイズの上限を超えた場合に、続きを読むためのセッションやトランザクションなどのリソースを保持します。続きが読まれない場合、これらのリソースは解放されずに残り続けていました。--query-pool-sizeを指定すると、保持するリソースの数が上限を超えたときに、最も古いリソースから解放します。デフォルトは0(無制限) です。
機能追加と改善 - Distribution
- インストーラーおよびドキュメントで使用する Apache Arrow のパッケージリポジトリ設定パッケージの取得先を
apache.jfrog.ioからpackages.apache.orgに変更- Apache Arrow の公式案内で
packages.apache.orgが推奨されていることに合わせた変更です。どちらのホストからも同じパッケージを取得でき、インストールされる Apache Arrow のバージョンに変更はありません。
- Apache Arrow の公式案内で
バグ修正
バグ修正 - SQL
TRUNCATE TABLEの実行中に、同じテーブルに対するDMLが実行されるとデータベースが異常終了することがあるTRUNCATE TABLEの処理中に、同じテーブルへのDMLを排他できない区間がありました。この区間でプレースホルダーを使用しない文の実行やprepareが行われると、TRUNCATE TABLEがエラー (DDL operation was blocked by other DML operation) で失敗し、その後のDMLの実行でデータベースが異常終了することがあります。また、TRUNCATE TABLEがエラーを返したにもかかわらず、テーブルのデータが削除されることがあります。
- 数値として等しいが内部表現の異なる
DECIMAL型の値 (例:1.0と1) が混在すると、GROUP BY,SELECT DISTINCT,COUNT(DISTINCT ...), 等価結合でそれらが異なる値として扱われ、クエリの結果が正しくないことがあるCASE式やCOALESCE関数で分岐ごとに型が異なる場合や、UNION ALLでスケールの異なる式の結果を合わせる場合など、式で計算したDECIMAL型の値をキーとして使用する場合に発生します。テーブルのカラムをそのままキーとして使用する場合は発生しません。
REAL型,DOUBLE型の値に対するCOUNT(DISTINCT ...)で、NaN同士が異なる値として扱われ、正しくない値を返す- 本バージョンより、
GROUP BYやSELECT DISTINCTと同様にNaN同士を等しい値として扱います。
- 本バージョンより、
- Parquetファイルのロードによって不正なUTF-8文字列を格納した文字列型のカラムに対して
CHAR_LENGTHなどの文字列関数を実行すると、文字列の範囲外のメモリを参照し、正しくない結果を返すことがある- 末尾が不完全なUTF-8のマルチバイト文字で終わる値に対して発生します。本バージョンより、このような値は不正なUTF-8文字列として扱い、
CHAR_LENGTHはNULLを返します。 - SQLやクライアントから送信した値では不正なUTF-8文字列は格納されないため、本件は発生しません。
- 末尾が不完全なUTF-8のマルチバイト文字で終わる値に対して発生します。本バージョンより、このような値は不正なUTF-8文字列として扱い、
バグ修正 - Transaction
- OCCトランザクションが長いキーを持つ行を
INSERTした後、同じトランザクションのスキャン範囲に他のトランザクションがINSERTを行った場合に、Serializableの違反を検出できずにコミットが成功してしまうことがある - LTXの実行中に、その
WRITE PRESERVEに指定したテーブルを他のトランザクションがDROP TABLEまたはTRUNCATE TABLEすると、LTXのコミットまたはロールバック時にデータベースが異常終了することがある - オンラインコンパクションによって、まれにコミット済みのデータがデータベースの再起動後に失われることがある
- オンラインコンパクションによって、コミット中のBLOBデータが失われることがある
バグ修正 - Endpoint
- リレーモードで
BLOB型,CLOB型のデータを送信すると、送信したデータのファイルがデータベース側のvar/blob/sessionsディレクトリ配下に残り続ける- リレーモードは Tsurugi 1.11.0 から各種クライアントライブラリのデフォルトです。Tsubakuro/Java の
PreparedStatementのパラメータやLargeObjectClientでデータを送信した場合にこの問題が発生します。UDFのBLOBクライアントAPIでは発生しません。 - 本バージョンより、送信したデータのファイルは、そのデータを使用したリクエストの処理が終わった時点か、セッションのクローズ時に削除されます。なお、従来のバージョンで残ったファイルはデータベースの再起動時に削除されます。
- リレーモードは Tsurugi 1.11.0 から各種クライアントライブラリのデフォルトです。Tsubakuro/Java の
バグ修正 - UDF
APPLY演算子を使用してユーザー定義表値関数を呼び出すクエリが途中でエラーになると、データベースが異常終了する- 本件は Tsurugi UDF 0.5.1 で修正されています。修正を適用するには、0.5.1 以降の
udf-plugin-builderでUDFプラグインを再生成してデプロイしてください。
- 本件は Tsurugi UDF 0.5.1 で修正されています。修正を適用するには、0.5.1 以降の
APPLY演算子を使用してユーザー定義表値関数を呼び出す際に、結果行に16バイト以上のVARCHAR,VARBINARY型の値が含まれると、その値が同じ行の他のカラムの値などで上書きされることがある- 同じ行に16バイト以上の可変長のカラムが複数ある場合や、
BLOB,CLOB型のカラムの後ろに16バイト以上の可変長のカラムがある場合に発生します。エラーにはならず、値だけが変わります。
- 同じ行に16バイト以上の可変長のカラムが複数ある場合や、
- UDFプラグイン設定ファイル (
<plugin-name>.ini) のendpoint,secureパラメータに、パイプ文字|の前後にスペースを入れて複数の値を指定すると、2件目以降の値が正しく反映されない
バグ修正 - Client
- Tsubakuro/Rust, Tsurugi Python DB-API : OCCトランザクションのクエリ実行中にシリアライゼーションの失敗でアボートされた場合に、例外が発生せず、存在する行が0行として返されることがある
- トランザクション自体はアボートされているためコミットは失敗し、誤った結果に基づく更新がコミットされることはありません。
- Tsurugi Python DB-API :
Connection.commitがエラーになった後に同じコネクションでSQLを実行すると、エラーになったトランザクションが使われ続け、以降のSQLもInactiveTransactionExceptionとなる - Tsurugi MCPサーバー :
queryツールでSQLがエラーになると、セッションとトランザクションが解放されずに残り続ける
アップグレードについて
クライアント互換性リスト
Tsurugi 1.13.0 では 以下のクライアントに対応しています。
Tsurugi のアップグレードに併せて、必要に応じて各クライアントをアップグレードしてください。
| Client | Version |
|---|---|
| Tsubakuro/Java | 1.16.0+ |
| Iceaxe | 1.16.0+ |
| Tanzawa | 1.16.0+ |
| Tsubakuro/Rust | 0.10.0+ |
| Tsurugi Python DB-API | 0.10.0+ |
| Tsurugi MCP Server | 0.8.0+ |
| Tsurugi JDBC | 0.5.0+ |
| Tsurugi UDF | 0.5.0+ |
トランザクションログのフォーマット変更
本バージョンではトランザクションログのフォーマットバージョンが Version 8 に変更されています。バージョン 1.12.0 で利用していたトランザクションログを利用してバージョン 1.13.0 を起動すると、トランザクションログのフォーマットバージョンが自動でアップデートされます。
これにより、バージョン 1.13.0 で起動したトランザクションログはバージョン 1.12.0 では利用できなくなります。また、バージョン 1.13.0 以降で作成したバックアップを バージョン 1.12.0 で復元することもできなくなりますのでご注意ください。
アップグレード手順
旧バージョンからのアップグレード手順については、以下のドキュメントを参照してください。
その他
本バージョンにおける変更内容の一覧は、以下のChangelogを参照してください。
本バージョンに対する既知の問題、および本バージョンにて修正された問題の補足情報については、以下のリンクを参照してください。