DoqueDBではスキーマ情報を見るための特殊な組み込み表が用意されています。 以下に表定義と索引定義の組み込み表を示します。
-- 表定義の組み込み表 create table SYSTEM_TABLE ( RowID int, -- 表ID Name nvarchar(50), -- 表名 AreaID int array[7] -- 表を格納するエリアID ); -- 索引定義の組み込み表 create table SYSTEM_INDEX ( RowID int, -- 索引ID ParentID int, -- 索引が属する表のID Name nvarchar(50), -- 索引名 Type int, -- 索引の種別(1:B+木索引, 2:全文索引, 3:ビットマップ索引) Flag int, -- 索引のプロパティ(奇数:unique, 偶数:非unique) FileID int, -- 索引ファイルのID AreaID int, -- 索引を格納するエリアID HintString nvarchar(no limit) -- 索引のヒントに指定された文字列 );
また、組み込みデータベース「$$SystemDB」には、以下のようなデータベース定義の組み込み表が用意されています。
-- データベース定義(組み込みDB:$$SystemDBでのみ定義) create table SYSTEM_DATABASE ( RowID int, -- データベースID Name nvarchar(50), -- データベース名 Flag int, -- データベースのオプション Paths nvarchar(100) ARRAY[3] -- データベースパス([1]:データパス, [2]:論理ログパス, [3]:システムパス) );
これらの表は通常の表と同様にSELECT文の一部に使用できます。 更新操作は行えません。
以下は「TBL」で始まる名前を持つ表に属するすべての索引の名前を得るSQL文です。
select name from system_index where exists (select * from system_table where name like 'TBL%' and system_table.rowid = system_index.parentid);
全文索引のHINT句でstemming=trueと指定してください。
DoqueDBは、全文索引の有無に関わらずLIKE述語により文字列マッチを行うことができます。 構文は以下のようになっています。
select * from TBL where f like <パターン文字列> escape <エスケープ文字>;
エスケープ文字はパターン文字列中の特殊文字をその文字自体として処理させるために使います。 特殊文字とは以下のものです。
% 0文字以上の任意の文字にマッチ _ (下線) 1文字の任意の文字にマッチ エスケープ文字
これらの特殊文字の前にエスケープ文字を置くと、特殊文字がその文字自体として処理されます。
なお、シングルクオートをエスケープ文字の直後においてもエスケープされません。 通常の文字列リテラルと同様に、文字列中のシングルクオートは2つ重ねる必要があります。
escape以下を省略したときは、エスケープ文字はないものとして処理されます。 エスケープ文字には1文字であれば任意の文字を指定できます。 ただし、'%'や'_'をエスケープ文字にするとパターン文字列を書くときに混乱するので避けたほうがよいでしょう。
以下に例を書きます。
f like '100%' -- '100'、'1000'、'100%'、'100人力'などにマッチ f like '100_' -- '1000'、'100人'などにマッチ f like '100*%' -- '100*3=300'、'100*****'などにマッチ f like '100*%' escape '*' -- '100%'にのみマッチ f like '100**' -- '100**'にのみマッチ f like '100**' escape '*' -- '100*'にのみマッチ
端的に答えると「不明」です。 レコードの取得順が重要な場合はORDER BY句を使用してください。
ORDER BYがないときのレコードの取得順序は更新操作の手順やSELECT文の形により変わるため、内部の構造をある程度知らないと予測できません。 それでもどういう順序になるのか知りたい人のために、下記にいくつかの場合について列挙します。
SQL> create table TBL (f int); SQL> insert TBL values (1), (2), (3); SQL> select f from TBL; -- 登録した順になる {1} {2} {3} SQL> delete TBL; -- 登録した順に削除される SQL> insert TBL values (4), (5), (6); SQL> select f from TBL; -- 登録と逆順になる {6} {5} {4} SQL> insert TBL values (7), (8), (9); SQL> select f from TBL; -- さらに登録したぶんは登録順になる {6} {5} {4} {7} {8} {9}
SQL> create table TBL (f int, g int); SQL> create index BIDX on TBL(f); SQL> insert TBL values (1, 1), (2, 22), (1, 11), (2, 2); SQL> select f, g from TBL where f > 0; -- fの順になる。fが同じなら登録順 {1,1} {1,11} {2,22} {2,2}
SQL> create table TBL (f int, t nvarchar(100)); SQL> create fulltext index FIDX on TBL(t); SQL> insert TBL values (1, 'abc'), (2, 'aabbcc'), (1, 'cab'), (2, 'ccaabb'); SQL> select f, t from TBL where t like '%ab%'; -- 登録順になる {1,abc} {2,aabbcc} {1,cab} {2,ccaabb}
SQL> create table TBL (f int, t nvarchar(100)); SQL> create index BIDX on TBL(f); SQL> create fulltext index FIDX on TBL(t); SQL> insert TBL values (1, 'abc'), (2, 'aabbcc'), (1, 'cab'), (2, 'ccaabb'); SQL> select f, t from TBL where f > 0 and t like '%ab%'; -- 登録順になる {1,abc} {2,aabbcc} {1,cab} {2,ccaabb}ただし、SELECT句にある列がすべてひとつのBtree索引のキー列だった場合は、そのキーの昇順になります。 これは、SELECT句に対応した列の値をBtree索引から順次取得しながら、残りの索引と結果を照合するという処理になるからです。
SQL> create table TBL (f int, t nvarchar(100)); SQL> create index BIDX on TBL(f); SQL> create fulltext index FIDX on TBL(t); SQL> insert TBL values (1, 'abc'), (2, 'aabbcc'), (1, 'cab'), (2, 'ccaabb'); SQL> select f from TBL where f > 0 and t like '%ab%'; -- f順になる {1} {1} {2} {2}
SQL> create table TBL1 (f int, g int); SQL> create table TBL2 (h int, t nvarchar(100)); SQL> create index BIDX2 on TBL2(h); SQL> insert TBL1 values (1, 100), (3, 300), (2, 200); SQL> insert TBL2 values (1, 'abc'), (1, 'def'), (2, 'xyz'), (3, 'uvw'); SQL> select * from TBL1, TBL2 where f = h; -- TBL1→TBL2の順に取得される {1,100,1,abc} -- TBL1は条件がないので登録順 {1,100,1,def} -- TBL2はキーが同じなら登録順 {3,300,3,uvw} {2,200,2,xyz}
ORDER BYが指定されているときはもちろんORDER BYに指定された列の順で取得されますが、その列の値が同じものの間での並び順は不明です。
ORDER BYに文字列が指定された場合、Unicode順になります。 他の順序(たとえばShiftJIS順)にすることはできません。
ORDER BYによる並び替えで、NULLはもっとも小さい値として扱われます。
ちなみに、比較演算ではNULLとの比較はUnknownなので NULL < 10 はヒットしません。 並び替えでのみそのような扱いになります。
期待に反して十分な性能が出ないとしたら、いろいろな原因が考えられます。
まずはどのようなSQL文の発行にどのくらいの時間がかかっているのか調べてみましょう。
DoqueDBに渡されているSQL文とその実行にかかる時間は、パラメーター設定によりファイルに出力することができます。 <インストールパス>/etc/default.confに以下のキーを追加します。
Statement_KeepSQLStatement "true" Server_PrintSQLStatement "ファイル名" Server_PrintParameter "ファイル名" Server_PrintTime "ファイル名"
このように設定したのちにサーバーを起動すると、サーバーに対して投げられるすべてのSQL文と実行時間が、「ファイル名」で指定されたファイルに以下のように出力されます。
(872:2080) Server::worker.cpp 857: [DEBUG] SQL: select f from TBL where g >? (872:2080) Server::worker.cpp 883: [DEBUG] Parameter: {100} (872:2080) Server::worker.cpp 266: [DEBUG] Optimizer Time: 00:00:00.050 (872:2080) Server::worker.cpp 266: [DEBUG] Executor Time: 00:00:00.031 (872:2080) Server::worker.cpp 266: [DEBUG] Worker Time: 00:00:00.094
ログの見方は以下の通りです。
このようにして出力したログをチェックすることで、時間のかかるSQL文を特定することができます。 もし全体的に遅いのであれば環境や設定をチェックすることになります。
注意:上記の設定はオーバーヘッドになるので実運用では削除してください。
以降で、主な原因のパターンごとに少し詳しく述べていきます。
※128Mバイトにする場合: <インストールパス>/etc/default.conf に以下の行を追加 Buffer_NormalPoolSize 134217728 または Buffer_NormalPoolSize "128M"
マシン環境 サーバ: DELL PowerEdge2850 CPU: Xeon3.2GHz x 2 (Hyper Threading ON) RAM: 4096MB OS: Windows Server 2003 Standard Edition RAID: RAID5 (146G x 4) コントローラー: PERC 4e/DI メモリー: 256MB ストライプサイズ: 32KB Read Policy: Adaptive Read-Ahead | No Adeah Write Policy: Write-Back | Write-Through Cache Policy: Direct I/O | Cache I/O 10万件(平均20KB)のテキストデータ登録時間 Read Policy Write Policy Cache Policy 実行時間 うちWrite時間 うちRead時間 -------------------------------------------------------------------------------------------------------------------- Adaptive Read-Ahead Write-Back Cache I/O 2171秒 97秒 207秒 Adaptive Read-Ahead Write-Back Direct I/O 2098秒 101秒 227秒 No Ahead Write-Through Direct I/O 6091秒 4272秒 326秒
多くのセッションから並行して使用される表に索引をつけることで、競合を起きにくくすることができます。
たとえば、列Cがある条件を満たすレコードを削除する場合を考えます。 列 C に索引が付いていなければ、削除の処理は全レコードスキャン&条件判定という処理手順になります。 この処理手順のときは全レコードに対して1レコードずつ順番にロックがかけられ、条件判定(さらにTRUEなら削除)後、ロックが外されるという処理になります。 したがって、各セッションの列Cに対する条件が異なっていてもロックの競合が起こる可能性があります。
そこで、列Cに索引を定義すると、直接条件を満たすレコードのみが取得されるため、削除されるレコードだけがロックされることになり、競合が起きにくくなります。
ただし、スキーマ定義の説明にあるように、不要な索引は性能を低下させる恐れがありますので注意してください。
検討できる対策がいくつかあります。 以下個別に見ていきます。
START TRANSACTION READ WRITE, ISOLATION LEVEL SERIALIZABLE (挿入処理) COMMIT挿入する表に索引が付いているでしょうか? もし付いているのであれば、挿入前に索引をいった削除して、挿入後に再作成した方が速いかもしれません。 新規に挿入する件数が挿入後の全体の件数に対して占める割合が大きいときに有効です。
ASCII ... ModKanjiCode::unknown ShiftJIS ... ModKanjiCode::shiftJis UTF-8 ... ModKanjiCode::utf8
INSERT INTO T1 VALUES ('aaa', 10, 'zzz', ..., 100), ('bbb', 20, 'yyy', ..., 200), ... ('zzz', 270, 'aaa', ..., 2700);加えて、以下のようなSQL文をPrepareして使いまわすことでさらに速くなります。
INSERT INTO T1 VALUES (?,?, ...,?), (?,?, ...,?), ... (?,?, ...,?);たとえば1000件ずつ挿入する場合に、挿入する件数が1000で割り切れない場合は、余りの分だけ別のSQL文にするといいでしょう。
Trans_NoLogicalLog "true" Version_NoVersion "true"
どんな索引を使っているでしょうか?
もし索引に uni-gramを使っているのでしたら、変更した方がいいでしょう。 データサイズが大きくなるようなら性能がまったく出ないおそれもあります。 特別な理由がなければindexing=wordをお勧めします。
網羅性が気になるのでしたら、これにstemming=trueを付けるといいでしょう。
自動復旧についての3.1 Not availableにあるように、そのデータベースは一切の操作を受け付けない状態になっています。 しかし、そのデータベースは壊れておらず、データ保護のために利用制限しているだけの場合もあります。
たとえば、下記のようなログが事前に出ていた場合です。
[INFO] (Checkpoint) Database synchronization failed.
このログは、不正なメモリーを参照したために出力される場合があるので、利用制限しているだけかもしれません。 サービスを再起動してみてください。
ちなみに、この「不正なメモリーを参照」する原因ですが、データベース全体のデータとデータ操作のすべてのログがあれば、解明できる可能性は高くなります。 しかし、データ操作のログの量は下手をすると数百GBになるかもしれませんし、動作速度も低下します。
このログは、自動復旧についてにあるように、定期ジョブで実行されているので、通常は定期的に出力されます。 ただし、SQL文が実行されているあいだはこのログは出力されません。 したがって、このログが出ていないことから、SQL文の実行に時間がかかっていることは推測できますが、何らかの障害が起きていることを示せるわけではありません。
このログは、全文索引の小転置ファイルを大転置ファイルにマージする処理が起動したことを示しています。 これは、小転置の大きさがある程度大きくなった場合に起動されます。 完了するまでのあいだ、ほかの操作の割り込みは可能なので、状況によっては時間がかかります。 したがって、Endのログが出ないことから、全文索引の更新処理に非常に時間がかかっていることは推測できますが、何らかの障害が起きていることを示せるわけではありません。
しかし、たとえば6時間もマージ処理が実行しっぱなしということになると、通常では考えられないため、何らかの障害が起きている可能性があります。
DoqueDBには2つの例外クラスがあります。
ほとんどの場合、Exception::Objectの例外がスローされますが、メモリー確保に失敗した場合などには、下位ライブラリであるlibcommonからModExceptionがスローされる可能性があります。
オンラインバックアップとオフラインバックアップをサポートしています。 詳細はユーザーズマニュアルをご覧ください。 サービスを停止しているあいだにファイルを直接コピーすることも可能です。
サービス起動時の設定が使われます。
たとえばパラメータの設定とバックアップとを同時に実行したいときは、以下のような手順になります。
サロゲートペアは、16ビットに収まらないUnicode文字を16ビット2文字で表現する仕組みです。 たとえば上が「土」になった「吉」は、Unicodeではサロゲートペア文字になります。
DoqueDBでは、入出力の文字コードをUTF-8とすることで、 サロゲートペアを格納し、検索し、取り出すことができます。 ただし、サロゲートペアは内部的には2文字として扱われるため、 たとえばSUBSTRING関数でサロゲートペアの一方が切り出されることがあり、 またLIKE述語では連続する2個の下線(_)とマッチします。 サロゲートペアの断片は、UTF-8で出力するときに置換文字 (U+FFFD, UTF-8では0xEF 0xBF 0xBD)に変換されます。
Copyright (c) 2023 Ricoh Company, Ltd. All rights reserved.