3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Oracle Cloud]データベース内Embeddingとセマンティック検索をActive Data Guard スタンバイインスタンスへのオフロード (2025/12/09)

3
Last updated at Posted at 2025-12-08

こちらの記事はOracle Cloud Infrastructure Advent Calendar 2025 シリーズ1 Day 09 の記事として書かれています。Day 08の記事は @ssfujita さんの記事「生成AIによるデータ加工でメダリオン・アーキテクチャを構築できるOracle AI Data Platform (AIDP) を使ってデータ分析してみた」でした。

はじめに

Oracle Database 26ai では、ONNXフォーマットでエクスポートした学習済みEmbeddingモデルをインポート(ロード)し、データベース内のEmbeddingに使用することができます。
負荷が高くなるEmbeddingの処理や新しいワークロードとなるセマンティック検索をActive Data Guardのスタンバイインスタンスへオフロードしてみました。
image.png

作業ステップ

  • 事前作業
  • モデルのインポート
  • Embedding実行するプロシージャの作成
  • Active Data GuardスタンバイインスタンスでEmbeddingの実施
  • Active Data Guardスタンバイインスタンスでセマンティック検索の実行

事前作業

Oracle Instance Client のインストール

Oracle Instance Client のインストールして、プライマリインスタンス、スタンバイインスタンスそれぞれに接続できるように設定します。

Oracle Machine Learning for Python(OML4Py)2.1の準備

  • OML4Py 2.1構成要件
Oracle Databaseリリース Pythonバージョン Operating Systemバージョン
26ai(Clientのみ) 3.12.6以上 Linux X64 ( OL8 )
  • OML4Pyに必要なOSパッケージをインストール

    $ sudo  dnf install perl-Env libffi-devel openssl-devel tk-devel xz-devel zlib-devel bzip2-devel readline-devel libuuid-devel ncurses-devel
    
  • Python仮想環境の作成と有効化(アクティベート)

    • uv の場合
    • プロジェクト初期化
uv init oml2.1
cd oml2.1
uv venv --python 3.12
  • pyproject.toml を開いて、[project] セクション内の requires-python を変更:
requires-python = ">=3.10, <3.13"
[tool.uv]
extra-index-url = ["https://download.pytorch.org/whl/cpu"]
  • .python-version を 3.12 に 変更
  • 仮想環境の有効化(アクティベート)
source .venv/bin/activate
  • パッケージの追加
$ python3 -m ensurepip --upgrade
Looking in links: /tmp/tmpyjjkbsk5
Processing /tmp/tmpyjjkbsk5/pip-25.0.1-py3-none-any.whl
Installing collected packages: pip
Successfully installed pip-25.0.1
uv add pandas
uv add setuptools
uv add scipy
uv add matplotlib
uv add oracledb
uv add joblib
uv add scikit-learn
uv add numpy
uv add onnx
uv add onnxruntime
uv add onnxruntime-extensions
uv add transformers
uv add sentencepiece
uv add torch --extra-index-url https://download.pytorch.org/whl/cpu
uv add python-dotenv
$ unzip ~/V1048628-01.zip
$ perl -Iclient client/client.pl


Oracle Machine Learning for Python 2.1 Client.

Copyright (c) 2018, 2025 Oracle and/or its affiliates. All rights reserved.
Checking platform .................. Pass
Checking Python .................... Pass
Checking dependencies .............. Pass
Checking OML4P version ............. Pass
Current configuration
  Python Version ................... 3.12.12
  PYTHONHOME ....................... /home/opc/oml2.1/.venv
  Existing OML4P module version .... None

  Operation ........................ Install/Upgrade

Proceed? [yes]yes

Processing ./client/oml-2.1-cp312-cp312-linux_x86_64.whl
Installing collected packages: oml
Successfully installed oml-2.1

Done

作業用データベースユーザ、表の作成

プライマリインスタンスのPDBにSYSDBA権限で接続し、データベースユーザ、表を作成

$ sqlplus / as sysdba

alter session set container=pdb1;
-- run this script as a DBA on the primary PDB
create user adgvec identified by &adgvecpass;
create role vec_role not identified;
-- most developer require these grants:
grant db_developer_role to vec_role;
-- usage of ONNX models:
grant create mining model to vec_role;
-- optional: grants for Application Continuity:
grant keep date time to vec_role;
grant keep sysguid to vec_role;
grant vec_role to adgvec;
alter user adgvec quota unlimited on users;
-- run this script as the user (ADGVEC) on the primary PDB
-- contains the images
create table if not exists pictures (
   id       varchar2(20) primary key,
   img_size number,
   img      blob
);
-- contains vector embeddings (1:1 relation with pictures)
create table if not exists picture_embeddings (
   id         varchar2(20),
   embedding vector,
   constraint picture_embeddings_pk primary key ( id ),
   constraint picture_embeddings_fk foreign key ( id ) references pictures ( id )
);

サンプル画像ファイルの用意

検索対象とする画像ファイルを用意します。
OML4pyをインストールしたマシンに配置します。

$ pwd
/home/opc/oml2.1/image
$ ls
image001.jpg  image004.jpg  image007.jpg  image010.jpg
image002.jpg  image005.jpg  image008.jpg  image011.jpg
image003.jpg  image006.jpg  image009.jpg

画像データをpicturesテーブルにロード

環境変数の設定

export ORACLE_HOME=/usr/lib/oracle/23/client64/lib
export TNS_ADMIN=/home/opc/oml2.1
export DB_USER=ADGVEC
export DB_PASSWORD=<PASSWORD>
export DB_DSN=PDB1
export IMAGE_DIRECTORY=/home/opc/oml2.1/image

画像ファイルをpictures表にインサート(ファイル名をID列に格納)

Python insert.py
import os
import oracledb
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Database connection parameters and image directory
db_user = os.getenv('DB_USER')
db_password = os.getenv('DB_PASSWORD')
db_dsn = os.getenv('DB_DSN')
image_directory = os.getenv('IMAGE_DIRECTORY')
oracle_home = os.getenv('ORACLE_HOME')
oracledb.init_oracle_client(lib_dir=oracle_home)
# Establish a database connection
with oracledb.connect(user=db_user, password=db_password, dsn=db_dsn) as connection:
    with connection.cursor() as cursor:
        for filename in os.listdir(image_directory):
            if filename.endswith('.jpg'):
                image_id = os.path.splitext(filename)[0]
                image_path = os.path.join(image_directory, filename)
                with open(image_path, 'rb') as image_file:
                    image_data = image_file.read()
                    image_size = len(image_data)
                    try:
                        # Insert the image data into the 'pictures' table
                        cursor.execute("""
                            INSERT INTO pictures (id, img_size, img)
                            VALUES (:id, :img_size, :img)""",
                            {'id': image_id, 'img_size': image_size, 'img': image_data})
                    except oracledb.IntegrityError as e:
                        error_obj, = e.args
                        if error_obj.code == 1:  # ORA-00001: unique constraint violated
                            print(f"Skipping image {filename}: ID {image_id} already exists.")
                        else:
                            raise
                    print(image_id)

        # Commit the transaction
        connection.commit()
$ python3 insert.py
image001
image002
image003
image004
image005
image006
image007
image008
image009
image010
image011
$

モデルのインポート

ONNXのエクスポート

以下マニュアルに沿って作業を進めます。
https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/onnx-pipeline-models-multi-modal-embedding.html

clip-vit-large-patch14 のONNXファイル作成 (テキスト向けと画像向けの2種類を生成)

from oml.utils import ONNXPipeline
pipeline = ONNXPipeline("openai/clip-vit-large-patch14")
pipeline.export2file("clip")

これで clip_txt.onnx と clip_img.onnx の2つが作成されます。
プライマリインスタンスのサーバーのディレクトリオブジェクト内にONNXファイルを配置します。

DBMS_VECTOR.LOAD_ONNX_MODELプロシージャを使用して、エクスポートしたONNXファイル(clip_txt.onnx と clip_img.onnx)をインポート(ロード)します。

引数

  • ディレクトリオブジェクト名 : DATA_PUMP_DIR
  • テキスト用モデル
    • インポートするONNXファイル名 : clip_txt.onnx
    • 登録するモデル名 : clip_txt
  • イメージ用モデル
    • インポートするONNXファイル名 : clip_img.onnx
    • 登録するモデル名 : clip_img
EXECUTE DBMS_VECTOR.LOAD_ONNX_MODEL( 'DATA_PUMP_DIR', 'clip_txt.onnx', 'clip_txt');
EXECUTE DBMS_VECTOR.LOAD_ONNX_MODEL( 'DATA_PUMP_DIR', 'clip_img.onnx', 'clip_img');

登録されたモデルの確認(user_mining_modelsビュー)

SQL> select model_name, mining_function, algorithm, model_size from user_mining_models;

MODEL_NAME           MINING_FUNCTION                ALGORITHM            MODEL_SIZE
-------------------- ------------------------------ -------------------- ----------
CLIP_IMG             EMBEDDING                      ONNX                  306374876
CLIP_TXT             EMBEDDING                      ONNX                  125759349

Embedding実行するプロシージャの作成

process_embeddingプロシージャの作成

/*
This script creates a procedure that process the embeddings of the images in the pictures table and insert them via DML redirection.
*/
CREATE OR REPLACE PROCEDURE process_embeddings (
    p_batch_size IN PLS_INTEGER,
    p_iterations IN PLS_INTEGER
) AS
    TYPE t_embedding IS RECORD (
        id            pictures.id%TYPE,
        embed_vector  picture_embeddings.embedding%TYPE
    );
    TYPE t_embedding_table IS TABLE OF t_embedding;
    v_embeddings t_embedding_table;
    v_batch_count PLS_INTEGER := 0;
    v_total_processed PLS_INTEGER := 0;
    v_continue BOOLEAN := TRUE;
    CURSOR c_embeddings IS
        SELECT c.id,
               vector_embedding(CLIP_IMG USING img AS data) AS embed_vector
        FROM pictures c
        LEFT OUTER JOIN picture_embeddings v ON c.id = v.id
        WHERE v.id IS NULL AND c.img IS NOT NULL and c.id IS NOT NULL;
BEGIN
    OPEN c_embeddings;
    EXECUTE IMMEDIATE 'ALTER SESSION ENABLE ADG_REDIRECT_DML';
    LOOP
        FETCH c_embeddings BULK COLLECT INTO v_embeddings LIMIT p_batch_size;
        EXIT WHEN v_embeddings.COUNT = 0;
        FOR i IN v_embeddings.FIRST .. v_embeddings.LAST LOOP
            DBMS_OUTPUT.PUT_LINE('Processing ID: ' || v_embeddings(i).id);
            INSERT INTO picture_embeddings (id, embedding)
            VALUES (v_embeddings(i).id, v_embeddings(i).embed_vector);
        END LOOP;
        COMMIT;
        v_total_processed := v_total_processed + v_embeddings.COUNT;
        v_batch_count := v_batch_count + 1;
        IF p_iterations > 0 AND v_batch_count >= p_iterations THEN
            v_continue := FALSE;
        END IF;
        EXIT WHEN NOT v_continue;
    END LOOP;
    CLOSE c_embeddings;
END process_embeddings;
/

Procedure created.

このプロシージャでは、「 ALTER SESSION ENABLE ADG_REDIRECT_DML 」を使用してDMLリダイレクトを有効にしています。セッションでDMLリダイレクトが有効になると、スタンバイデータベース上で実行するDML文がプライマリデータベース上で実行されます。

Active Data GuardスタンバイインスタンスでEmbeddingの実施

  • Active Data Guardスタンバイインスタンスでロールと状態確認
    OPEN_MODEが「READ ONLY WITH APPLY」(参照可能かつ REDO Apply あり)であることを確認
$ sqlplus / as sysdba

SQL> SELECT DATABASE_ROLE, OPEN_MODE FROM V$DATABASE;

DATABASE_ROLE    OPEN_MODE
---------------- --------------------
PHYSICAL STANDBY READ ONLY WITH APPLY

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ ONLY  NO

補足)スタンバイ DB を「参照可能かつ REDO Apply あり(READ ONLY WITH APPLY)」にする手順

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
ALTER DATABASE OPEN READ ONLY;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;

Active Data Guardスタンバイインスタンス上でプロシージャを実行

$ sqlplus adgvec@pdb1
Copyright (c) 1982, 2025, Oracle.  All rights reserved.

Enter password:
SQL> set serveroutput on
SQL> select count(*) from PICTURES;

  COUNT(*)
----------
        11
        
-- プロシージャ実行前は、PICTURE_EMBEDDINGS表は0件
SQL> select count(*) from PICTURE_EMBEDDINGS;

  COUNT(*)
----------
         0

SQL>  EXECUTE process_embeddings(p_batch_size => 10, p_iterations => 100);

Processing ID: image001
Processing ID: image002
Processing ID: image003
Processing ID: image004
Processing ID: image005
Processing ID: image006
Processing ID: image007
Processing ID: image008
Processing ID: image009
Processing ID: image010
Processing ID: image011

PL/SQL procedure successfully completed.


-- プロシージャ実行後は、PICTURE_EMBEDDINGS表は11件
SQL>  select count(*) from PICTURE_EMBEDDINGS;

  COUNT(*)
----------
        11

CPU負荷の高いEmbedding処理をスタンバイデータベース側にオフロードすることで、プライマリ側は通常処理のリソースを確保できます。

Active Data Guardスタンバイインスタンスでセマンティック検索の実行

  • VECTOR_EMBEDDING(cliptxt USING 'a blue bird' AS data) は、検索クエリ(この例では “a blue bird”)を画像の埋め込みベクトルと同じ空間にエンコードします。
  • VECTOR_DISTANCE は、セマンティック的に最も近い画像を検索します
SQL> SELECT c.id, c.img,VECTOR_DISTANCE(v.embedding,VECTOR_EMBEDDING(clip_txt USING 'a blue bird' AS data),COSINE) AS distance
  2  FROM pictures c JOIN picture_embeddings v ON c.id = v.id ORDER BY distance FETCH FIRST 10 ROWS ONLY;

ID
--------------------
IMG
--------------------------------------------------------------------------------
  DISTANCE
----------
image004
FFD8FFE000104A46494600010100000100010000FFDB008400090607131212151213131516151715
15151515151515151515151515161715151515181D2820181A251D151521312125292B2E2E2E171F
7.144E-001

image006
FFD8FFE000104A46494600010100000100010000FFDB0084000906071212121510101012150F0F15
15150F0F15150F150F0F0F1515161615151515181D2820181A251D151521312125292B2E2E2E171F
7.165E-001

image010
FFD8FFE000104A46494600010100000100010000FFDB0084000906070F0F0F0D0D0F0F0D0D0D0D0D
0D0D0D0D0D0D0F0D0E0D0D1511161615111515181D2820181A251D151521312226292B2E2E30171F
7.213E-001

image001
FFD8FFE000104A46494600010100000100010000FFDB008400090607131312151313131616151618
1A18171717171818171A1D1D18181718171A181D1D2820181D251D171721312225292B2E2E2E171F
7.273E-001

image009
FFD8FFE000104A46494600010100000100010000FFDB008400090607121312151212121515121717
15151515151515151515151515161715151515181D2820181A251D151521312125292B2E2E2E171F
7.349E-001

image007
FFD8FFE000104A46494600010100000100010000FFDB008400090607121212151010130F150F1015
1010100F10100F0F1010151511161615111515181D2820181A251D151521312125292B2E2E2E171F
7.353E-001

image005
FFD8FFE000104A46494600010100000100010000FFDB008400090607101010151110101515151619
18151815151515161916171618171917161617181D2820181A261B181722312125292B2E2E2E171F
7.398E-001

image003
FFD8FFE000104A46494600010100000100010000FFDB008400090607121312151312131516151718
1718181818181A1818181A17171A17181D1A1D1A1D2820191A251D181D22312125292B2E2E2E181F
7.408E-001

image011
FFD8FFE000104A46494600010100000100010000FFDB008400090607121312121312121515151715
15161515151515161515151515161615161515181D2820181A251D151521312125292B2E2E2E171F
7.446E-001

image008
FFD8FFE000104A46494600010100000100010000FFDB008400090607131312151313131616151718
1D18181618191D171B1A1E1D17171D181D18171D1D28201D1A251D171722312125292B2E2E2E1A1F
7.449E-001

10 rows selected.

AIベクトル検索は、スタンバイデータベース内で安全に実行されます。

おわりに

ONNXのEmveddingモデルをデータベースにインポートし表データのEmveddingをActive Data Guardスタンバイインスタンスで実施してみました。Embeddingされたデータに対してセマンティック検索もすることができました。
Active Data Guardを組み合わせることの主なメリットは次の通りです。

  • プライマリデータベースへ影響を与えずリソースを最大限活用
  • シンプルなアーキテクチャによるコスト削減の可能性
  • 外部通信なしでインライン推論を実行

参考情報

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?