テーブルロックは最も基本的なロック戦略であり、OceanBaseデータベースは単一のテーブル、複数のテーブル、およびテーブルの複数のパーティションやサブパーティションに対するロックをサポートしています。
データベースは複数のユーザーが共有するリソースです。複数のユーザーが同時にデータにアクセスすると、データベース内では複数のトランザクションが同一のデータに同時にアクセスする状況が発生します。並行操作を制御しない場合、誤ったデータの読み書きが行われ、データベースの一貫性が損なわれる可能性があります。ロックは、データベースの並行制御を実現するための非常に重要な技術です。リソースの安全性(つまりデータの完全性と一貫性)を確保するために、ロック機構が導入されました。このロック機構を利用して、トランザクション間のデータへの並行アクセスとデータの一貫性を実現します。
テーブルをロックすると、ロックされたテーブルは、トランザクションをコミットするか、トランザクションをロールバック(テーブルがロックされる前のセーブポイントまでロールバック)するまでロック状態が維持されます。トランザクション制御文の詳細については、トランザクション制御文を参照してください。
テーブルロックモード
OceanBaseデータベースの現在のバージョンでサポートされているロックモードは以下のとおりです:
- ROW SHARE:ロックされたテーブルへの並行アクセスを許可しますが、他のユーザーによるテーブル全体のロックと排他的アクセス(つまり、テーブルに対する
EXCLUSIVEロック)を禁止します。 - ROW EXCLUSIVE:他のユーザーによる
SHARE以上のモード(SHARE,ROW SHARE EXCLUSIVE,EXCLUSIVE)でのテーブルロックを禁止します。更新、挿入、削除、またはSELECT FOR UPDATE操作を実行する際には、自動的にROW EXCLUSIVEロックが取得されます。 - SHARE:並行クエリを許可しますが、ロックされたテーブルの更新を禁止します。SHARE ロックは、テーブル内の行に対する更新(
UPDATE,DELETE,INSERT)およびSELECT FOR UPDATE操作を防ぐだけでなく、テーブルに対するより高レベルのロックも防ぎます。つまり、他のユーザーによるテーブルに対するSHARE ROW EXCLUSIVEおよびEXCLUSIVEロックを禁止します。 - SHARE ROW EXCLUSIVE:他のユーザーがテーブル内の行を参照することを許可しますが、行の更新(
UPDATE,DELETE,INSERT)やSELECT FOR UPDATEを使用したテーブル内行のクエリを禁止し、テーブルに対するROW SHAREモード以外のロックも禁止します。 - EXCLUSIVE:他のユーザーがロックされたテーブルに対してクエリを実行することのみを許可し、テーブルに対する任意のタイプのDMLステートメントの実行や任意のタイプのロックを禁止します。
各モードにおけるロックの競合関係は次の表のとおりです:
| 要求ロックモード | 現在のロックモード |
|||||
|---|---|---|---|---|---|---|
| ROW SHARE | ROW EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ||
| ROW SHARE | 競合なし | 競合なし | 競合なし | 競合なし | 競合 | |
| ROW EXCLUSIVE | 競合なし | 競合なし | 競合 | 競合 | 競合 | |
| SHARE | 競合なし | 競合 | 競合なし | 競合 | 競合 | |
| SHARE ROW EXCLUSIVE | 競合なし | 競合 | 競合 | 競合 | 競合 | |
| EXCLUSIVE | 競合 | 競合 | 競合 | 競合 | 競合 | |
権限要件
ロック対象のテーブルは、ユーザー自身のスキーマ内に存在するか、ユーザーは LOCK ANY TABLE システム権限を持っている必要があります。
テーブル全体のロック
テーブル全体をロックするSQLステートメントは次のとおりです:
LOCK TABLE [schema.]table_name[,[schema.]table_name ...] IN lockmode MODE [NOWAIT | WAIT integer];
ステートメントの使い方:
table_name:ロック対象のテーブル名を指定します。複数のテーブルを指定する場合は、半角カンマ(,)で区切ります。lockmode:テーブルロックモードを指定します。NOWAIT | WAIT integer:ロック競合が発生した場合の処理方法を指定します。NOWAITを指定した場合、ターゲットテーブルにロックをかけようとしてロック競合が発生すると、システムは直ちに制御権をユーザーに返し、エラーメッセージを返します。WAIT integerを指定した場合、ターゲットテーブルにロックをかけようとしてロック競合が発生すると、システムは競合するテーブルロックが解除されるまで待機します。ユーザーが設定したステートメント実行のタイムアウト時間を超えても競合するテーブルロックが解除されない場合、システムはエラーメッセージを返します。ここで、integerの単位は秒で、その値に制限はありません。注意
WAIT integerを指定した場合、ステートメント実行のタイムアウト時間はinteger、ob_query_timeout および ob_trx_timeout の値に依存し、これらのうちの最小値がステートメント実行の実際のタイムアウト時間となります。例えば、WAIT 10を指定し、ob_query_timeoutとob_trx_timeoutがデフォルト値の場合、ロック競合を待機するタイムアウト時間は 1000000 us、すなわち 1 秒となります。NOWAITとWAIT integerの両方を指定しない場合、ステートメント実行のタイムアウト時間は ob_query_timeout と ob_trx_timeout の最小値に依存します。
テーブル tbl1 に EXCLUSIVE ロックをかける例を以下に示します。
LOCK TABLE tbl1 IN EXCLUSIVE MODE NOWAIT;
この例では、テーブル tbl1 に EXCLUSIVE ロックをかけた後、他のユーザーはそのテーブルをクエリすることはできますが、そのテーブルに対して任意のタイプのDMLステートメントを実行したり、他の任意のタイプのロックをかけたりすることはできません。
テーブルのパーティションロック
テーブルのパーティションをロックするSQLステートメントは以下のとおりです:
LOCK TABLE
{
[ schema.]table_name
[ PARTITION '('partition_name ...')' | SUBPARTITION '(' subpartition_name ...')' ] ...
}
IN lockmode MODE
[ NOWAIT | WAIT integer] ;
ステートメントの使い方:
table_name:ロック対象のパーティションテーブル名を指定します。partition_name:ロック対象のパーティションの名前を指定します。複数のパーティションを同時にロックできます。パーティション名は半角カンマ(,)で区切ります。subpartition_name:ロック対象のサブパーティションの名前を指定します。複数のサブパーティションを同時にロックできます。サブパーティション名は半角カンマ(,)で区切ります。lockmode:テーブルロックモードを指定します。NOWAIT | WAIT integer:ロック競合が発生した場合の処理方法を指定します。NOWAITを指定した場合、ターゲットテーブルのロック中にロック競合が発生すると、システムは直ちに制御権をユーザーに返し、エラーメッセージを返します。WAIT integerを指定した場合、ターゲットテーブルのロック中にロック競合が発生すると、システムは競合するテーブルロックが解放されるのを待機します。ユーザーが設定したステートメント実行のタイムアウト時間を超えても競合するテーブルロックが解放されない場合、システムはエラーメッセージを返します。ここで、integerの単位は秒であり、その値に制限はありません。注意
WAIT integerを指定した場合、ステートメント実行のタイムアウト時間はinteger、ob_query_timeout および ob_trx_timeout の値によって決まり、これらのうちの最小値がステートメント実行の実際のタイムアウト時間となります。例えば、WAIT 10を指定し、かつob_query_timeout、ob_trx_timeoutがデフォルト値の場合、ロック競合を待機するタイムアウト時間は 1000000 us、すなわち 1 秒となります。NOWAITとWAIT integerの両方を指定しない場合、ステートメント実行のタイムアウト時間は ob_query_timeout と ob_trx_timeout の最小値に依存します。
以下に例を挙げて説明します。現在のデータベースに、テンプレート化されたサブパーティションテーブル tbl2 があると仮定します。そのテーブル作成ステートメントは以下のとおりです。
CREATE TABLE tbl2(col1 NUMBER, col2 NUMBER)
PARTITION BY RANGE (col1)
SUBPARTITION BY RANGE (col2)
SUBPARTITION TEMPLATE
(
SUBPARTITION sp0 VALUES LESS THAN (3),
SUBPARTITION sp1 VALUES LESS THAN (6),
SUBPARTITION sp2 VALUES LESS THAN (9)
)
(
PARTITION p0 VALUES LESS THAN (100),
PARTITION p1 VALUES LESS THAN (200),
PARTITION p2 VALUES LESS THAN (300)
);
パーティションテーブルを作成した後、テンプレート化されたサブパーティションテーブルのパーティション名付けルールに従って、作成されたサブパーティション名はそれぞれ p0ssp0、p0ssp1、p0ssp2 となります。パーティション名付けルールの詳細については、パーティションの概要を参照してください。
次に、このテーブルに対して以下のロック操作を行います:
テーブル
tbl2のパーティションp1に EXCLUSIVE ロックをかけます。LOCK TABLE tbl2 PARTITION (p1) IN EXCLUSIVE MODE NOWAIT;テーブル
tbl2のサブパーティションp1ssp1に EXCLUSIVE ロックをかけます。LOCK TABLE tbl2 SUBPARTITION (p1ssp1) IN EXCLUSIVE MODE WAIT 60;テーブル
tbl2のパーティションp1、p2およびサブパーティションp2ssp0、p2ssp1に SHARE ロックをかけます。LOCK TABLE tbl2 PARTITION (p1, p2), tbl2 SUBPARTITION (p2ssp0, p2ssp1) IN SHARE MODE;
テーブルロックの確認
テーブルにロックをかけた後、ビュー GV$OB_LOCKS および V$OB_LOCKS を使用して、現在ユーザーが各テーブルで保持または要求しているロックの状況を確認できます。
SELECT * FROM GV$OB_LOCKS;
クエリ結果の例。
+----------------+----------+-----------+----------+------+--------+------+-------+---------+-----------+-------+
| SVR_IP | SVR_PORT | TENANT_ID | TRANS_ID | TYPE | ID1 | ID2 | LMODE | REQUEST | CTIME | BLOCK |
+----------------+----------+-----------+----------+------+--------+------+-------+---------+-----------+-------+
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 500003 | NULL | RX | NONE | 462723786 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 500003 | NULL | RS | NONE | 6690958 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200005 | NULL | X | NONE | 462718854 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200006 | NULL | X | NONE | 462710583 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200007 | NULL | X | NONE | 462701111 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200005 | NULL | S | NONE | 6687863 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200006 | NULL | S | NONE | 6679696 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200007 | NULL | S | NONE | 6671395 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200008 | NULL | S | NONE | 6661725 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200009 | NULL | S | NONE | 6653562 | 0 |
| xx.xx.xx.xx | 2882 | 1004 | 1344711 | TM | 200010 | NULL | S | NONE | 6645387 | 0 |
+----------------+----------+-----------+----------+------+--------+------+-------+---------+-----------+-------+
11 rows in set
クエリ結果の一部のフィールドについては、次の表のとおりです。
フィールド名 |
説明 |
|---|---|
| SRV_IP | ロックを保持またはロックを要求しているOBServerノードのIPアドレス |
| SRV_PORT | ロックを保持またはロックを要求しているOBServerノードのポート番号 |
| TENANT_ID | ロックを保持またはロックを要求しているテナントのID |
| TRANS_ID | ロックを保持またはロックを要求しているトランザクションID |
| TYPE | ロックタイプ:
|
| ID1 | ロック識別子1:
|
| ID2 | ロック識別子2:
|
| LMODE | 現在保持しているロックのモード:
|
| REQUEST | 現在要求しているロックのモード:
|
| CTIME | ロックの保持または待機時間、単位はマイクロ秒 |
| BLOCK | 現在のトランザクションが要求するロックが他のトランザクションによって保持されているかどうかを示します:
|
詳細を見る
LOCK TABLES ステートメントの詳細については、LOCK TABLESを参照してください。