Slide 1

Slide 1 text

MySQL9でベクトルカラム登場! PHP×AWSでのAI/類似検索はこう変わる PHPカンファレンス関西 2025 株式会社TechBowl スー 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

結論 3

Slide 4

Slide 4 text

PostgreSQL+pgvectorを使おう・・・ 4

Slide 5

Slide 5 text

なぜその結論か?説明していきます! 5

Slide 6

Slide 6 text

なぜその結論か?説明していきます! 1. ベクトルカラムがなぜ必要か? 2. ベクトル検索の構成要素は? 3. AWS+MySQLでは具体的にどのように利用するか? 6

Slide 7

Slide 7 text

今日のゴール: MySQL9のベクトル検索を理解 し、実装判断ができるようになる 7

Slide 8

Slide 8 text

なぜその結論か?説明していきます! 1. ベクトルカラムがなぜ必要か? ←←← 2. ベクトル検索の構成要素は? 3. AWS+MySQLでは具体的にどのように利用するか? 8

Slide 9

Slide 9 text

ベクトルカラムがなぜ必要か? 9

Slide 10

Slide 10 text

ベクトルカラムがなぜ必要か? なぜベクトル検索が必要なのか? 現実的な課題から解決策の価値を理解する 10

Slide 11

Slide 11 text

こんな課題はありませんか? 11

Slide 12

Slide 12 text

こんな課題はありませんか? 現実的なECサイト検索シナリオ 実際のユーザー検索パターン 「赤いワンピース」で検索 「おしゃれなバッグ」で検索 「暖かいコート」で検索 「プレゼント用の時計」で検索 12

Slide 13

Slide 13 text

こんな課題はありませんか? 現実的なECサイト検索シナリオ ユーザーが期待する検索結果 「赤いワンピース」の場合: クリムゾンドレス(深紅色のドレス) 真紅のワンピース(鮮やかな赤) ルビーレッドワンピース(宝石のような赤) 赤色のフォーマルドレス(正装用) ワインレッドのワンピース(落ち着いた赤) 13

Slide 14

Slide 14 text

こんな課題はありませんか? 現実的なECサイト検索シナリオ 実際によく起こる問題 検索結果が0件 → 即座に離脱 全く関係ない商品が表示 → 競合サイトへ移動 3回目の検索失敗 → サイトから完全離脱 年間売上の15-25%を失っている可能性があります 14

Slide 15

Slide 15 text

従来手法の限界を実際の検索結果で検証 15

Slide 16

Slide 16 text

従来手法の限界を実際の検索結果で検証 ECサイトっぽいデータベースでテスト テストデータ(商品マスタ) -- 実際の商品データ例 INSERT INTO products (name, description, price) VALUES ('赤いワンピース', '鮮やかな赤色のエレガントなワンピース', 12800), ('クリムゾンドレス', '深紅色の上品なパーティードレス', 15600), ('真紅のワンピース', '美しい真紅色のフォーマルワンピース', 18900), ('ルビーレッドワンピース', '宝石のような赤色のワンピース', 22400), ('ワインレッドドレス', '落ち着いたワインレッドのドレス', 16800), ('スカーレットワンピース', 'スカーレット色のカジュアルワンピース', 9800); 16

Slide 17

Slide 17 text

従来手法の限界を実際の検索結果で検証 LIKE検索の場合 SELECT name, price FROM products WHERE name LIKE '%赤い%' AND name LIKE '%ワンピース%'; 17

Slide 18

Slide 18 text

従来手法の限界を実際の検索結果で検証 LIKE検索の場合 検索結果(実際のデータ例) 商品名 価格 検索結果 理由 赤いワンピース ¥12,800 ヒット 完全一致 クリムゾンドレス ¥15,600 ヒットしない 「赤い」が含まれない 真紅のワンピース ¥18,900 ヒットしない 「赤い」が含まれない ルビーレッドワンピース ¥22,400 ヒットしない 「赤い」が含まれない 結果: 6商品中1商品のみヒット(検索成功率: 16.7%) 問題: 完全一致する文字列のみ検索、同義語を認識できない 18

Slide 19

Slide 19 text

従来手法の限界を実際の検索結果で検証 全文検索の場合 -- 全文検索インデックスを作成 ALTER TABLE products ADD FULLTEXT(name, description); -- 検索実行 SELECT name, price, description FROM products WHERE MATCH(name, description) AGAINST('赤い ワンピース' IN BOOLEAN MODE); 19

Slide 20

Slide 20 text

従来手法の限界を実際の検索結果で検証 全文検索の場合 検索結果(実際のデータ例) 商品名 価格 説明文 検索結果 スコ ア 赤いワンピース ¥12,800 鮮やかな赤色のエレガント なワンピース ヒット 2.1 真紅のワンピー ス ¥18,900 美しい真紅色のフォーマル ワンピース 「ワンピース」の みヒット 0.8 結果: 6商品中3商品ヒット(検索成功率: 50%)、しかし関連性の低い結果も含む 問題: 単語の部分一致はできるが、意味的関連性は理解できない 20

Slide 21

Slide 21 text

従来手法の限界を実際の検索結果で検証 全文検索の場合 全文検索の限界 「赤い」と「クリムゾン」「真紅」「ルビーレッド」が同じ意味だと理解できない 「ワンピース」と「ドレス」の関連性を認識できない 無関係な商品でも単語が含まれていればヒットしてしまう 21

Slide 22

Slide 22 text

ベクトル検索について検証 22

Slide 23

Slide 23 text

ベクトル検索について検証 ベクトル検索の場合 同じクエリ「赤いワンピース」での検索結果 // ベクトル検索の実装例(PHP) $queryVector = $embedding->embed('赤いワンピース'); $results = $vectorSearch->searchSimilar($queryVector, 10); 23

Slide 24

Slide 24 text

ベクトル検索について検証 ベクトル検索の場合 検索結果(実際のベクトル類似度計算) 商品名 価格 類似度スコ ア 検索結 果 理由 赤いワンピース ¥12,800 0.98 ヒッ ト 完全一致 真紅のワンピース ¥18,900 0.92 ヒッ ト 「真紅」=「赤い」を理解 結果: 6商品中6商品すべてヒット(検索成功率: 100%) 特徴: 意味的な類似性を理解し、関連する商品をすべて発見 24

Slide 25

Slide 25 text

ベクトル検索について検証 ベクトル検索の場合 検索結果(実際のベクトル類似度計算) ベクトル検索の優位性 同義語・類義語を自動的に認識(赤い ≈ クリムゾン ≈ 真紅 ≈ ルビーレッド) 関連概念の理解(ワンピース ≈ ドレス) 類似度スコアによる関連性の定量化 ユーザーの意図に最も近い結果を提供 25

Slide 26

Slide 26 text

[結果] ユーザーの期待に応えられない検索体験 体験的にベクトルカラムがあった方が良い 26

Slide 27

Slide 27 text

結果:ユーザーの期待に応えられない検索体験 想定されるユーザー行動 検索失敗の影響 検索結果0件 → ユーザーが離脱 関連性の低い結果 → ユーザーが再検索 3回目の検索失敗 → ユーザーがサイト離脱 ビジネスへの影響 機会損失: 月間売上のが数十%実は取れてないなどがある 顧客満足度低下: サブスクリプションの場合は継続率に直結 競合他社への流出: そのまま同じ予算を切り替えるだけなので簡単 27

Slide 28

Slide 28 text

[検索の場合] 離脱リスクの課題を解決する1つの方法が ベクトル検索 28

Slide 29

Slide 29 text

※PHPだけでやってきたぜ!という方へわかりやすくするために、テキストのみの検索を対 象としましたが、画像などをベクトルとして取り込むこともできるのでさらに汎用性が高ま ります。 29

Slide 30

Slide 30 text

ここまでのまとめ 30

Slide 31

Slide 31 text

ここまでのまとめ 1. ベクトルカラムがなぜ必要か? 全文検索でもLIKE検索でも実現できない 意味の近さを使った検索を実現するため 31

Slide 32

Slide 32 text

なぜその結論か?説明していきます! 1. ベクトルカラムがなぜ必要か? 2. ベクトル検索の構成要素は?←←← 3. AWS+MySQLでは具体的にどのように利用するか? 32

Slide 33

Slide 33 text

2. ベクトル検索の構成要素は? 33

Slide 34

Slide 34 text

ベクトル検索の構成要素は? 1. テキストを数値(ベクトル)化するモデル 2. ベクトルを保存するストレージ 3. クエリとストレージから類似度を計算する処理 34

Slide 35

Slide 35 text

1. テキストを数値(ベクトル)化するモデル MLのモデルを使ってテキストを数値化(ベクトル)する テキスト → ベクトル(数値の配列) 「赤いワンピース」→ [0.1, -0.5, 0.3, ..., 0.2] 「クリムゾンドレス」→ [0.15, -0.48, 0.31, ..., 0.18] AIが言葉の意味を数値で表現する 35

Slide 36

Slide 36 text

1. テキストを数値(ベクトル)化するモデル MLのモデルを使ってテキストを数値化(ベクトル)する テキスト → ベクトル(数値の配列) 「赤いワンピース」→ [0.1, -0.5, 0.3, ..., 0.2] 「クリムゾンドレス」→ [0.15, -0.48, 0.31, ..., 0.18] 言葉の意味を数値(ベクトル)で表現する ※このベクトルは言語ごとに違います。テストに出るよ! (i18n対応に影響あり) 36

Slide 37

Slide 37 text

2. ベクトルを保存するストレージ テキストではなく、ベクトルで保存するストレージが必要 機能 PostgreSQL + pgvector MySQL 9 標準サーバー 距離計算 完全サポート HeatWave専用 インデックス HNSW/IVFFlat 作成不可 最大次元数 16,000 16,383 類似度検索 高速 アプリ層で実装必要 37

Slide 38

Slide 38 text

3. クエリとストレージから類似度を計算する処理 機能 PostgreSQL + pgvector MySQL 9 標準サーバー 距離計算 完全サポート HeatWave専用 インデックス HNSW/IVFFlat 作成不可 最大次元数 16,000 16,383 類似度検索 高速 アプリ層で実装必要 MySQL9のVECTOR型が大敗である・・・。 ※ CloudSQL for MySQLであれば、ベクトルカラムにインデックスが貼れますが、それでも 類似度計算は実装の必要があり、大敗。 38

Slide 39

Slide 39 text

ここまでのまとめ2 39

Slide 40

Slide 40 text

ベクトル検索の構成要素 1. テキストを数値(ベクトル)化するモデル MLのモデルを使ってベクトル化する 2. ベクトルを保存するストレージ RDBの場合はPostgreSQL+pgvector, MySQL9のベクトルカラムがわかりやすくある 3. クエリとストレージから類似度を計算する処理 ストレージに計算機能がない場合はアプリケーションで計算ロジックを実装する pgvectorが全体的に優勢w 40

Slide 41

Slide 41 text

なぜその結論か?説明していきます! 1. ベクトルカラムがなぜ必要か? 2. ベクトル検索の構成要素は? 3. AWS+MySQLでは具体的にどのように利用するか?←←← 41

Slide 42

Slide 42 text

大敗してるのに使う方法 考えるんですか・・・? 42

Slide 43

Slide 43 text

はい。 DBを切り替えずにユーザーに 体験させたいとかあるはず 43

Slide 44

Slide 44 text

3. AWS+MySQLでは具体的にどのように利用するか? あくまで現時点での実現可能な構成を考えます 1. MySQL9のVECTOR型はデータ保存にのみ使う 2. テキストのベクトル化についてはBedrockやOpenAIなどを 利用 3. 既存のPHPアプリケーションでベクトル計算 ※ まだ使えないので検証はできません 44

Slide 45

Slide 45 text

3. 既存のPHPアプリケーションでベクトル計算 だけ一旦考える 45

Slide 46

Slide 46 text

そもそもベクトル検索の「意味が近い」って何?から紐解ける ベクトル空間での距離の概念 直感的理解 地図上の2点間の距離と同じ概念 近い = 類似している 遠い = 異なっている 46

Slide 47

Slide 47 text

ベクトルで計算する際は コサイン類似度やユークリッド距離などを 使うことが多い なんか久しぶりに聞いたなそんなの 47

Slide 48

Slide 48 text

実装に入る前にMySQL9で標準で使える ベクトル系の標準関数を確認 48

Slide 49

Slide 49 text

MySQL9の機能区分 VECTOR型:ベクトルデータの保存 STRING_TO_VECTOR():文字列→ベクトル変換 VECTOR_DIM():ベクトル次元数取得 49

Slide 50

Slide 50 text

それでは実装していく 50

Slide 51

Slide 51 text

ベクトル化プロセス詳解 テキスト→ベクトル変換実装 class TextEmbedding { private $apiKey; private $model = 'text-embedding-3-small'; public function embed($text) { $url = 'https://api.openai.com/v1/embeddings'; $data = [ 'model' => $this->model, 'input' => $text, 'encoding_format' => 'float' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); // POSTデータ curl_setopt($ch, CURLOPT_HTTPHEADER, [ // HTTPヘッダー 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl exec($ch); 51

Slide 52

Slide 52 text

MySQL9 テーブル設計 CREATE TABLE products ( id INT PRIMARY KEY, name VARCHAR(255), description TEXT, description_vector VECTOR(1536) -- text-embedding-3-small次元 ); VECTOR型の特徴 データ型: 各エントリは4byte浮動小数点値 サイズ制限: デフォルト長:2048、最大:16383 制約: キーとして使用不可(PRIMARY/FOREIGN/UNIQUE) 操作: 比較は等価性のみ 52

Slide 53

Slide 53 text

データ挿入実装 public function insertProduct($name, $description) { $vectorString = $this->embedding->embedToVectorString($description); $sql = "INSERT INTO products (name, description, description_vector) VALUES (?, ?, STRING_TO_VECTOR(?))"; $stmt = $this->pdo->prepare($sql); $stmt->execute([$name, $description, $vectorString]); } STRING_TO_VECTOR関数 文字列 "[1.05, -17.8, 32]" → バイナリVECTOR 標準MySQL9で利用可能 逆変換はVECTOR_TO_STRING() 53

Slide 54

Slide 54 text

類似度検索実装(PHPで計算) 1. クエリのベクトル化と全データ取得 public function searchSimilar($query, $limit = 10) { // 1. クエリをベクトル化 $queryVector = $this->embedding->embed($query); // 2. 全ベクトル取得 $sql = "SELECT id, name, description_vector FROM products"; $stmt = $this->pdo->query($sql); $products = $stmt->fetchAll(); 54

Slide 55

Slide 55 text

類似度検索実装(PHPで計算) 2. コサイン類似度の計算とソート // 3. コサイン類似度計算 $similarities = []; foreach ($products as $product) { $vectorBinary = $product['description_vector']; $productVector = $this->binaryToFloatArray($vectorBinary); $similarity = $this->cosineSimilarity($queryVector, $productVector); $similarities[] = [ 'id' => $product['id'], 'name' => $product['name'], 'similarity' => $similarity ]; } // 4. 類似度でソート usort($similarities, function($a, $b) { // PHP標準ソート関数 return $b['similarity'] <=> $a['similarity']; 55

Slide 56

Slide 56 text

コサイン類似度計算 private function cosineSimilarity($a, $b) { $dotProduct = 0; $normA = 0; $normB = 0; for ($i = 0; $i < count($a); $i++) { $dotProduct += $a[$i] * $b[$i]; $normA += $a[$i] * $a[$i]; $normB += $b[$i] * $b[$i]; } return $dotProduct / (sqrt($normA) * sqrt($normB)); } private function binaryToFloatArray($binary) { // バイナリVECTORを浮動小数点配列に変換 $floats = []; for ($i = 0; $i < strlen($binary); $i += 4) { $floats[] = unpack('f', substr($binary, $i, 4))[1]; } return $floats; } 56

Slide 57

Slide 57 text

でもこのままだとパフォーマンスはもちろん 心配なので・・・ 57

Slide 58

Slide 58 text

パフォーマンス最適化戦略 58

Slide 59

Slide 59 text

パフォーマンス最適化戦略 1. 事前計算:商品データは事前にベクトル化 2. バッチ処理:APIコスト削減 3. インデックス戦略:IDでの絞り込み併用 59

Slide 60

Slide 60 text

まとめ3(ラスト) 60

Slide 61

Slide 61 text

まとめ3(ラスト) まとめ MySQL9のベクトルカラムはロマンはあるけど、実務で使うにはまだ工夫が必要! できるならPostgreSQL+pgvectorを使うのが良いかも! 61

Slide 62

Slide 62 text

まとめ3(ラスト) と思ったのですが・・・ S3 Vectorsが出たのでみんなそっちでやっていくといいぞ! 多分! 62

Slide 63

Slide 63 text

63

Slide 64

Slide 64 text

所属 株式会社TechBowl 何やってる? 「TechTrain」というサービスで反復横跳びし続けている何で も屋さん(Laravel, Next.js, AWS, etc...) 趣味 お酒(よく溺れる) サウナ 読書 64

Slide 65

Slide 65 text

ご静聴ありがとうございました! 65

Slide 66

Slide 66 text

色々続くよ! 66

Slide 67

Slide 67 text

References MySQL 9.0 Documentation OpenAI Embedding API 67

Slide 68

Slide 68 text

補足:MySQL HeatWaveとは 68

Slide 69

Slide 69 text

補足:MySQL HeatWaveとは Oracle提供のクラウドサービス フルマネージドMySQLサービス インメモリ分析アクセラレーター統合 OLTP(オンライントランザクション処理) + OLAP(オンライン分析処理)を単一 環境で実現 69

Slide 70

Slide 70 text

補足:MySQL HeatWaveとは 利用環境 Oracle Cloud Infrastructure(OCI) Amazon Web Services(AWS) Microsoft Azure オンプレミス不可 70

Slide 71

Slide 71 text

補足:MySQL HeatWaveとは 特徴 分析クエリを10-1000倍高速化 ETL不要のリアルタイム分析 機械学習機能統合 標準MySQLとの棲み分けを理解して適切に選択してください! 71

Slide 72

Slide 72 text

補足:pgvectorの使い方 72

Slide 73

Slide 73 text

補足:pgvectorの使い方 1. Dockerを使った簡単セットアップ # pgvector組み込み済みPostgreSQLコンテナ起動 docker pull pgvector/pgvector:pg17 docker run --name postgres-vector \ -e POSTGRES_PASSWORD=mypassword \ -p 5432:5432 \ pgvector/pgvector:pg17 73

Slide 74

Slide 74 text

2. 拡張機能の有効化 -- データベースに接続後、pgvectorを有効化 CREATE EXTENSION vector; -- 確認 SELECT extversion FROM pg_extension WHERE extname = 'vector'; 3. Ubuntu/Debianでの手動インストール Copy# PostgreSQL開発ファイルとpgvectorのインストール sudo apt install postgresql-server-dev-17 sudo apt install postgresql-17-pgvector または手動コンパイル git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git cd pgvector make sudo make install 74

Slide 75

Slide 75 text

基本的な使用方法 1. テーブル作成とデータ挿入 -- ベクトルテーブルの作成 CREATE TABLE documents ( id SERIAL PRIMARY KEY, title TEXT, content TEXT, embedding vector(1024) -- 1024次元のベクトル ); -- ベクトルデータの挿入 INSERT INTO documents (title, content, embedding) VALUES ('文書1', '人工知能について', '[0.1, 0.2, 0.3, ...]'), ('文書2', '機械学習の基礎', '[0.4, 0.5, 0.6, ...]'); -- 文字列からベクトルへの変換も可能 INSERT INTO documents (embedding) VALUES (string_to_vector('[1.0, 2.0, 3.0]')), (cast('[4.0, 5.0, 6.0]' as vector)); 75

Slide 76

Slide 76 text

2. ベクトル検索の実行 -- L2距離(ユークリッド距離)による最近傍検索 SELECT title, content, embedding <-> '[0.2, 0.3, 0.4, ...]' AS distance FROM documents ORDER BY distance LIMIT 5; 76

Slide 77

Slide 77 text

2. ベクトル検索の実行 -- コサイン類似度による検索 SELECT title, content, 1 - (embedding <=> '[0.2, 0.3, 0.4, ...]') AS cosine_similarity FROM documents ORDER BY cosine_similarity DESC LIMIT 5; -- 内積による検索(正規化済みベクトルの場合に最適) SELECT title, content, (embedding <#> '[0.2, 0.3, 0.4, ...]') * -1 AS inner_product FROM documents ORDER BY inner_product DESC LIMIT 5; 77

Slide 78

Slide 78 text

3. 距離関数の種類 -- <-> : L2距離(ユークリッド距離) -- <#> : 負の内積 -- <=> : コサイン距離 -- <+> : L1距離(マンハッタン距離) -- <~> : ハミング距離(バイナリベクトル用) -- <%> : ジャカード距離(バイナリベクトル用) -- 関数形式でも利用可能 SELECT l2_distance(embedding, '[1,2,3]') AS euclidean, cosine_distance(embedding, '[1,2,3]') AS cosine, inner_product(embedding, '[1,2,3]') AS dot_product FROM documents; 78

Slide 79

Slide 79 text

高性能インデックスの作成 1. HNSWインデックス(推奨) -- L2距離用HNSWインデックス CREATE INDEX ON documents USING hnsw (embedding vector_l2_ops); -- コサイン距離用HNSWインデックス CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops); -- 内積用HNSWインデックス CREATE INDEX ON documents USING hnsw (embedding vector_ip_ops); -- パラメータ調整版 CREATE INDEX ON documents USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64); 79

Slide 80

Slide 80 text

2. IVFFlatインデックス -- データが十分蓄積されてから作成 CREATE INDEX ON documents USING ivfflat (embedding vector_l2_ops) WITH (lists = 100); -- リスト数の目安: -- ~100万行: rows / 1000 -- 100万行超: sqrt(rows) 80

Slide 81

Slide 81 text

3. 検索パフォーマンスの調整 -- HNSW検索精度の調整 SET hnsw.ef_search = 100; -- デフォルト40 -- IVFFlat検索範囲の調整 SET ivfflat.probes = 10; -- デフォルト1 -- トランザクション内での一時設定 BEGIN; SET LOCAL hnsw.ef_search = 200; SELECT * FROM documents ORDER BY embedding <-> '[...]' LIMIT 10; COMMIT; 81

Slide 82

Slide 82 text

実践的な応用例 1. フィルタリングとの組み合わせ -- カテゴリフィルタとベクトル検索の組み合わせ CREATE INDEX ON documents (category_id); -- フィルタ用インデックス -- 効率的な検索 SELECT * FROM documents WHERE category_id = 123 ORDER BY embedding <-> '[...]' LIMIT 5; -- フィルタリング専用の部分インデックス CREATE INDEX ON documents USING hnsw (embedding vector_l2_ops) WHERE category_id = 123; 82

Slide 83

Slide 83 text

2. ハイブリッド検索(全文検索+ベクトル検索) -- 全文検索インデックスも作成 CREATE INDEX ON documents USING gin(to_tsvector('english', content)); -- ハイブリッド検索の実装 WITH vector_results AS ( SELECT id, title, embedding <-> '[...]' AS vec_distance FROM documents ORDER BY vec_distance LIMIT 20 ), text_results AS ( SELECT id, title, ts_rank_cd(to_tsvector('english', content), query) AS text_score FROM documents, plainto_tsquery('english', 'machine learning') query WHERE to_tsvector('english', content) @@ query ORDER BY text_score DESC LIMIT 20 ) -- Reciprocal Rank Fusionで結合 SELECT * FROM vector_results UNION SELECT * FROM text_results; 83

Slide 84

Slide 84 text

3. メモリ使用量の最適化 -- 半精度ベクトルでメモリ使用量を半減 CREATE TABLE efficient_docs ( id SERIAL PRIMARY KEY, title TEXT, embedding halfvec(1024) -- 半精度ベクトル ); -- 半精度でのインデックス作成 CREATE INDEX ON efficient_docs USING hnsw (embedding halfvec_l2_ops); -- バイナリ量子化でさらなる圧縮 CREATE INDEX ON documents USING hnsw ((binary_quantize(embedding)::bit(1024)) bit_hamming_ops); 84

Slide 85

Slide 85 text

パフォーマンスチューニング 85

Slide 86

Slide 86 text

1. PostgreSQL設定の最適化 # postgresql.conf shared_buffers = 25% of RAM maintenance_work_mem = 2GB # インデックス作成用 max_parallel_workers_per_gather = 4 max_parallel_maintenance_workers = 4 86

Slide 87

Slide 87 text

2. インデックス作成の最適化 -- インデックス作成の高速化 SET maintenance_work_mem = '8GB'; SET max_parallel_maintenance_workers = 7; -- 本番環境では同時実行インデックス作成 CREATE INDEX CONCURRENTLY idx_vector ON documents 3. 監視とチューニング -- インデックス使用状況の確認 EXPLAIN ANALYZE SELECT * FROM documents ORDER BY embedding <-> '[...]' LIMIT 5; -- インデックスサイズの確認 SELECT pg_size_pretty(pg_relation_size('idx_vector')); 87

Slide 88

Slide 88 text

多様なベクトル型の活用 88

Slide 89

Slide 89 text

1. スパースベクトル -- スパースベクトルテーブル CREATE TABLE sparse_data ( id SERIAL PRIMARY KEY, sparse_vec sparsevec(1000) ); -- スパースベクトルの挿入({index:value}/dimensions形式) INSERT INTO sparse_data (sparse_vec) VALUES ('{1:1.0, 10:2.0, 100:3.0}/1000'), ('{5:4.0, 50:5.0, 500:6.0}/1000'); 89

Slide 90

Slide 90 text

2. バイナリベクトル -- バイナリベクトル(画像ハッシュなど) CREATE TABLE image_hashes ( id SERIAL PRIMARY KEY, image_hash bit(64) ); -- ハミング距離による検索 SELECT * FROM image_hashes ORDER BY image_hash <~> '1010101010101010...' LIMIT 5; 90

Slide 91

Slide 91 text

PostgreSQLのpgvectorは、MySQL 9と比較して遥かに成熟した本格的なベクトルデータベー ス機能を提供します。特に距離計算とインデックス機能が標準で利用可能な点が大きな優位 性となっています。 91

Slide 92

Slide 92 text

92

Slide 93

Slide 93 text

ベクトル距離の視覚的理解 2次元空間でのベクトル距離 %%{init: {'theme': 'base', 'themeVariables': { 'background': 'transparent', 'primaryColor': 'transparent', 'primaryBorderColor': '#ffffff', 'primaryTextColor': '#ffffff', 'lineColor': '#ffffff', 'arrowheadColor': '#ffffff', 'edgeLabelBackground': '#3D3D5C' }}}%% graph LR subgraph "2次元ベクトル空間" O[原点 (0,0)] A["赤いワンピース (0.8, 0.6)"] B["クリムゾンドレス (0.75, 0.65)"] C["青いTシャツ 93

Slide 94

Slide 94 text

コサイン類似度の視覚的理解 ベクトル間の角度 = 意味の類似性 %%{init: {'theme': 'base', 'themeVariables': { 'background': 'transparent', 'primaryColor': 'transparent', 'primaryBorderColor': '#ffffff', 'primaryTextColor': '#ffffff', 'lineColor': '#ffffff', 'arrowheadColor': '#ffffff', 'edgeLabelBackground': '#3D3D5C' }}}%% graph TD subgraph "コサイン類似度の概念" O[原点] A["ベクトルA 「赤いワンピース」"] B["ベクトルB 「クリムゾンドレス」"] C["ベクトルC 94

Slide 95

Slide 95 text

95

Slide 96

Slide 96 text

テキストをベクトルに変換するとは? Embeddingの概念 Embedding(埋め込み) とは、テキストを多次元の数値ベクトルに変換する技術 「赤いワンピース」→ [0.1, -0.5, 0.3, ..., 0.2] (1536次元) 意味的に近い言葉は、ベクトル空間で近い位置に配置される 人間には理解しづらいが、コンピュータで扱いやすい形式 96

Slide 97

Slide 97 text

「赤いワンピース」のベクトル変換プロセス ステップ1: テキスト入力 入力テキスト 「赤いワンピース」 AIモデルの理解プロセス 1. 単語分解: 「赤い」+ 「ワンピース」 2. 意味解析: 色(赤)+ 衣服(ワンピース) 3. 文脈理解: 女性用衣服、ファッション、色彩 4. 関連概念: エレガント、フォーマル、カジュアル 97

Slide 98

Slide 98 text

ステップ2: ベクトル変換(実際の数値例) OpenAI text-embedding-3-small による変換結果 実際のベクトル値(一部抜粋) 「赤いワンピース」→ [ 0.0234, // 色彩関連の次元 -0.1567, // 衣服関連の次元 0.0891, // 女性用品関連の次元 0.2134, // ファッション関連の次元 -0.0456, // フォーマル度関連の次元 ... // 1536次元まで続く 0.1789 ] ベクトルの特徴 次元数: 1536次元(text-embedding-3-small) 98

Slide 99

Slide 99 text

ステップ3: 類似商品のベクトル比較 実際の数値を使った類似度計算 各商品のベクトル値(簡略化した5次元で説明) 商品名 次元1 次元2 次元3 次元4 次元5 赤いワンピース 0.023 -0.157 0.089 0.213 -0.046 クリムゾンドレス 0.031 -0.149 0.095 0.198 -0.052 真紅のワンピース 0.028 -0.162 0.087 0.221 -0.041 青いTシャツ -0.156 -0.089 0.034 0.067 0.123 コサイン類似度の計算例 「赤いワンピース」vs「クリムゾンドレス」 99

Slide 100

Slide 100 text

ステップ4: 類似度ランキングの生成 計算結果による商品ランキング 「赤いワンピース」との類似度ランキング 順位 商品名 類似度スコア 解釈 1位 赤いワンピース 1.00 完全一致(同じ商品) 2位 真紅のワンピース 0.92 非常に類似(色+形状) 3位 クリムゾンドレス 0.89 高い類似性(色+関連形状) 4位 ルビーレッドワンピース 0.85 高い類似性(色+形状) 5位 ワインレッドドレス 0.78 中程度の類似性(色系統) 6位 赤色のスカート 0.65 中程度の類似性(色のみ) 7位 青いTシャツ 0.21 低い類似性(異なる色) 100

Slide 101

Slide 101 text

ベクトル変換の仕組み なぜベクトルに変換するのか? %%{init: {'theme': 'base', 'themeVariables': { 'background': 'transparent', 'primaryColor': 'transparent', 'primaryBorderColor': '#ffffff', 'primaryTextColor': '#ffffff', 'lineColor': '#ffffff', 'arrowheadColor': '#ffffff', 'edgeLabelBackground': '#3D3D5C' }}}%% graph LR A["テキスト '赤いワンピース'"] -->|AIモデル| B["ベクトル [0.023, -0.157, ...]"] C["テキスト 'クリムゾンドレス'"] -->|AIモデル| D["ベクトル [0.031, -0.149, ...]"] E["テキスト 101

Slide 102

Slide 102 text

ベクトルの類似度計算 コサイン類似度の直感的理解 計算式 cos(θ) = (A・B) / (|A| × |B|) 値の意味 1.0: 完全に同じ意味 0.7~0.9: かなり類似 0.5~0.7: ある程度類似 0.5未満: 関連性が低い 例:「赤いワンピース」との類似度 クリムゾンドレス: 0.85 真紅のワンピース: 0.92 赤色のスカート: 0.73 青いTシャツ: 0.21 102

Slide 103

Slide 103 text

ベクトル検索実現のために必要なステップ 1. 単語/文章 → 数値の配列(ベクトルにする = 埋め込み) 2. 類似度 = ベクトル計算(コサイン類似度やユークリッド距 離など) 3. AI埋め込みモデルの活用 (コサイン類似度なんて高校の数学以来くらいで聞く人も多いのでは・・・) 103

Slide 104

Slide 104 text

ベクトル検索に必要なものは、次の3つ 1. テキストをベクトルに変換するための機構 → ライブラリや OpenAPI Embedding API 2. ベクトルを保管するためのカラム → VECTOR型のDatabase のカラムなど 3. ベクトルの意味的な距離を計算するための機能 → DISTANCE関数やアプリケーション実装 104

Slide 105

Slide 105 text

バッチ処理で効率化 なぜバッチ処理が重要? 単一リクエスト方式の問題点 graph LR A[商品1] -->|API呼出1| B[ベクトル1] C[商品2] -->|API呼出2| D[ベクトル2] E[商品3] -->|API呼出3| F[ベクトル3] style A fill:#f9f,stroke:#333,stroke-width:2px style C fill:#f9f,stroke:#333,stroke-width:2px style E fill:#f9f,stroke:#333,stroke-width:2px 課題: 1000商品 = 1000回のAPI呼び出し → 高コスト・低速 105

Slide 106

Slide 106 text

バッチ処理の実装 embedBatch()メソッド public function embedBatch($texts) { $data = [ 'model' => $this->model, 'input' => $texts, // 配列で一括送信 'encoding_format' => 'float' ]; // APIリクエスト処理... $result = json_decode($response, true); $embeddings = []; foreach ($result['data'] as $item) { $embeddings[] = '[' . implode(',', $item['embedding']) . ']'; } ポイント: input に配列を渡すことで複数テキストを一括処理 106

Slide 107

Slide 107 text

応用層への移行 107

Slide 108

Slide 108 text

応用層への移行 いつ・どのように導入するか? 実装層で学んだこと MySQL9の具体的な機能 段階的なコード実装方法 実際の動作例 応用層で学ぶこと 実装選択肢の比較と判断基準 ユースケース別の推奨事項 導入のための具体的な次のステップ 108

Slide 109

Slide 109 text

応用層 いつ・どのように導入するか? 意思決定のための判断基準を獲得しましょう 109

Slide 110

Slide 110 text

HeatWave vs 標準MySQL 項目 標準MySQL HeatWave VECTOR型 利用可能 利用可能 距離計算 PHP実装 DISTANCE関数 パフォーマンス 中小規模で実用的 大規模で高速 コスト 低い 高い 環境 どこでも クラウド限定 HeatWaveの例 SELECT name, DISTANCE(description_vector, ?, "COSINE") as similarity FROM products ORDER BY similarity LIMIT 10; 110

Slide 111

Slide 111 text

ユースケース別推奨事項 データ規模・予算・技術レベル別の選択指針 シナリオ データ規模 予算 推奨アプローチ 理由 スタートアップ ~10万件 低 標準MySQL + PHP 初期コスト抑制、柔軟性 中規模EC ~100万件 中 標準MySQL + 最適化 バランス重視 大規模サービス 1000万件+ 高 HeatWave パフォーマンス最優先 実験・検証 任意 低 標準MySQL + PHP 学習コスト最小 111

Slide 112

Slide 112 text

次のステップガイド 開発者向け 1. 環境構築: MySQL 9の検証環境セットアップ 2. プロトタイプ作成: 小規模データでの動作確認 3. パフォーマンステスト: 実データでの性能評価 意思決定者向け 1. ROI分析: 検索改善による売上向上の試算 2. コスト比較: 開発・運用コストの詳細見積もり 3. 段階的導入計画: リスクを抑えた導入戦略 112

Slide 113

Slide 113 text

パフォーマンス最適化戦略 キャッシュ戦略 // クエリベクトルのキャッシュ $cacheKey = 'vector_' . md5($query); $queryVector = $this->cache->get($cacheKey); if (!$queryVector) { $queryVector = $this->embedding->embed($query); $this->cache->set($cacheKey, $queryVector, 3600); } 113