背景
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の全文インデックスは、複雑なクエリを処理する際にも常に迅速な応答時間を提供し、高い同時実行性と高性能な検索が求められる実際のアプリケーションシナリオにより適しています。