Tsurugi 1.13.0をリリースしました。

概要

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 はエラーにならず、無視されていました。

Note

その他、SQL機能の詳細仕様や現時点での制約については、tsurugidbの以下のドキュメントを参照してください。

機能追加と改善 - Transaction

  • インデックス構造の格納効率を改善し、テーブルとインデックスのメモリ使用量を削減
    • テーブルの行とインデックスのエントリは、キーを先頭から8バイトずつ区切った階層構造でメモリ上に格納されます。従来のバージョンでは、キーの途中で他のキーと区別できるようになった後も、残りの部分を8バイトごとに1階層ずつ格納していたため、長いキーほど多くのメモリを使っていました。本バージョンでは、この残りの部分をまとめて格納するよう改善しました。

    • テーブル本体やインデックスごとの削減の効果は、キーの先頭に近い位置で他のキーと区別でき、残りの部分が長いほど大きくなります。

      • UUIDやメールアドレス、タイムスタンプのように値の重複が少ないカラムのセカンダリインデックスでは、メモリ使用量が大きく削減されます。セカンダリインデックスのキーはカラムの値の後ろに主キーをつなげたものなので、カラムの値や主キーが長いほど効果は大きくなります。UUIDなどの長い文字列を主キーに持つテーブルでは、テーブル本体のメモリ使用量も削減されます。
      • 一方、フラグや状態コードのように同じ値が多いカラムのセカンダリインデックスでは、末尾の主キーまで区別できないため、メモリ使用量はほとんど変わりません。日時のカラムでも、同じ時刻の行が多ければ効果は小さくなります。例えば、主キーが BIGINT のテーブル本体のようにキーが8バイトに収まる場合は、削減の余地はほぼありません。
    • ベンチマーク Phone-Bill では、データベースのメモリ使用量 (RSS) が約130GBから約62GBへと半分以下になりました。このベンチマークの通話履歴テーブルは、電話番号や通話開始日時からなる平均約32バイトの主キーを持ち、通話開始日時などのセカンダリインデックスが作成されています。通話開始日時はほぼ一意で、その後ろに長い主キーが連結されるため、本バージョンの改善が大きく効きます。

    • その他、単一のインデックス構成による効果例を以下に示します。主キーが BIGINT の300万行のテーブルに、値の傾向が異なるカラムでセカンダリインデックスを1つずつ作成し、エントリ1件あたりのメモリ使用量を測定したものです。

      インデックスを作成したカラムの値1.12.01.13.0削減率
      UUID形式の文字列 (CHAR(36))2,148 bytes582 bytes73%
      メールアドレス形式の文字列 (VARCHAR(64))1,436 bytes587 bytes59%
      1秒刻みで増加するタイムスタンプ (TIMESTAMP)1,134 bytes513 bytes55%
      10桁のコード、乱数 (CHAR(10))1,123 bytes523 bytes53%
      連番 (BIGINT)811 bytes512 bytes37%
      金額、乱数 (DECIMAL(12,2))722 bytes514 bytes29%
      28日間の範囲でランダムなタイムスタンプ (TIMESTAMP)832 bytes683 bytes18%
      1000種類の値 (BIGINT)481 bytes482 bytes0%
      コード、10種類の値 (INT)448 bytes449 bytes0%

機能追加と改善 - Client

  • Tsurugi Python DB-API
    • SQLの実行計画を取得する Cursor.explain メソッドを追加
    • Tsurugiとの通信中にPythonのGIL (Global Interpreter Lock) を解放するよう変更
      • 従来のバージョンでは通信中もGILを保持していたため、複数のスレッドから呼び出しても各スレッドは順に実行されていました。本バージョンより、通信中に他のスレッドが実行できるようになり、マルチスレッドのアプリケーションで処理が並列に実行されます。
    • Connection.commit, Connection.rollback の呼び出し時に、トランザクションを明示的にクローズするよう変更
      • 従来のバージョンでは、デストラクター内で新しいスレッドを起動してトランザクションを破棄していました。本バージョンより、commit, rollback の呼び出し時にリソースを解放します。
  • 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 のバージョンに変更はありません。

バグ修正

バグ修正 - 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文字列は格納されないため、本件は発生しません。

バグ修正 - 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では発生しません。
    • 本バージョンより、送信したデータのファイルは、そのデータを使用したリクエストの処理が終わった時点か、セッションのクローズ時に削除されます。なお、従来のバージョンで残ったファイルはデータベースの再起動時に削除されます。

バグ修正 - UDF

  • APPLY 演算子を使用してユーザー定義表値関数を呼び出すクエリが途中でエラーになると、データベースが異常終了する
    • 本件は Tsurugi UDF 0.5.1 で修正されています。修正を適用するには、0.5.1 以降の udf-plugin-builder でUDFプラグインを再生成してデプロイしてください。
  • APPLY 演算子を使用してユーザー定義表値関数を呼び出す際に、結果行に16バイト以上の VARCHAR, VARBINARY 型の値が含まれると、その値が同じ行の他のカラムの値などで上書きされることがある
    • 同じ行に16バイト以上の可変長のカラムが複数ある場合や、BLOB, CLOB 型のカラムの後ろに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 のアップグレードに併せて、必要に応じて各クライアントをアップグレードしてください。

ClientVersion
Tsubakuro/Java1.16.0+
Iceaxe1.16.0+
Tanzawa1.16.0+
Tsubakuro/Rust0.10.0+
Tsurugi Python DB-API0.10.0+
Tsurugi MCP Server0.8.0+
Tsurugi JDBC0.5.0+
Tsurugi UDF0.5.0+

トランザクションログのフォーマット変更

本バージョンではトランザクションログのフォーマットバージョンが Version 8 に変更されています。バージョン 1.12.0 で利用していたトランザクションログを利用してバージョン 1.13.0 を起動すると、トランザクションログのフォーマットバージョンが自動でアップデートされます。

これにより、バージョン 1.13.0 で起動したトランザクションログはバージョン 1.12.0 では利用できなくなります。また、バージョン 1.13.0 以降で作成したバックアップを バージョン 1.12.0 で復元することもできなくなりますのでご注意ください。

アップグレード手順

旧バージョンからのアップグレード手順については、以下のドキュメントを参照してください。

その他

本バージョンにおける変更内容の一覧は、以下のChangelogを参照してください。

本バージョンに対する既知の問題、および本バージョンにて修正された問題の補足情報については、以下のリンクを参照してください。