毎秒1000リクエスト を捌く超高速CMS「adiary」
2010/01/27(水)PostgreSQL関連メモ
あとで適当に追記。
共有メモリ設定
共有メモリの確認
# ipcs -m # cat /proc/sys/kernel/shmmax
共有メモリの変更
1G # echo 1099511627776 >/proc/sys/kernel/shmmax 1.5G # echo 1649267441664 >/proc/sys/kernel/shmmax
起動時に設定するには。
# /etc/sysctl.conf kernel.shmmax = 1649267441664
制約やカラムの操作
Ver8.3で確認。テーブル名、カラム名が変更されていても作成時のものとなるので注意。
制約の種類 | 制約名 |
---|---|
PRIMARY KEY | [table]_pkey |
UNIQUE | [table]_[col]_key |
REFERENCES | [table]_[col]_fkey |
ALTER TABLE table ADD CONSTRAINT table_[col]_fkey FOREIGN KEY ([col]) REFERENCES usr([ref_col]); ALTER TABLE table DROP CONSTRAINT table_[col]_key;
目的 | SQL |
---|---|
NOT NULLを付ける | ALTER TABLE table ALTER COLUMN [col] SET NOT NULL |
NOT NULLを外す | ALTER TABLE table ALTER COLUMN [col] DROP NOT NULL |
DEFAULTを付ける | ALTER TABLE table ALTER COLUMN [col] SET DEFAULT [value] |
DEFAULTを外す | ALTER TABLE table ALTER COLUMN [col] DROP DEFAULT |
カラム名を変える | ALTER TABLE table RENAME [old_col] TO [new_col] |
テーブル名を変える | ALTER TABLE table RENAME TO table2 |
2010/01/09(土)Perl/UTF8, 日本語「全角」→「半角」変換ルーチン
よくネットショップ等で買い物をすると
郵便番号は半角で入力してください。
とか言われてウザくないですか? こういうのを解消するためのルーチンです。
条件
- Perl 5.8以降
- 利用可能文字列はutf-8のみ(そうでない場合はutf8に変換して渡してください)
- WTFPL(PDS扱いでも可)。
ソースは必ずutf-8で保存してください。
日本語に混ざる全角英数等を半角にする
use utf8; use Encode (); sub utf8_zen2han { my $str = shift; my $flag = utf8::is_utf8($str); Encode::_utf8_on($str); $str =~ tr/ !”#$%&’()*+,-./0-9:;<=>?@A-Z[¥]^_`a-z{|}/ -}/; if (!$flag) { Encode::_utf8_off($str); } return $str; }
実行例。
(変換前)abcdefgさささ110-2244あいう##$” (変換後)abcdefgさささ110-2244あいう##$"
半角カタカナを全角カタカナにする
use utf8; use Encode (); my %hankana_map = ( 'ガ'=>'ガ','ギ'=>'ギ','グ'=>'グ','ゲ'=>'ゲ','ゴ'=>'ゴ', 'ザ'=>'ザ','ジ'=>'ジ','ズ'=>'ズ','ゼ'=>'ゼ','ゾ'=>'ゾ', 'ダ'=>'ダ','ヂ'=>'ヂ','ヅ'=>'ヅ','デ'=>'デ','ド'=>'ド', 'バ'=>'バ','ビ'=>'ビ','ブ'=>'ブ','ベ'=>'ベ','ボ'=>'ボ', 'パ'=>'パ','ピ'=>'ピ','プ'=>'プ','ペ'=>'ペ','ポ'=>'ポ', 'ヴ'=>'ヴ'); sub utf8_hankana2zen { my $str = shift; my $flag = utf8::is_utf8($$str); Encode::_utf8_on($$str); $str =~ s/(ガ|ギ|グ|ゲ|ゴ|ザ|ジ|ズ|ゼ|ゾ|ダ|ヂ|ヅ|デ|ド|バ|ビ|ブ|ベ|ボ|パ|ピ|プ|ペ|ポ|ヴ)/$hankana_map{$1}/g; $str =~ tr/。-゚/。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜/; if (!$flag) { Encode::_utf8_off($str); } return $str; }
Encode::JP::H2Z
今更使いませんが、EUC-JPの場合は標準モジュールでもできます。逆変換は Encode::JP::H2Z::z2h()。
use Encode (); use Encode::JP::H2Z (); sub eucjp_hankana2zen { my $str = shift; Encode::JP::H2Z::h2z(\$str); return $str; }
その他
バグ等あったらコメントください。
2009/09/04(金)MySQL/PostgreSQLでのシリアル値まとめ
adiaryではすべてのテーブルに pkey というシリアル値(PRIMARY KEY)を設定しています。その扱いについて。
特にMySQLで特別な加工をせず、安全にシリアル値を取得する方法。
PostgreSQLの場合
PostgreSQLではそのままserial型というものがあり、
CREATE TABLE test(pkey SERIAL PRIMARY KEY, x INT); INSERT INTO test(x) VALUES(10);
とすることで、pkeyをプライマリキーとして自動的に生成することができます。
PostgreSQLではシーケンス操作関数というものがあり、SERIAL型を定義すると自動的に作成されます。
例えば、現在の値を取得したければ
SELECT currval(pg_catalog.pg_get_serial_sequence('test', 'pkey'))
とします。pkeyに値を設定して INSERT したとき、シーケンス関数は自動的に再定義されないため次の関数で再設定する必要があります。
SELECT setval(pg_catalog.pg_get_serial_sequence('test', 'pkey'), (SELECT max(pkey) FROM test))
現在の値(最後に生成された値)を取得するには次のようにします。
SELECT currval(pg_catalog.pg_get_serial_sequence('test', 'pkey'), (SELECT max(pkey) FROM test))
同じセッション内でテーブル問わず最後に生成された値を取得する場合は次で済みます。
SELECT lastval()
安全に(他のセッションともかぶらない唯一無二な)次のシリアル値を取得するには、次のようにします。
SELECT nextval(pg_catalog.pg_get_serial_sequence('test', 'pkey'))
MySQLの場合
MySQLにも5.1以降(それより前から?)SERIAL型があります。
CREATE TABLE test(pkey SERIAL PRIMARY KEY, x INT); INSERT INTO test(x) VALUES(10);
これによりpkeyに自動的にシリアル値を設定することができます。
- SERIALは「BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE」のエイリアスです。
- AUTO_INCREMENTは1つのテーブルに1カラムしか指定できない。
- AUTO_INCREMENTを指定したカラムに、例えばpkeyを指定してINSERTしても、AUTO_INCREMENTがよきにはからってくれる(PostgreSQLのように値を再設定する必要なし)。
現在の値(最後に生成された値)を取得するには次のようにします。
SELECT LAST_INSERT_ID();
ただしC API等では mysql_insert_id()というものがあるため、こちらを使用するほうが効率が良いようです。例えばPerl DBIならば $sth->{mysql_insertid}。
追記。
SELECT * FROM test WHERE pkey IS NULL;
以前はこの方法でも取得が可能でしたが、いつの間にか使えなくなっているようなので注意しましょう。
値を取得、値の変更
SHOW TABLE STATUS WHERE NAME = 'test';
で得られたテーブル情報の中からAuto_incrementカラムの値を参照します。得られるのは次に挿入される値です。*1
何かの都合で値を設定するときは次のようにします。
ALTER TABLE test AUTO_INCREMENT=1;
安全に次の値を取得
MySQLでもっとも厄介なのはこれです。auto_incrementには安全に次の値を得る方法が提供されていません。別にシーケンステーブルを作る方法などがありますが、面倒です。
INSERTでしか得られないならINSERTしてしまえばいいやという単純な方法です。
INSERT INTO test() VALUES(); DELETE FROM test WHERE pkey=LAST_INSERT_ID(); SELECT LAST_INSERT_ID();
NOT NULL制約等が付いているとINSERTが失敗するので、CREATE TABLE等であらかじめ制約を満たすDEFAULT値を設定しておく必要があります。MyISAMではトランザクションが使えないため、NOT NULLかつUNIQUE制約が付いていると、複数のプロセスが同時に"INSERT INTO test() VALUES();"を実行したとき一方が失敗します。
MySQLの場合はINSERTしてUPDATEした方がスマートなのはたしかなのですが、PostgreSQLではシーケンス型の次の値を安全に取得できるし、INSERTに成功してUPDATEに失敗した場合を考えるとややこしいので。
2009/08/09(日)PostgreSQLトランザクションとDBD::Pgの謎の挙動
PostgreSQLのトランザクションの仕様
CREATE TABLE test(x INT UNIQUE); INSERT INTO test (x) VALUES (1); INSERT INTO test (x) VALUES (2);
としておきます。
ここで、
=> BEGIN; => INSERT INTO test (x) VALUES (3); INSERT 0 1 => INSERT INTO test (x) VALUES (1); ERROR: duplicate key value violates unique constraint "test_x_key"
とするとエラーになります。PostgreSQLはトランザクション中にエラーが起こると、以後何をしてもエラーになり受け付けなくなります。ためしに続けて色々発行してみても、
=> INSERT INTO test (x) VALUES (10); ERROR: current transaction is aborted, commands ignored until end of transaction block => INSERT INTO test (x) VALUES (20); ERROR: current transaction is aborted, commands ignored until end of transaction block
こんな感じです。COMMITすると
=> COMMIT; ROLLBACK
このようにROLLBACKされます。これがPostgreSQLの仕様です。
DBD::Pgの謎
- 確認環境
- PostgreSQL 8.3.7
- DBD::Pg Ver1.49
DBD::Pgの場合
01: $dbh->begin_work; 02: $dbh->do('INSERT INTO test (x) VALUES (10)'); 03: $dbh->do('INSERT INTO test (x) VALUES (1);'); 04: $dbh->do('INSERT INTO test (x) VALUES (20);'); 05: $dbh->commit;
とすると、3行目でエラーが起こり、4行目の実行でも"ERROR: current transaction is aborted, commands ignored until end of transaction block"といわれます。
これを、DBIのprepareを使用して
01: $dbh->begin_work; 02: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(10); 03: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(1); 04: $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(20); 05: $dbh->commit;
とすると、3行目でエラーが起っても、4行目の実行が反映されてしまいます。謎の挙動です。
謎の解析
このようにソースを改変してログを取ってみました。
open(my $fh, ">pg_log.txt"); $dbh->pg_server_trace($fh); $dbh->begin_work; $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(10); $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(1); $dbh->prepare('INSERT INTO test (x) VALUES (?)')->execute(20); $dbh->pg_server_untrace(); close($fh);
このときサーバに対して次のようなコマンドが発行されていました。
=> BEGIN; => INSERT INTO test (x) VALUES (10); => INSERT INTO test (x) VALUES (1); エラー : duplicate key value violates unique constraint "test_x_key" => ROLLBACK; => BEGIN; => INSERT INTO test (x) VALUES (20); => COMMIT;
どうりで最後のCOMMITが成功するはずです。取得した生ログも置いておきます。
これを、
01: $dbh->begin_work; 02: $dbh->prepare('INSERT INTO test (x) VALUES (10)')->execute(); 03: $dbh->prepare('INSERT INTO test (x) VALUES ( 1)')->execute(); 04: $dbh->prepare('INSERT INTO test (x) VALUES (20)')->execute(); 05: $dbh->commit;
とすると再現しません。prepare中にUNIQUE制約をチェックして、勝手にrollbackしているようですが、原因がPostgreSQL側なのかDBD::Pg側なのかは絞り込めませんでした。
追記
DBD::Pgの実装仕様みたいです。トラックバックを参考にしてください(iakioさんに感謝)。
DBD::Pgを直すとすれば、プレースホルダが破棄されてもトランザクションが終了するまでDEALLOCATEするのを待つ、くらいでしょうが。
明示的なトランザクション内でのエラーとDEALLOCATE
ROLLBACKしていいから、DEALLOCATE後に空のトランザクションを begin して、失敗させてくれればそれで十分な予感。
メモ
- どちらの場合も $dbh->commit(); の戻り値は "1" で成功している。
2007/12/01(土)IIS + ActivePerl で ImageMagick のインストール
ActivePerl に ImageMagick をインストールする
- ImageMagick公式サイトからWindowsバイナリ(-windows-dll.exe)をダウンロードしインストーラーを上の画面まで進める。
- インストーラーの画面から、対応するActivePerlのVersionとビルドナンバーを確認したら(この場合 Ver5.8.8 build 822)インストールせずに一度インストーラーを閉じる。
- 今確認したバージョンの ActivePerl インストールする
- ImageMagickを上記画面で「Update executable search path」「install PerlMagick」の2つにチェックを入れてインストールする。
これで ImageMagick がインストールされているはずです。コマンドラインで次のように入力すると手動で確認できます(エラーがでなければ正常です)。
c:\>perl -e "use Image::Magick"
IIS で ImageMagick を使う場合
IISで ImageMagick を使う CGI などを起動する場合は、更にWindowsを再起動します。
Image::Magick関連のDLLをロードさせるためにパス情報が設定されるのですが(環境変数PATH)、IISに新しいPATHを認識させるためには再起動しなければなりません。