SymmetricDSで日本語の使えない機能

SQL Server 2000とSQL Server 2012のデータ連係(SymmetricDS)で日本語を使えると書いたが、機能しない機能も見つかってきたので覚書。

同期条件の指定(Sync On ~ Condition)

SYM_TRIGGERテーブルのSYNC_ON_UPDATE_CONDITION、 SYNC_ON_INSERT_CONDITION、 SYNC_ON_DELETE_CONDITIONには日本語カラム名を含む条件式を指定できない。SYNC_ON_~_CONDITIONは同期対象のカラムを限定する制約条件を指定する事の出来るパラメーターだが、日本語を含むカラム名をしていすると、トリガーの登録時にエラーログにカラムが見つからない旨のエラーが記録される。
これはSymmetricDSのサンプルにおいても使われている機能です。サンプルも一部動かないので気を付けよう。

構造化データエラー「updatedがありません」への対処。

WordPressでテーマにTwentyElevenを使っているとき、Google Webマスターツールで構造化データエラー「updatedがありません」が発生した場合の対処法。

テーマ→テーマの編集の画面に移動し、テーマのための関数(function.php)を編集して、twentyeleven_posted_onにあるclass=”entry-date”をclass=”entry-date date updated”に書き換えて保存します。

変更前:

function twentyeleven_posted_on() {
    printf( __( '<span class="sep">Posted on </span><a href="%1$s" title="%2$s" rel="bookmark"><time class="entry-date" datetime="%3$s">%4$s</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="%5$s" title="%6$s" rel="author">%7$s</a></span></span>', 'twentyeleven' ),
        esc_url( get_permalink() ),
        esc_attr( get_the_time() ),
        esc_attr( get_the_date( 'c' ) ),
        esc_html( get_the_date() ),
        esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
        esc_attr( sprintf( __( 'View all posts by %s', 'twentyeleven' ), get_the_author() ) ),
        get_the_author()
    );
}

変更後:

function twentyeleven_posted_on() {
    printf( __( '<span class="sep">Posted on </span><a href="%1$s" title="%2$s" rel="bookmark"><time class="entry-date date updated" datetime="%3$s">%4$s</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="%5$s" title="%6$s" rel="author">%7$s</a></span></span>', 'twentyeleven' ),
        esc_url( get_permalink() ),
        esc_attr( get_the_time() ),
        esc_attr( get_the_date( 'c' ) ),
        esc_html( get_the_date() ),
        esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
        esc_attr( sprintf( __( 'View all posts by %s', 'twentyeleven' ), get_the_author() ) ),
        get_the_author()
    );
}

これで徐々に構造化エラーの件数が減っていくので、どうやらうまくいっているようです。

Threadを使用した処理の高速化

HadoopやGPGPU、FPGAなんかを使うほどじゃないけど、Many CoreのCPUを最大限に使うための考え方とか覚え書きしておく。

1.スレッドの構成

マルチスレッドで効率を高めるためには同じデータやデバイスへのアクセスが少なく、各スレッドの独立性が高いことが大事になる。もし独立性が低いと、同じデバイスやデータにアクセスすることによって、同期や排他、あるいはスヌーピング(CPUのキャッシュメモリの保持している内容と、メインメモリ上の内容が一致しなくなった時に、この同期をとるための動作)が発生して効率が上がらない。

マルチスレッドで効率良くデータを処理するためには、の設計パターンは3種類に集約される。

1.1.並列化(パラレル)

パラレル
同一の処理を行うスレッドを複数作成する事で、スレッドに処理を分散する。言語レベルでサポートされている場合もあり、対応するのは容易。その代わり処理内容によってはスレッドの独立性が低くなり、効率が上がりにくい。

1.2.直列化(パイプライン)

パイプライン
一連の処理を何段階かのステップに分けて、ステップごとにスレッドを作成する。手前の処理を行うスレッドから、次の処理を行うスレッドへとデータを受け渡すことで処理を行う。各ステップごとの処理にかかる時間が一様ではないため、効率が上がりにくい。実際には並列化とあわせて、ステップ毎の処理量の差を吸収させるようになる。

1.3.星型(スター)

スター
複数のデータ処理を行うスレッドと、データ処理を行うスレッドに対して処理要求を送るスレッドにて構成する。各スレッドのデータ処理量が一様で無くとも処理待ちになりにくく、スレッドの独立性も高いように設計しやすい。ただし処理速度が向上すると、処理要求を送るスレッドの処理能力がボトルネックとなるため、性能向上が頭打ちになる。

2.データの保持

HDDたデータベースなど低速なデバイスへのアクセスは大きなペナルティとなるので、可能な限りデータをメモリ上に保持する。メモリ上に保持したデータへの検索処理はハッシュテーブル等を使うとよい。最近はインメモリデータベースも多様な選択肢があるので検討する。

SQL Server 2000からSQL Server 2012への移行

レガシーマイグレーションというほどでは無いが、古いWindows 2003 ServerをWindows 2012 Serverに移行するにあたって覚え書き。

SQL Server 2000のデータベースをSQL Server 2012でマウントする事は出来ません。SQL SerSQL Server 2005のデータベースをSQL Server 2014でマウントする事は出来ません。したがってSQL Server 2000や2005から互換性がないSQL Serverにデータを移行するには、いったんSQL Server 2008を経由する必要があります。

SQL Serverのデータフォルダ(既定では%PROGRAM FILES%¥Microsoft SQL Server¥MSSQL¥Data)にあるMDFファイルとLDFファイルをSQL Server 2008の環境にコピーします。

SQL Server 2008上でコピーしたMDFファイルとLDFファイルをアタッチしてデータベースを起動します。

互換性レベルが80(SQL Server 2000)ないし90(SQL Server 2005)になっているので、100(SQL Server 2008)に変更します。これで先ほどアタッチしたMDFファイルとLDFファイルのバージョンが上がります。

SQL Server 2008上のMDFファイルとLDFファイルを、移行先となるSQL Server 2012またはSQL Server 2014にコピーしてアタッチします。

最終移行先となるSQL Server上で再び互換性レベルを変更します。110(SQL Server 2012)または120(SQL Server 2014)に変更します。

以上でデータの移行は完了。

念のために移行後のデータベースに「DBCC CHECKDB(~)」をかける。変換時に不整合が発生したり、互換性の無い機能が含まれていて問題になることもある。もしエラーになるなら、速やかに修正する。

データのインポートおよびエクスポートウィザードを使用する方法考えられるが、SQL Server Native Clientなどでは接続できないため、OLE DB Providerやフラットファイルソースを使用する事になる。この場合テーブル自体はインポートできるが、テーブルに付属するインデックス情報や制約情報、ビュー、ストアドプロシージャなどは取込むことが出来ない。テーブルデータ以外は別途SQL文として取得して、移行先のSQL Serverで実行することになる。手間を考えるとデータベースのバイナリファイルを直接移動する方が良いと思う。

SQL Server 2000とSQL Server 2012のデータ連係(SymmetricDS)

せめてSQL Server 2005とSQL Server 2012であればリプリケーションを使って、片側のデータベースに対する更新を、もう片方のデータベースに反映して同期を取ることも出来るのだけど・・・と、良い方法を探していたところSymmetricDS(http://www.symmetricds.org/)というリプリケーションツールを見つけた。

SymmetricDSはJava SE6以上が動作するOSで、JDBCで接続可能なデータベース間であれば、およそ殆どの異なるアーキテクチャ間のDBを双方向にリプリケーション出来る優れものです。SQL Server 2000でも動作するはずなので、今回の用途にはぴったりです。さっそく試験環境を作成してテストしてみましょう。

1.SymmetricDSのインストール

1.1.SymmetricDSのダウンロード

SymmetricDSのホームページからzipファイルをダウンロードして、インストール先に解凍します。ここではC:\App\symmetricに解凍することにします。

1.2.ルートノードの設定ファイルを作成する

データベースへの接続設定を作成します。データベースへの接続に関する設定はC:\App\symmetric\enginesに保存します。C:\App\symmetric\samplesからcorp-000.propertiesを複製して設定します。今回はrootnode.propertiesという名称にします。

engine.nameには任意の名称を指定します。今回はrootnodeとします。

db.driverにはJDBCドライバの名称を指定します。デフォルトではH2 Databaseが指定されているので、これをコメントにしてnet.sourceforge.jtds.jdbc.Driverのコメントを外します。

db.urlには同期するデータベースへの接続文字列を指定します。デフォルトではH2 Databaseが指定されているので、これをコメントにしてjdbc:jtds:sqlserver://localhost:1433/rootdb;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;に変更します。rootdbはデータベース名ですが、ここには日本語を指定できません。

db.userにはデータベースへのログインIDを指定します。本来はSymmetricDS用にアカウントを作るべきですが、今回はsaを指定しておきます。

db.passwordには先のログインIDで使用するパスワードを指定します。

sync.urlには同期に使用するHTTP URLを指定します。ここではhttp://localhost:31415/sync/rootnodeと指定します。sync移行に続く文字列はengine.nameで指定した文字列と一致しなくてはなりません。

group.idとexternal.idは、SymmetricDS上でデータベースを一位に識別するためのIDを指定します。ここではgroup.idをrootnode、external.idを000としておきます。

他にもパラメータがありますが、そのままにしておきます。

engine.name=rootnode

# The class name for the JDBC Driver
...#db.driver=org.h2.Driver
db.driver=net.sourceforge.jtds.jdbc.Driver

# The JDBC URL used to connect to the database
...
#db.url=jdbc:h2:corp;AUTO_SERVER=TRUE;LOCK_TIMEOUT=60000
db.url=jdbc:jtds:sqlserver://localhost:1433/rootdb;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;

# The user to login as who can create and update tables
db.user=sa

# The password for the user to login as
db.password=xxxxxx

registration.url=
sync.url=http://localhost:31415/sync/rootdb

# Do not change these for running the demo
group.id=rootnode
external.id=000

1.3.同期対象となるノードの設定ファイルを作成する

データベースへの接続設定を作成します。データベースへの接続に関する設定はC:\App\symmetric\enginesに保存します。C:\App\symmetric\samplesからstore-000.propertiesを複製して設定します。今回はsubnode-000.propertiesという名称にします。

engine.nameには任意の名称を指定します。今回はsubnode-001とします。

db.driverにはJDBCドライバの名称を指定します。ルートノードと同じくデフォルトではH2 Databaseが指定されているので、これをコメントにしてnet.sourceforge.jtds.jdbc.Driverのコメントを外します。

db.urlには同期するデータベースへの接続文字列を指定します。ルートノードと同じくデフォルトではH2 Databaseが指定されているので、これをコメントにしてjdbc:jtds:sqlserver://localhost:1433/subdb01;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;に変更します。

db.userにはデータベースへのログインIDを指定します。本来はSymmetricDS用にアカウントを作るべきですが、今回はsaを指定しておきます。

db.passwordには先のログインIDで使用するパスワードを指定します。

registration.urlにはルートノードsync.urlの値を指定します。ここではhttp://localhost:31415/sync/rootnodeと指定します。

group.idとexternal.idをルートノードと同様に設定します。ここではgroup.idをsubnode、external.idを001としておきます。

他にもパラメータがありますが、そのままにしておきます。

engine.name=subnode-001

# The class name for the JDBC Driver
...#db.driver=org.h2.Driver
db.driver=net.sourceforge.jtds.jdbc.Driver

# The JDBC URL used to connect to the database
...
#db.url=jdbc:h2:corp;AUTO_SERVER=TRUE;LOCK_TIMEOUT=60000
db.url=jdbc:jtds:sqlserver://localhost:1433/subdb01;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;

# The user to login as who can create and update tables
db.user=sa

# The password for the user to login as
db.password=xxxxxx

registration.url=http://localhost:31415/sync/rootdb

# Do not change these for running the demo
group.id=subnode
external.id=001

1.4.ルートノードに制御テーブルを作成する

次のコマンドを実行してルートノードのデータベースに制御テーブルを作成します。これによりデータベースにsym~で始める各種テーブルが作成されます。制御テーブルは別のデータベースに作りたいと思うかもしれませんが、ルートノードで指定したデータベース以外の場所に作ることはできないようです。

bin\symadmin --engine rootnode create-sym-tables

続いてデータベース間の同期方法に関する設定を、上記コマンドで作成されたテーブルにINSERTします。

--既存の設定を削除。sym_channelには既定のテーブルがあるので、作成したものだけ削除するようになっています。
delete from sym_trigger_router;
delete from sym_trigger;
delete from sym_router;
delete from sym_channel where channel_id in ('tblcopy');
delete from sym_node_group_link;
delete from sym_node_group;
delete from sym_node_host;
delete from sym_node_identity;
delete from sym_node_security;
delete from sym_node;

--テーブルのコピーに使用するチャンネルを作成。
--同期でエラーが発生すると、同じチャンネルを使用しているその他の複製処理もエラーが回復するまで待たされます。
--エラーが発生しても並列して複製処理を行いたい場合には、チャンネルを複数作成します。
insert into sym_channel (channel_id, processing_order, max_batch_size, enabled, description)
values('tblcopy', 1, 100000, 1, 'Copy Sample Table');

insert into sym_node_group (node_group_id) values ('rootnode');
insert into sym_node_group (node_group_id) values ('subnode');

insert into sym_node_group_link (source_node_group_id, target_node_group_id, data_event_action) values ('rootnode', 'subnode', 'W');
insert into sym_node_group_link (source_node_group_id, target_node_group_id, data_event_action) values ('subnode', 'rootnode', 'P');

--複製の対象テーブルや複製を開始する判断条件を指定します。
--ここではワイルドカードで全てのテーブルを複製するように指定して、それ以外の項目は規定値にしています。
insert into sym_trigger
(trigger_id,source_table_name,channel_id,last_update_time,create_time)
values('tblcopy','*','tblcopy',current_timestamp,current_timestamp);

--複製の方向の指定と、複製するデータのフィルタ条件を指定します。
--複製する条件はdefaultとして全てを複製するように指示しています。
insert into sym_router (router_id,source_node_group_id,target_node_group_id,router_type,create_time,last_update_time)
values('root_2_sub', 'rootnode', 'subnode', 'default',current_timestamp, current_timestamp);
insert into sym_router (router_id,source_node_group_id,target_node_group_id,router_type,create_time,last_update_time)
values('sub_2_root', 'subnode', 'rootnode', 'default',current_timestamp, current_timestamp);

--sym_triggerとsym_routerを関連付けています。
insert into sym_trigger_router
(trigger_id,router_id,initial_load_order,last_update_time,create_time)
values('tblcopy','root_2_sub', 100, current_timestamp, current_timestamp);

--ノードの登録を行います。
insert into sym_node (node_id,node_group_id,external_id,sync_enabled,sync_url,schema_version,symmetric_version,database_type,database_version,heartbeat_time,timezone_offset,batch_to_send_count,batch_in_error_count,created_at_node_id)
values ('000','rootnode','000',1,null,null,null,null,null,current_timestamp,null,0,0,'000');
insert into sym_node (node_id,node_group_id,external_id,sync_enabled,sync_url,schema_version,symmetric_version,database_type,database_version,heartbeat_time,timezone_offset,batch_to_send_count,batch_in_error_count,created_at_node_id)
values ('001','subnode','001',1,null,null,null,null,null,current_timestamp,null,0,0,'000');

insert into sym_node_security (node_id,node_password,registration_enabled,registration_time,initial_load_enabled,initial_load_time,created_at_node_id)
values ('000','5d1c92bbacbe2edb9e1ca5dbb0e481',0,current_timestamp,0,current_timestamp,'000');
insert into sym_node_security (node_id,node_password,registration_enabled,registration_time,initial_load_enabled,initial_load_time,created_at_node_id)
values ('001','5d1c92bbacbe2edb9e1ca5dbb0e481',1,null,1,null,'000');

--ルートノードのnode_idを指定します。ルートノードは1つだけ作成します。
insert into sym_node_identity values ('000');

1.5.SymmetricDSを起動する

次のコマンドを実行してSymmetricDSを起動します。

bin\sym

正常に起動した場合、sym_~で指定したテーブルや、rootdbにある同期対象のテーブルがsubdb01に複製されデータの同期が開始されます。

2.その他TIPSなど

2.1.日本語への対応

日本語で作成したテーブル名やフィールド名があっても正常に動作しているようです。
ただしデータベース名に日本語を使用する場合はdb.urlで指定することができないので、データベースで使用するユーザーアカウントの既定のデータベースでデータベース名を指定する等の対応が必要になります。

2.2.設定に誤りがあった場合の訂正

設定に誤りがあった場合、テーブルを直接書き換えても認識して動作が変わるのですが、各テーブルに作成された中間データとの整合性が取れなくなる場合があるようで、設定が反映されない場合があります。この場合は”bin/symadmin –engine uninstall”のコマンドを実行して制御テーブルを削除し、あらためて作り直した方がよいです。

2.3.Microsoft製JDBCドライバの使用

Microsoft製JDBCドライバでも問題なく動作しています。
ただしSymmetricDSの開発サイドではjtdsのドライバを使用して動作確認しているとの事なので、そちらを利用したほうがよいでしょう。