2026年11月2日、ブログ記事のタイトル重複判定ロジックを見直した。従来は保存された埋め込みをすべてPythonで読み込み類似度を計算していたが、今回はMariaDB 11.8+のベクトルインデックスを利用して、近いタイトルの候補のみを取得するように変更した。

ここで注意すべき点は、SQLのソート式であった。ORDER BY 1 - VEC_DISTANCE_COSINE(...) DESCは類似度の順序を表現しているが、距離関数を計算式でラップすると、ベクトルインデックスを利用できなくなる可能性がある。公式ドキュメントでは、距離関数またはその別名をそのまま昇順でソートし、LIMITを併用するように説明されている。MariaDB公式ドキュメント

そのため、検索は以下のように距離を基準とし、返された値のみをPythonでsimilarity = 1 - distanceに変換した。以下は、核となる部分のみを簡略化したSQLAlchemyバインディングの例である。:query_vectorには、同じ埋め込みモデルによって生成されたベクトルのJSON文字列を渡す。

sql
SELECT translation_id,
       VEC_DISTANCE_COSINE(embedding, VEC_FromText(:query_vector)) AS distance
FROM title_vectors
ORDER BY VEC_DISTANCE_COSINE(embedding, VEC_FromText(:query_vector)) ASC
LIMIT 10;

この例は、embeddingにDISTANCE=cosineベクトルインデックスが存在することを前提としている。インデックス作成時の距離方式と検索関数が異なる場合、インデックスは利用されない。VEC_DISTANCE_COSINEドキュメント

今回の変更には、EXPLAIN結果のkeyが期待通りのベクトルインデックスを利用しているかを確認する統合テストも追加された。性能改善の程度については別途測定が必要だが、ベクトル全体をPythonで読み込んでいた検索パスが候補取得に短縮された点はコードから確認できる。

インデックスを作成した場合は、クエリが実際にそのインデックスを使用しているかまで確認しよう。数式上同じ順序を表現していても、実行計画まで同一になるとは限らない。