こちらの記事は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のスタンバイインスタンスへオフロードしてみました。

作業ステップ
- 事前作業
- モデルのインポート
- 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
- OML4Py2.1のダウンロードとインストール
- 以下サイトからOML4Py 2.1 以上をダウンロードし、サーバに配置
https://www.oracle.com/database/technologies/oml4py-downloads.html
zipファイルを解凍し、Perlスクリプトを使ってOML4Pyをインストール
- 以下サイトからOML4Py 2.1 以上をダウンロードし、サーバに配置
$ 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列に格納)
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を組み合わせることの主なメリットは次の通りです。
- プライマリデータベースへ影響を与えずリソースを最大限活用
- シンプルなアーキテクチャによるコスト削減の可能性
- 外部通信なしでインライン推論を実行
参考情報
- In-database AI inference on Oracle Active Data Guard: A practical walkthrough
- サンプルコードやReactアプリの例
- Oracle Database RU23.7で機能追加されたDB内マルチモーダルEmbeddingを試してみた
- [Oracle Cloud]インポートしたONNXフォーマットモデルでOracle Database 23ai表データをEmbeddingしてみた。(2024/05/06)
- フィジカル・スタンバイ・データベースのオープン
- Offload more than Read-Only Workloads to Your Standby Database