PostgreSQL テーブル LOCK MODE 関係図

テーブルロックモードの関係を把握しやすいように図で表してみました。
PostgreSQL LOCK MODE 関係図
() 内はそのロックを自動的に獲得するクエリです。
 
■ロックとトランザクション
SQL における排他制御の方法として、トランザクションとロックが挙げられます。
複数のクエリをトランザクションでひとつにまとめることで、(デフォルトのトランザクションレベル READ COMMITTED の場合)コミット済みの結果のみ参照されるようになり、トランザクション中の内容を外部からは見えなくなるため一見アトミック性が守られるように見えます。
 
しかし、複数の接続で同時に既存の同じレコードに対して参照と更新を分けて実行する場合などはトランザクションだけでは限界があるのも事実です。
 
たとえば以下のテーブルを考えてみましょう。

CREATE TABLE foo (
  id int NOT NULL, — 商品の識別番号
  price int NOT NULL — 値段
);
INSERT INTO foo (id, price) VALUES(1, 100); — id:1 は 100 円
INSERT INTO foo (id, price) VALUES(2, 500); — id:2 は 500 円

このテーブルに対して、以下の処理を行うものとします。

BEGIN;
SELECT id FROM foo WHERE price < 500; -- 500円未満の id:1 を得る
UPDATE foo SET price = price + 500 WHERE id = 1; — 500円未満だった id:1 を 500円増し
COMMIT;

READ COMMITED レベルで同時に同じのクエリが実行されると、id:1 が 1100 円になる可能性があります。
 
トランザクションのみでこれを防ぐには衝突する可能性のある全てのクエリのトランザクションレベルを最も厳密な SERIALIZABLE にする必要があります。

— 衝突回避版
BEGIN;
SET TRANSACTION SERIALIZABLE; — この接続のトランザクションレベルを SERIALIZABLE に。
SELECT id FROM foo WHERE price < 500; -- 500円未満の id:1 を得る
UPDATE foo SET price = price + 500 WHERE id = 1; — 500円未満だった id:1 を 500円増し
COMMIT;

ただし、 SERIALIZABLE 隔離レベルのトランザクションの実行中に他の接続で COMMIT された場合、直列性を守るためにエラーになり処理が続行できなくなります。このため、衝突した場合は ROLLBACK の後再処理を行う必要があるなど, 欠点もあります。トランザクションのみでは、参照を完全にブロックすることができないのが原因のひとつです。
 
そこで、読み込みも制御できるロックの出番というわけです。
 
■ロックの獲得方法
多くのクエリは暗黙にクエリ単位で行レベルロックを獲得しています。
ロックの種類と影響する範囲については図を参照してください。
 
複数のクエリでロックを獲得するには LOCK 構文を用います。
LOCK 構文で取得できるロックはテーブルに対するロックのみです。

BEGIN;
LOCK foo_table IN EXCLUSIVE MODE; — EXCLUSIVE ロック獲得。
SELECT * FROM foo_table;
UPDATE foo_table SET …
— その他何か色々なクエリ…
COMMIT;

たとえば EXCLUSIVE モードでロックを獲得すると、他の接続では SELECT, ANALYZE 文以外の全てのクエリ、および ACCESS_SHARE 以外の全てのロックを明示的に獲得しようとするとロック待ちになります。
※具体的には UPDATE, DELETE, INSERT 文および SELECT … FROM table FOR …, CREATE INDEX, ALTER TABLE, DROP TABLE, REINDEX, CLUSTER, VACUUM, VACUUM FULL 文の実行を防ぎます。
また、ACCESS EXCLUSIVE, EXCLUSIVE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, SHARE の明示ロック獲得を防ぎます。
 
■ロックの解放方法
ロックはトランザクションの終了時(COMMIT, ROLLBACK実行時)に自動で解放されます。
トランザクションの途中でロックだけを解放する方法はありません。
 
参考:
トランザクションの隔離(PostgreSQL 8.3.1 マニュアル)
LOCK 構文(同上)
トランザクションと隔離レベルとロック(ファイヤープロジェクト)

複数ファイルの中身を一括置換するには

複数のファイルをまとめて置き換えたいときは sed を使うと便利です。
 
たとえばこのようにすると指定したディレクトリの中のすべての .html ファイルの内容を 2007年 から 2008 年に書き換えることができます。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  mv $i $i.bak
  sed -e ‘s/2007年/2008年/g’ $i.bak > $i
done

影響があったファイルの一覧を得るには次のようにします

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  diff -q $i.bak $i
done

影響があったファイルの内容を比較する場合も diff を使います。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  echo $i
  diff $i.bak $i
done

問題に気づいて元に戻したいときは *.bak を移動させれば元に戻ります。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  mv $i.bak $i
done

変更が問題なければ .bak ファイルを削除して置換完了です。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  rm $i.bak
done

参考:
sed の使い方

sed の使い方

書式:

sed -e ‘コマンド1’ [-e ‘コマンド2’ […]] [‘ファイル名’]

指定したファイル(省略した場合は標準入力)を入力テキストとして読み込み、
-e で指定したスクリプトを順番に実行して標準出力に書き出します。
 
-e で指定できる書式は次のようなものがあります。
 
■命令のみ指定するタイプ( -e 命令 ):

-e p

行バッファの内容を出力(Print)する。バッファの内容は操作しないため、-ep -ep のように複数指定すると指定回出力されます。バッファの消去には後述の d を使用します。また、全てのスクリプトを実行したあとにもバッファ内容が出力されます。

-e d

行バッファの内容を消去(Delete)する。通常、p 命令と組み合わせて利用します。
 
■正規表現パターン + 処理内容指定をするタイプ(-e /正規表現/命令):

-e /^foo/p

foo から始まる行を出力します。

-e ‘/boO$/d’

boO で終わる行をバッファから削除します。

-e ‘/[J][j]ohn/p’

John または john が含まれる行を出力します。
 
■正規表現による置換(-e s/正規表現/置換内容/オプション)

-e s/foo/boo/

各行バッファで最初に見つかった foo を boo に置換します。

-e ‘s/a/A/g’

バッファ中の全ての a を A に置換します。

-e s/john/JOHN/ig

バッファ中の全ての john(大文字小文字問わない)を大文字の JOHN に統一します。
 
例:

echo “Hallo, world” | sed -e s/a/e/

typo を訂正する(Hallo -> Hello)。

cat /var/log/httpd/access_log | sed -e ‘/GET/p’ -e ‘/^127\.0\.0\.1/p’ -ed

Apache のローカルからの GET アクセスを抽出する。

右クリック→新規作成からテキストファイルが消えた

XP の話。
検索してみると同じ症状の方が結構いたので解決方法のメモを残しておきます。
 
原因A: レジストリの HKEY_CLASSES_ROOT\.txt の ShellNew キーの内容が正しくない、もしくは不足している

HKEY_CLASSES_ROOT\.txt\ShellNew というキーがあり、NullFile という空の文字列値があるか?

ない場合は作成する。
 
原因B: レジストリの HKEY_CLASSES_ROOT\.txt の(既定)の値が正しくない
HKEY_CLASSES_ROOT\.txt の既定の値は txtfile です。
これ以外の値に書き換わっている場合(txt_auto_fileになっている、などの場合)は txtfile に変更することで解決する場合があります。
 
原因C: レジストリの HKEY_CLASSES_ROOT\.txt の PerceivedType キーの内容が正しくない、もしくは不足している

HKEY_CLASSES_ROOT\.txt\PerceivedType というキーがあり、text という文字列が設定されているか?

ない場合は作成する。
 
原因D: レジストリの HKEY_CLASSES_ROOT\Directory 以下が Windows Update で書き換わった
( http://pub.ne.jp/pchitorigoto/?entry_id=1145705 より。)

HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\New\

の(既定)の値が

{D969A300-E7FF-11d0-A93B-00A0C90F2719}

以外になっていると新規作成が正しく動作しない現象が発生するようです。
 
どうしても解決しない場合:
Windows XP なら、こちらのサイトから、「TXT File Association Fix」をダウンロードして実行するとレジストリキーを修復できます。
ただし、.txt ファイル関係の設定をリセットするためテキストファイルへの関連付け等が外れてしまいます。
参考:
New command missing in Windows Explorer(Microsoft サポートオンライン:英語)
Windowss XP File Association Fixes

tips – PuTTY でコマンドライン – サーバごとに接続設定を変える

PuTTY は SSH と互換のコマンドライン引数を持っているため、以下のようにしてコマンドラインからユーザ foo, ドメイン example.com でログインできます。

putty foo@example.com

 
環境変数 PATH に putty.exe (または puttyjp.exe)へのパスを通しておけば、Windows の「ファイル名を指定して実行(Windowsキー + Rキー)」で実行できるためかなり便利です。
 
GUIでのログインと同様、サーバごとに秘密鍵や文字コードの設定−保存(ストア)されたセッション−を切り替えてログインしたい場合は、

putty -load “セッション名”

とすると指定した設定(セッション)をコマンドラインから読み込んでログインすることができます。セッション名を user@hostname の書式で設定しておけば

putty -load foo@example.com

のように通常のログインに -load を加えるだけでよいので管理、利用が容易になります。
  
参考:
PuTTY でデフォルト設定を変更する(過去記事)
ssh -R の応用例(過去記事)
ssh を任意のポート、プロトコルの proxy にする(過去記事)

.htaccess で PHP の表示言語を切り替える

php_value, php_flag を使って自動文字コード変換をする tips です。
  
.htaccess:

# ソースの文字コード(SJIS, EUC-JP, UTF-8 など mb_convert_encoding()で指定する書式)
php_value mbstring.internal_encoding UTF-8
# ヘッダで表明する出力文字コード(Shift_JIS, EUC-JP, UTF-8 など Content-Type で指定する書式)
php_value default_charset Shift_JIS
# 変換に使う出力文字コード(mb_convert_encoding()で指定する書式)
php_value mbstring.http_output SJIS
# 変換文字コードが属する言語
php_value mbstring.language Japanese
# フォームなどの入力文字コードは自動判別させる
php_value mbstring.http_input auto
# 入力の自動変換を有効化(Off で無効化)
php_flag mbstring.encoding_translation On
# 出力の自動変換を有効化(空値で無効化)
php_value output_handler mb_output_handler

なお、動作には

1.CGI モードではなく PHP-CLIモードで動いていること
2. httpd.conf で AllowOverride Options または AllowOverride All など Options の変更ができるように設定されていること。

が必要です。


ちなみに余談ですが、同じことを .htaccess なしでやろうとした場合は次のようになります。

<?php
// 自動変換処理
mb_language(“Japanese”);
// ソース文字コード
mb_internal_encoding(“UTF-8”);
// 出力文字コード
mb_http_output(“SJIS”);
header(“Content-Type:text/html;charset=Shift_JIS”);
 
// mb_output_handler で出力をフィルタリング
ob_start(“mb_output_handler”);
register_shutdown_function(‘ob_end_flush’);
 
// 入力をフィルタリング
// (mb_convert_encoding($value, mb_internal_encoding(), “auto, UTF-8”);
// を使い、 $_REQUEST, $_GET, $_POST, $_COOKIE など入力変数を変換すればエミュレートできるが
// 長くなるので省略)
 
// —————————–
// あとは普通の処理
 
echo “はろーわーるど\n”;

参考:
AllowOverride ディレクティブ(Apache 2.2 リファレンス)
PHP 出力制御関数(php.net)

AllowOverride が効かない

<Directory ~ “^/path/to/(htdocs|secure)/”>
AllowOverride All
</Directory>

こういう設定で、 AllowOverride All してるのに .htaccess が無視されていておかしいなと首を傾げていたところ、マニュアルを見て納得。

AllowOverride ディレクティブ:
(中略)
<Directory> セクションでのみ使用可能
AllowOverride は正規表現無しの<Directory> セクションでのみ有効で、<Location> や <DirectoryMatch> や <Files> セクションでは無効です。

なーるほど…。
ただ複数書くのが面倒、っていう理由で正規表現形式にしていたので、<Directory> を複数に分けて解決。

トラブルシューティング – portsdb.rb で cross-thread violation on rb_gc()

ある日 portversion と portupgrade で ports をアップデートしようとしたところ、portsdb.rb で次のようなエラーが出るようになり portupgrade 関係のプログラムがまったく動作しなくなってしまった。

/usr/local/lib/ruby/site_ruby/1.8/portsdb.rb:116:[BUG] cross-thread violation on rb_gc()
ruby 1.8.6 (2007-09-24) [i386-freebsd7]
 
SIGABRT (Abort trap: 6)

ちなみに環境は FreeBSD 7.0 + ruby_1.8.6_p111_2,1。
ports から ruby, portupgrade 入れなおしても
ソースを辿ったところ, libpthread が有効になっている時のみこのエラーが出るようになっているということが判明したため、

# cd /usr/ports/lang/ruby18
# make config

で LIB_PTHREAD を無効化した上で make install しなおしたところ無事解決しました。

tips – 色々なプロトコルをネットワークドライブに使う

最近は便利な世の中になったもので、色々なストレージサービスが増えてきましたね。
エクスプローラの「ネットワークドライブの割り当て」機能を使うとネットワークごしのドライブでも “Z:\foo.txt” など仮想のドライブとして認識できるため、ファイルの編集、作成、削除など、ほとんどのアプリケーションでの操作がまるでローカルHDDのように扱えるようになります。そこで、色々なネットワークストレージをネットワークドライブ化できるソフトウェア/方法についてまとめてみました。
 


– Gmail をネットワークドライブ化
Gmail Drive shell extension
エクスプローラのシェル拡張として動作するようです。
日本語での解説は「臨機応変? – Gmailをネットワークドライブとして使ってみる」が参考になります。


– FTP をネットワークドライブ化
Novell NetDrive というフリーソフトを使うと FTP, WebDAV プロトコルが使えるサーバをネットワークドライブにできます。ライセンスを他社に譲渡し、WebDriveという有償の製品になったため、開発元の novell.com からはダウンロードできなくなっていますが、サーチエンジンで検索すると見つかります。一応うちにも置いておきます。
 
ミラー:netdrive.exe をダウンロード
 
なお、Windows Vista の場合は NetDrive が非対応のため WebDrive を使う必要があります。
ネットワークドライブではなく “ftp://username:password@hostname/path”の形式ならばエクスプローラ標準でフォルダのように仮想的に扱うことができますが、当然ですが、こちらはFTP対応していないアプリケーションからはアクセスできません。


– WebDAV をネットワークドライブ化
前述の NetDrive, WebDrive が WebDAV に対応しています。
また、Windows Vista の場合は標準で対応しているようです。


– Amazon S3(Simple Storage Service) をネットワークドライブ化
Amazon S3 は Amazon が提供する 1GB あたり 15セントで使えるネットワークストレージサービス。容量制限はありません。
 
Jungle Diskというシェアウェアでネットワークドライブ化できます。
また、前述の WebDrive も Amazon S3 に対応しているようです。


– SFTP,SCP, Frontpage server をネットワークドライブ化
前述の WebDrive がこれらに対応しています。


– Xdrive をネットワークドライブ化
AOL が提供している Xdrive というフリーのストレージサービス。
1アカウントあたり 5GB のストレージを無料で利用できます。
Downloads から Xdrive Desktop をダウンロードすればネットワークドライブ化できそう。


– Windows Live SkyDrive をネットワークドライブ化
Microsoft が提供する Windows Live SkyDrive というフリーのストレージサービス。
hotmail のアカウントごとに 5GB のストレージが無料で利用できます。
今のところは無理そう。