背景
OceanBaseの全文インデックス機能は、特にシステムログ分析やユーザー行動・プロファイリング分析などの実環境で直面する様々な課題を効果的に解決します。この機能により、データの高速かつ効率的なフィルタリングと選別、および高品質な相関性評価が可能になります。さらに、疎ベクトルと密ベクトルを組み合わせたマルチリコールアーキテクチャにより、特定の知識領域におけるRAGシステムにおいて、より効率的な検索結果の提示が実現されます。
本チュートリアルでは、ニュース情報業務シナリオを例に説明します。このようなシナリオでは、検索システムに対して3つの主要な課題が提起されます:
- リアルタイム性の要求:TBレベルのデータから迅速にターゲット情報を特定すること。
- 意味の複雑さ:中国語の分かち書きや類義語処理など、自然言語処理の難題を解決すること。
- ハイブリッドクエリの要求:テキスト検索と構造化クエリの統合最適化能力を向上させること。
本チュートリアルでは、全文インデックス機能を使用し、膨大な情報量の中からターゲットとなるニュースを迅速に見つける方法を実演します。クエリに含まれるキーワードを通じて、OceanBaseの全文インデックスが機能、パフォーマンス、使いやすさの面でどのように向上しているかを示します。
原理の概要
OceanBaseストレージエンジンでは、ユーザーのドキュメント(doc)とクエリ(query)は、分かち書き器によって複数の単語(word/token)に分割されます。これらの単語およびドキュメントの統計情報の特徴は、内部の補助テーブル(tablet)に格納され、情報検索段階での関連性評価(ranking)に利用されます。OceanBaseは、ユーザーのクエリ文中の単語と格納されたドキュメントとの間の関連性スコアをより効果的に計算し、条件を満たすドキュメント及びそのスコアを出力する、高度なBM25アルゴリズムを採用しています。

前提条件
OceanBaseの全文インデックス機能をスムーズに操作し、体験するためには、以下の前提条件を満たしていることを確認してください:
環境要件:OceanBase V4.3.5 BP1以降のバージョンのクラスタをデプロイし、MySQLモードのテナントを作成していること。OceanBaseクラスタのデプロイ方法については、デプロイの概要を参照してください。デプロイが完了したら、以下のSQLでクラスタとテナントの情報を確認してください:
-- クラスタ情報の確認 SELECT * FROM GV$OB_SERVERS; -- テナント情報の確認 SELECT * FROM oceanbase.DBA_OB_TENANTS;権限設定:作成したMySQLモードのテナントに挿入およびクエリの権限が付与されていること。権限設定の詳細については、直接権限付与を参照してください。
データベースの作成:データベースが作成されていることを確認してください。詳細な手順については、データベースの作成を参照してください。
手順
以下の手順に従って、OceanBaseの全文インデックスおよび一般的なビューとクエリのテクニックを体験します。
ステップ1:データセットのインポート
OceanBaseには中国語をサポートするIK分かち書き器が組み込まれており、従来の自然言語処理よりも効率的なブールモードも備えています。中国語サッカースポーツニュースデータセットを使用して、OceanBaseにデータをインポートします。sport_data_wholeという名前の主キーなしパーティションテーブルを作成し、3列の可変長文字列(event、date、news)を含めます。そして、IK中国語分かち書き器を使用してnewsフィールドに対して全文インデックスを作成し、max_wordモードを指定します。
説明
IK分かち書き器のsmartモードとmax_wordモードの違いは、前者が最長の単語に一致した後、より短い単語へのマッチングを続けない点です。
OceanBaseに組み込まれている他の分かち書き器には、英語に適したspaceやbeng、文字長に基づいて分割するngramがあります。
-- テーブルを作成し、IK分かち書き器を使用して全文インデックスを作成する
CREATE TABLE sport_data_whole (
event VARCHAR(64),
date VARCHAR(16),
news VARCHAR(65535),
FULLTEXT INDEX (news) WITH PARSER ik PARSER_PROPERTIES=(ik_mode="max_word")
);
クライアントのローカルファイルを使用して、ニュースデータセットをテーブルにインポートします。所要時間は約30秒です。
-- データをインポートする
LOAD DATA /*+ PARALLEL(8) */ LOCAL INFILE '/home/sports_data_whole.csv' INTO TABLE sport_data_whole
FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
データをインポートした後、テーブルには合計5268件のニュースがあり、平均ニュースの長さは約2700字、元のデータサイズは約57MBです。効率的に圧縮することで、実際のストレージ容量は30MBに満たなくなりました。その大部分は、大量の分かち書き記録を格納する全文インデックスの転置インデックスと正規インデックスの補助テーブルです。
-- インポートされた件数を確認する
SELECT AVG(LENGTH(news)), COUNT(*) FROM sport_data_whole;
結果は次のとおりです:
+-------------------+----------+
| avg(length(news)) | count(*) |
+-------------------+----------+
| 2781.6900 | 5268 |
+-------------------+----------+
1 row in set
-- ビューをクエリして、結果を確認する
SELECT * FROM oceanbase.DBA_OB_TABLE_SPACE_USAGE;
結果は次のとおりです:
+----------+---------------+--------------------------------+-------------+---------------+
| TABLE_ID | DATABASE_NAME | TABLE_NAME | OCCUPY_SIZE | REQUIRED_SIZE |
+----------+---------------+--------------------------------+-------------+---------------+
| 500035 | test | sport_data_whole | 6597450 | 8392704 |
| 500036 | test | __idx_500035_news | 10715722 | 12587008 |
| 500037 | test | __idx_500035_fts_rowkey_doc | 21058 | 28672 |
| 500038 | test | __idx_500035_fts_doc_rowkey | 23236 | 28672 |
| 500039 | test | __idx_500035_news_fts_doc_word | 11178599 | 12587008 |
+----------+---------------+--------------------------------+-------------+---------------+
ステップ2:全文インデックスを利用したクエリ
保存済みのニュースデータセットとインデックスを使用することで、複数の条件を組み合わせたり、高いフィルタリング精度で検索を実行したりできます。例えば、サッカーファンが「バイエルン」と「オウンゴール」を含むニュースを検索したい場合、ブールモードを利用できます。
インデックスのない文字列 LIKE 比較に比べて、ブールモードは構文が簡潔で、クエリの実行速度も速くなります。
-- ブールモードを使用してクエリを実行し、「オウンゴール」と「バイエルン」の両方を含むニュースを検索する
SELECT COUNT(*) FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+オウンゴール +バイエルン' IN BOOLEAN MODE);
実行結果は次のとおりです:
+----------+
| count(*) |
+----------+
| 2 |
+----------+
1 row in set (0.03 sec)
対照的に、LIKE を使用したクエリの方法は次のとおりです:
-- LIKE構文を使用してクエリを実行する
SELECT COUNT(*) FROM sport_data_whole
WHERE news LIKE '%オウンゴール%' AND news LIKE '%バイエルン%';
実行結果も次のとおりです:
+----------+
| count(*) |
+----------+
| 2 |
+----------+
1 row in set (0.08 sec)
返された2つのニュースについて、さらにランキング(ranking)を行い、出力結果のスコアからどちらのニュースがクエリにより関連しているかを判断できます。
-- 関連性を判断するために、ニュースのイベント、日付、スコアを返す
SELECT event, date, MATCH (news) AGAINST ('オウンゴール バイエルン') AS score
FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+オウンゴール +バイエルン' IN BOOLEAN MODE);
実行結果は次のとおりです:
+-------+------+---------------------+
| event | date | score |
+-------+------+---------------------+
| ucl | 0278 | 0.4657063867776557 |
| ucl | 0201 | 0.41760566608994765 |
+-------+------+---------------------+
2 rows in set
また、ブールモードでは、特定のキーワードを除外することも可能です。例えば、ほぼすべてのサッカー試合にはファウルが発生します。赤や黄色のカードが出たり、ファウルがなかったりする非常に激しかった試合を知りたい場合、ブールモードの - 演算子を利用できます。
-- 黄色のカード、赤いカード、ファウルがなく激しかった試合を検索する
SELECT COUNT(*) FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+激しい -黄色のカード -赤いカード -ファウル' IN BOOLEAN MODE);
実行結果は次のとおりです:
+----------+
| count(*) |
+----------+
| 31 |
+----------+
1 row in set
ステップ3:チューニング
TOKENIZE 関数を使用した最適化
全文インデックスのクエリ結果が期待に応えない場合、通常は分かち書きの結果が理想的でないためです。OceanBaseは、分かち書きの効果をテストするための高速な TOKENIZE 関数を提供しています。この関数はすべてのトークナイザーとその対応するプロパティをサポートしており、TOKENIZE 関数を使用してトークナイザーの処理効果を検証できます。
例えば、以下の手動分かち書きの結果は、辞書が海外のスポーツスターの名前(例:ボアテン、グェッツェ)を十分にサポートしていないことを示しており、これが名前を用いた検索のパフォーマンスを低下させる原因となっています。
TOKENIZE関数を使用してトークナイザーの処理効果を検証します:-- 中国語のスポーツニュースの分かち書き効果を検証するため、ik_smart 分かち書きモードを使用します。 SELECT TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]');実行結果は次のとおりです:
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]') | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ["亚", "格", "夫", "阿", "门内", "从", "下钻", "后点", "右路", "分过", "传中", "低", "转身", "球", "射", "闪开", "博", "进", "反击", "门前", "停", "人", "皮", "裆", "策", "滕", "8米处", "托", "在", "角度"] | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set上記の結果では、「博阿滕」や「格策」といった名前は認識されませんでした。
次に、以下のステートメントを実行して、クエリがターゲットドキュメントにヒットしたかどうかを確認します:
-- ブールモードで特定の選手のニュースを検索する SELECT COUNT(*) FROM sport_data_whole WHERE MATCH (news) AGAINST ('+格策 +博阿滕' IN BOOLEAN MODE);実行結果は次のとおりです:
+----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set上記の結果は、明示的にターゲットレコードがマッチしなかったことを示しています。
システム辞書を更新するには、上記の中国語の人名をシステム辞書テーブルに挿入することができます。システム辞書を更新した後、キャッシュをリフレッシュする必要があります。
説明
システム辞書を更新する必要がある場合は、事前にOceanBaseテクニカルサポートにお問い合わせください。
全文インデックスを再構築します:
-- 新しい辞書を適用するために全文インデックスを再構築する ALTER TABLE sport_data_whole DROP INDEX ft_idx_news, ADD FULLTEXT INDEX ft_idx_news (news) WITH PARSER ik;トークナイザーの最適化効果を検証します:
-- 分かち書きテストを再度実行する(同じ入力) SELECT TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]');実行結果は次のとおりです:
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | tokenize('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]') | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ["门内", "从", "下钻", "后点", "右路", "分过", "传中", "低", "转身", "球", "皮亚托夫", "射", "闪开", "进", "反击", "门前", "停", "人", "裆", "8米处", "在", "角度", "格策", "博阿滕"] | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set上記の結果は、専門的な人名エンティティが正常に認識されたことを示しています。
以下のステートメントを実行して、検索精度を検証します:
SELECT COUNT(*) FROM sport_data_whole WHERE MATCH (news) AGAINST ('+格策 +博阿滕' IN BOOLEAN MODE);実行結果は次のとおりです:
+----------+ | count(*) | +----------+ | 79 | +----------+ 1 row in set上記の結果は、79件の関連レコードが正しくヒットしたことを示しています。
MySQLとのパフォーマンス比較
OceanBaseとMySQLの全文インデックスのパフォーマンスの違いを比較するため、MySQLの全文インデックス機能を基準としました。MySQLの中国語分かち書き能力は相対的に弱いため、英語のデータセット wikir1k(369,721行、各行平均100語)を用いてパフォーマンスを比較しました。
以下は、自然言語モードとブールモードでの様々なシナリオにおける比較結果です。大量の分かち書きや結果の返却が必要なシナリオでは、OceanBaseのパフォーマンスがMySQLよりも顕著に優れていることがわかります。小規模な結果セットでは、計算量の割合が小さいため、クエリエンジンの優位性は明確ではなく、両エンジンのパフォーマンスは近接しています。
テスト環境:OceanBaseテナントの仕様は8コア16GB、MySQLバージョンは8.0.36 for Linux on x86_64(MySQL Community Server - GPL)を使用しました。
自然言語モード
-- q1: "and" を含むドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('and');
-- q2: "and" を含むドキュメントを検索し、10件を返す
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('and') LIMIT 10;
-- q3: "librettists" を含むドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('librettists');
-- q4: "librettists" を含むドキュメントを検索し、10件を返す
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('librettists') LIMIT 10;
-- q5: "alleviating librettists" を含むドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists');
-- q6: "black spotted white yellow" を含むドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('black spotted white yellow');
-- q7: "black spotted white yellow" を含むドキュメントを検索し、10件を返す
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('black spotted white yellow') LIMIT 10;
-- q8: "between up and down" を含むドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('between up and down');
-- q9: "between up and down" を含むドキュメントを検索し、10件を返す
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('between up and down') LIMIT 10;
-- q10: 長文ドキュメントを検索する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire');
-- q11: 長文ドキュメントを検索し、"and" を追加する
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire and');
-- q12: 長文ドキュメントを検索し、10件を返す
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire and') LIMIT 10;
シナリオ |
OceanBase |
MySQL |
|---|---|---|
| q1 単一トークンの高頻度語 | 3820458us | 5718430us |
| q2 単一トークンの高頻度語 limit | 231861us | 503772us |
| q3 単一トークンの低頻度語 | 879us | 672us |
| q4 単一トークンの低頻度語 limit | 720us | 700us |
| q5 複数トークンの小規模結果セット | 1591us | 1100us |
| q6 複数トークンの中規模結果セット | 259700us | 602221us |
| q7 複数トークンの中規模結果セット limit | 25502us | 42620us |
| q8 複数トークンの大規模結果セット | 3842391us | 6846847us |
| q9 複数トークンの大規模結果セット limit | 301362us | 784024us |
| q10 トークンが非常に多い場合の小規模結果セット | 22143us | 10161us |
| q11 トークンが非常に多い場合の大規模結果セット | 3905829us | 5929343us |
| q12 トークンが非常に多い場合の大規模結果セット limit | 345968us | 769970us |
ブールモード
-- q1: +高頻度語 -中頻度語
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and -which -his' IN BOOLEAN MODE);
-- q2: +高頻度語 -低頻度語
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+which (+and -his)' IN BOOLEAN MODE);
-- q3: +中頻度語 (+高頻度語 -中頻度語)
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and -carabantes -bufera' IN BOOLEAN MODE);
-- q4: +高頻度語 +低頻度語
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and +librettists' IN BOOLEAN MODE);
シナリオ |
OceanBase |
MySQL |
|---|---|---|
| q1: +高頻度語 -中頻度語 | 1586657us | 2440798us |
| q2: +高頻度語 -低頻度語 | 3726508us | 7974832us |
| q3: +中頻度語 (+高頻度語 -中頻度語) | 3080644us | 5612041us |
| q4: +高頻度語 +低頻度語 | 230284us | 357580us |
パフォーマンス比較のまとめ
上記のデータ比較から、OceanBaseは複雑な全文検索を実行する際、自然言語モードであれブールモードであれ、MySQLよりも顕著に優れたパフォーマンスを発揮していることがわかります。特に、大量の分かち書きや結果のサイズが大きいクエリを処理する場合、OceanBaseの優位性は一層明確になります。これは、デベロッパーやデータアナリストがデータベースを選択する際の有力な参考となり、特に大量のデータを効率的に検索する必要があるアプリケーションシナリオにおいて、OceanBaseはその強力なパフォーマンスと柔軟なクエリ機能を明確に証明しています。
OceanBaseの全文インデックスは、複雑なクエリを処理する際に常に高速な応答時間を提供し、高並行性と高性能な検索を求める実際のアプリケーションシナリオにより適しています。