シャーディング・キーとしてのSODA IDの使用

分散データベース・スキーマの作成時に、SODA IDをシャーディング・キーとして指定できます。

次の例は、JSONコレクション用のシャード表を作成し、SODAマッピングを作成し、アプリケーションからシャード表にアクセスする方法をJavaおよびPythonコード・サンプルで示しています。

JSONコレクション用のシャード表の作成

SODA IDをシャーディング・キーとして使用するシャード表を作成するには、次のようにします。

  1. シャード・カタログにSharding/SODAユーザーとして接続します。
  2. SHARD DDLを有効にします。
  3. 表領域セットを作成します。
  4. 次の例に示すように、CREATE SHARDED TABLEを実行します。

次の例では、顧客プロファイル・ドキュメント(CUSTPROFILE)のJSONコレクション用のシャード表(Customers)を作成します。

SODA ID (ID)の列がJSONエントリを識別し、主キーおよびシャーディング・キーとしても使用されます。SODAを使用してJSONエントリを表に作成すると、アプリケーションによってID列に一意の値が移入されます。

その他の列は、SODAによって基礎となるコレクションを保持する表が作成されたときに指定されたデフォルトの列名です。これは、SODAコレクションを作成し、作成した表を調べると自分で確認できます。

シャード表の作成: システム管理

/* Enable shard DDL */
ALTER SESSION ENABLE SHARD DDL;

/* Create a tablespace set */
CREATE TABLESPACE SET TSP_SET_1 USING TEMPLATE
 (datafile size 100m autoextend on next 10M maxsize unlimited
 extent management local segment space management auto); 

/* Create the sharded table */
CREATE SHARDED TABLE CUSTOMERS
(
"ID" VARCHAR2(255) NOT NULL,
"CREATED_ON" timestamp default sys_extract_utc(SYSTIMESTAMP) NOT NULL,
"LAST_MODIFIED" timestamp default sys_extract_utc(SYSTIMESTAMP) NOT NULL,
"VERSION" varchar2(255) NOT NULL,
"CUSTPROFILE" JSON,
PRIMARY KEY (ID),
)
TABLESPACE SET TSP_SET_1
PARTITION BY CONSISTENT HASH (ID) PARTITIONS AUTO;

シャード表の作成: ユーザー定義

SODA IDに意味のある値がある場合は、ユーザー定義の方法を使用してデータベースをシャーディングでき、次の例を使用してシャード表を作成できます。

ユーザー定義分散データベースにシャード表を作成する前に、必要な表領域およびシャード領域が作成されていることを確認してください。分散データベース・オブジェクトの作成の詳細は、ユーザー定義のデータ分散および分散データベース・トポロジの構成を参照してください。

/* Enable shard DDL */
ALTER SESSION ENABLE SHARD DDL;

/* Create the sharded table */
CREATE SHARDED TABLE CUSTOMERS
(
"ID" VARCHAR2(255) NOT NULL,
"CREATED_ON" timestamp default sys_extract_utc(SYSTIMESTAMP) NOT NULL,
"LAST_MODIFIED" timestamp default sys_extract_utc(SYSTIMESTAMP) NOT NULL,
"VERSION" varchar2(255) NOT NULL,
"CUSTPROFILE" JSON,
PRIMARY KEY (ID),
)
PARTITION BY RANGE (ID)
(PARTITION p1 VALUES LESS THAN ('5000') TABLESPACE ts1,
PARTITION p2 VALUES LESS THAN ('10000') TABLESPACE ts2)

シャード表でのマップされたSODAコレクションの作成

マップされたSODAコレクションを作成して、シャード表を操作する際に使用する列をSODAに認識させます。

このタスクでは、最初にマップされたコレクションを作成するプロシージャを実行します。これにより、以前に作成した表をSODAコレクションとしてSODAで認識するために必要なメタデータが作成されます。

その後、追加のプロシージャsys.exec_shard_plsql()を実行して、すべてのシャードおよび今後のすべてのシャードでマップ・コレクションが作成されるようにします。

すべてのシャードでSODAマップ済コレクションの作成

Sharding/SODAユーザーとして、SHARD DDLを有効にして、次のコマンドをシャード・カタログで実行します。シャード・カタログは、自動的に処理されるように、すべてのシャードにプロシージャを伝播します。

GRANT SODA_APP TO PROCEDURE APP_SCHEMA.COLLECTION_PROC_CUSTOMERS;

create or replace procedure COLLECTION_PROC_CUSTOMERS AS 
METADATA varchar2(8000);
 COL SODA_COLLECTION_T;
 begin METADATA := '{"tableName":"CUSTOMERS",
 "keyColumn":{"name":"ID","assignmentMethod" : "CLIENT"},
 "contentColumn":{"name":"CUSTPROFILE","sqlType":"JSON"},
 "versionColumn":{"name":"VERSION","method":"UUID"},
 "lastModifiedColumn":{"name":"LAST_MODIFIED"},
 "creationTimeColumn":{"name":"CREATED_ON"},
 "readOnly":false}'; 
-- Create a collection using "map" mode, based on 
-- the table you've created above and specified in 
-- the custom metadata under "tableName" field. 
COL := dbms_soda.create_collection('CUSTOMERS',METADATA,DBMS_SODA.CREATE_MODE_MAP); 
end ; 
/ 

exec sys.exec_shard_plsql('app_schema.collection_proc_customers()',4+1);

keyColumnは、各ドキュメントの一意のIDを保持するIDとしてマップされます。アプリケーションで挿入時にドキュメントごとに一意キーを提供するため、ここではCLIENTとして指定されます。

この時点で、新しいコレクションが作成されています。

PL/SQLを実行してコレクションを表示できます。シャード・カタログで次のコマンドを実行し、次に示すようにCUSTOMERSコレクションが出力に表示されていることを確認します。

SET SERVEROUTPUT ON
DECLARE
l_coll_list SODA_COLLNAME_LIST_T;
BEGIN
l_coll_list := DBMS_SODA.list_collection_names;
 
IF l_coll_list.COUNT > 0 THEN
FOR i IN 1 .. l_coll_list.COUNT LOOP
DBMS_OUTPUT.put_line(i || ' : ' || l_coll_list(i));
END LOOP;
END IF;
END;
/
1 : CUSTOMERS
 
PL/SQL procedure successfully completed.

コード・サンプル

JavaおよびPythonの次のコード・サンプルでは、シャーディング・キーを使用してシャードに接続し、新しいドキュメントを挿入する方法を示しています。

分散データベース環境でSODAを使用する場合は、シャード・カタログを使用せずに特定のシャードに接続して新しいドキュメントを作成する必要があることに注意してください。

Javaコード・サンプル

これらのJavaコード・サンプルは、「シャーディング・キーとしてのSODA IDの使用」のユースケース用に作成されています。

次のJavaサンプルは、シャードに接続し、JSONドキュメントをコレクションに挿入する方法を示しています。

import java.sql.Connection;
import java.util.Properties;
import java.util.List;
 
// SODA specific imports
import oracle.soda.rdbms.OracleRDBMSClient;
import oracle.soda.OracleDatabase;
import oracle.soda.OracleCursor;
import oracle.soda.OracleCollection;
import oracle.soda.OracleDocument;
import oracle.soda.OracleException;
 
// Sharding and UCP imports
import oracle.jdbc.OracleShardingKey;
import oracle.jdbc.OracleType;
import oracle.jdbc.pool.OracleDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
 
 
/*
* The sample demonstrates connecting to a distributed database using
* Oracle JDBC driver and UCP as a client side connection pool.
*/
public class QuickInsertShard {
 
 public static void main(String args[]) throws Exception {
 
// TNS_ADMIN - Should be the path where the tnsnames.ora file resides
// dbshard_rw - It is the TNS alias present in tnsnames.ora.
// Note that the connection is to the Shard Director (GSM) and the service name is the shard RW service
final String DB_URL="jdbc:oracle:thin:@dbshard_rw?TNS_ADMIN=/home/opc/dbhome/";
 
// Update the Database Username and Password to the Shard User
final String DB_USER = "app_schema";
String DB_PASSWORD = "<user_password>" ;
 
 
// Get the PoolDataSource for UCP
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
 
// Set the connection factory first before all other properties
pds.setConnectionFactoryClassName(OracleDataSource.class.getName());
pds.setURL(DB_URL);
pds.setUser(DB_USER);
pds.setPassword(DB_PASSWORD);
pds.setConnectionPoolName("JDBC_UCP_POOL");
 
// Default is 0. Set the initial number of connections to be created
// when UCP is started.
pds.setInitialPoolSize(10);
// Default is 0. Set the minimum number of connections
// that is maintained by UCP at runtime.
pds.setMinPoolSize(10);
// Instead of Max Pool Size, we can set the number of max connections per shard
pds.setMaxConnectionsPerShard(20);
 
// We cannot get the connection until we have the Shard key which is part of the SQL
//We first set the sharding key or document id explicitly
String shardingKeyVal="10";
 
// Now we build the connection using this shard key
OracleShardingKey sdkey = pds.createShardingKeyBuilder().subkey(shardingKeyVal, OracleType.VARCHAR2).build();
System.out.println("Initiating UCP and Creating Connection...");
Connection conn = pds.createConnectionBuilder().shardingKey(sdkey).build();
 
// Enable the SODA Shared Metadata cache
Properties props = new Properties();
props.put("oracle.soda.sharedMetadataCache", "true");
OracleRDBMSClient cl = new OracleRDBMSClient(props);
 
// Get a DB Connection for use in SODA
OracleDatabase db = cl.getDatabase(conn);
 
// Print all the Collections in this DB
List<String> names =  db.admin().getCollectionNames();
for (String name : names)
 System.out.println ("Collection name: " + name);
 
// Open up the CUSTOMERS Collection
OracleCollection col = db.openCollection("CUSTOMERS");
 
//For a collection configured with client-assigned document keys,
//you must provide the key for the input document. Build a document with JSON.
OracleDocument cKeyDoc = db.createDocumentFromString(shardingKeyVal, "{\"name\": \"Matilda\", \"State\": \"CA\", \"ZIP\":\"94065\"}");
 
// Insert the document above
//If the key  already identifies a document in the collection
//then this will replace the existing doc.
OracleDocument savedDoc = col.saveAndGet(cKeyDoc);
 
// Get the document back assuming we only know the key
// We are still connected to the same shard
OracleDocument doc = col.find().key(shardingKeyVal).getOne();
String content = doc.getContentAsString();
System.out.println("Retrieved content is: " + content);
 
// We are done, so close the connection to the shard
conn.close();
 
// At this point we could open up a new shard connection using a different sharding key
 
 
 }} // End of QuickInsertShard

このJavaサンプルは、マルチシャード問合せの実行方法を示しています。

import java.sql.Connection;
import java.util.Properties;
import java.util.List;
 
// SODA specific imports
import oracle.soda.rdbms.OracleRDBMSClient;
import oracle.soda.OracleDatabase;
import oracle.soda.OracleCursor;
import oracle.soda.OracleCollection;
import oracle.soda.OracleDocument;
import oracle.soda.OracleException;
 
// Sharding and UCP imports
import oracle.jdbc.OracleShardingKey;
import oracle.jdbc.OracleType;
import oracle.jdbc.pool.OracleDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
 
 
/*
* The sample demonstrates connecting to a distributed database using
* Oracle JDBC driver and UCP as a client side connection pool.
*/
public class QuickQueryCat {
 
 public static void main(String args[]) throws Exception {
 
// TNS_ADMIN - Should be the path where the tnsnames.ora file resides
// dbshard_rw - It is the TNS alias present in tnsnames.ora.
// This connection is to the shard director using the catalog service name.
final String DB_URL="jdbc:oracle:thin:@dbcat?TNS_ADMIN=/home/opc/dbhome/";
 
// Update the Database Username and Password to the Shard User
final String DB_USER = "app_schema";
String DB_PASSWORD = "<user_password>" ;
 
 
// Get the PoolDataSource for UCP
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
 
// Set the connection factory first before all other properties
pds.setConnectionFactoryClassName(OracleDataSource.class.getName());
pds.setURL(DB_URL);
pds.setUser(DB_USER);
pds.setPassword(DB_PASSWORD);
pds.setConnectionPoolName("JDBC_UCP_POOL");
 
// Now we get a direct connection to the shard catalog
System.out.println("Initiating UCP and Creating Connection...");
Connection conn = pds.getConnection();
 
// Enable the SODA Shared Metadata cache
Properties props = new Properties();
props.put("oracle.soda.sharedMetadataCache", "true");
OracleRDBMSClient cl = new OracleRDBMSClient(props);
 
// Get a DB Connection
OracleDatabase db = cl.getDatabase(conn);
 
// Print all the Collections in this DB
List<String> names =  db.admin().getCollectionNames();
for (String name : names)
 System.out.println ("Collection name: " + name);
 
// Open up the CUSTOMERS Collection
OracleCollection col = db.openCollection("CUSTOMERS");
 
// Do a search across ALL Shards. In this case all users named Matilda
// Setup the specification and open a cursor
OracleDocument filterSpec = db.createDocumentFromString("{ \"name\" : \"Matilda\"}");
 
OracleCursor c = col.find().filter(filterSpec).getCursor();
 
// Print the results of the query
while (c.hasNext()) {
  OracleDocument resultDoc = c.next();
 
  // Print the document key and document content
  System.out.println ("Document key: " + resultDoc.getKey() + "\n" +
                        " document content: " + resultDoc.getContentAsString());
}
 
// Close the cursor
c.close();
 
// Here, we could initiate another multi-shard query if desired
 
// We are done, so close the connection
conn.close();
 
 
 }} // End of QuickQueryCat

Pythonコード・サンプル

このPythonサンプルは、分散データベース環境でSODAを使用してJSONオブジェクトを実際に操作する方法を示しています。

このサンプル・コードを環境で使用するには、Python用のcx_Oracleモジュールをインストールする手順に従います: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html

この例は、シャーディング・キーを使用してシャードに接続し、新しいドキュメントを挿入する方法を示しています。

分散データベース環境でSODAを使用する場合は、シャード・カタログを使用せずに特定のシャードに接続して新しいドキュメントを作成する必要があることに注意してください。

# import the cx_Oracle module for Python
import cx_Oracle

# Create a connection pool that will be used for connecting to all shards
# The components of the dsn are hostname (shard director),
# port (usually 1522), global service (created with GDSCTL)
# The pool is then created and SODA metadata caching is enabled.
dsn=cx_Oracle.makedsn("shard_director_host",1522,service_name="service_name")
pool=cx_Oracle.SessionPool("app_schema","password",dsn, soda_metadata_cache=True)

# Connect to a specific shard by using the sharding key, which in this example is
# set explicitly with "sodaid", but this might be passed in or part of a loop 
# You must know beforehand if you are creating or working with a document for a specific Customer
# 
sodaid="2468"
connection=pool.acquire(shardingkey=[sodaid])

# Set autocommit and open the CUSTOMERS collection
connection.autocommit = True
soda = connection.getSodaDatabase()
collection = soda.openCollection("CUSTOMERS")

# Insert a document
# Because you are specifying the shard key, you must pass that in with the document (key=custid)
# The value can be a UUID for example but it need not have any relation to the JSON Content.

content = {'name': 'Matilda', 'State': 'CA', 'ZIP':'94065'}
idcontent=soda.createDocument(content, key=sodaid)
doc = collection.insertOneAndGet(idcontent)

# Fetch the document back by key
doc = collection.find().key(sodaid).getOne()
content = doc.getContent()
print('Retrieved SODA document dictionary is:')
print(content)

# After you have finished, release this connection back into the pool
pool.release(connection)
  
# If you want to add or work with more customers, start with another connection
# For example: connection=pool.acquire(shardingkey=["123"]) and so on.
  
#When you are completely finished working with customers you can shut down the pool
pool.close()

このコード・サンプルは、マルチシャード問合せを実行して、名前がMで始まるすべての顧客名を返す方法を示しています。

import cx_Oracle
  
# Create an unpooled connection to the shard catalog
# In general, pooled connections should be used for all connections. This is shown here only as an example.
# The connect string connects to the shard director, but uses the catalog service, e.g. GD$catalog.oradbcloud
connection = cx_Oracle.connect("app_schema","password","db_connect_string")
  
# Open the CUSTOMERS collection
connection.autocommit = True
soda = connection.getSodaDatabase()
collection = soda.openCollection("CUSTOMERS")
  
# Now query the collection
# It is important to note that this is a query across ALL shards
# In other words, you will get ALL users whose names start with M
documents = collection.find().filter({'name': {'$like': 'M%'}}).getDocuments()
for d in documents:
    content = d.getContent()
    print(content["name"])
  
# Close the connection
connection.close()