[PGメモ]MySQL AUTO_INCREMENT の使用についてのエトセトラ。 | Late Riser

Late Riser

ダメ主婦ミルミルのプログラムと道の駅ドライブとリラックマの日々。
プログラム系は情報提供ではなく個人的メモなので、信憑性薄め。

本日の宿題

・たとえるなら同期通信の様に頻繁に更新があるシステム。
・積んでいく順番が重要(抜けは問題なし)。
・perl&MySQL(検証 version: 5.0.67-community-log MySQL Community Edition (GPL))です。
・スタックなのでInsertのみ行い、基本的にUPDATEはない。
・DBロックは最低限。
・複合キーである。
・要は下記のようなテーブルをマルチタスクで更新したい。

CREATE  TABLE `test_stack` (
`big_id` int unsigned NOT NULL DEFAULT 0 , #大カテゴリ
`mid_id` int unsigned NOT NULL DEFAULT 0 , #中カテゴリ
`min_id` int unsigned NOT NULL DEFAULT 0 , #小カテゴリ
`stack_id` bigint unsigned NOT NULL AUTO_INCREMENT, #+1で管理する
`user_id` int unsigned NOT NULL DEFAULT 0, #ユーザーID
`create_time` int unsigned NOT NULL DEFAULT 0 , #スタック時刻
PRIMARY KEY (`big_id`,`mid_id`, `min_id`, `stack_id`)
);


上記、少々命名センスがないのは気に留めるな。
ちなみにcreate_timeを小数点可能な8byte保存カラム(DOUBLE)にしておけば、
結構な時刻までのタイムスタンプ(マイクロ秒つき)を
保存できるんじゃねーの?と思って見たりもする。
numeric型とかnumeric型もいいかもね。使ったことないけど。



話を戻して、単純に衝突を回避する為ならば
PRIMARY KEY (`big_id`,`mid_id`, `min_id`, `user_id`,`create_time`)
とすれば、良いのではないかとも思われるが、
今回の場合他人のuser_idで更新することもあるので、その辺は残念ながら除外の方向で。

PRIMARY KEY (`stack_id`)で(`big_id`,`mid_id`, `min_id`, `stack_id`)に
index張るっていうのも、本来の意味を損ねるので不可。


個人的な希望は①が動くことが理想なわけである。
しかもトランザクション制御下で。

--------------------------------


ということで、一縷の望みをかけてこちらを流してみる。
mysql> CREATE  TABLE `test_stack` (
-> `big_id` int unsigned NOT NULL DEFAULT 0 , #大カテゴリ
-> `mid_id` int unsigned NOT NULL DEFAULT 0 , #中カテゴリ
-> `min_id` int unsigned NOT NULL DEFAULT 0 , #小カテゴリ
-> `stack_id` bigint unsigned NOT NULL AUTO_INCREMENT, #+1で管理する
-> `user_id` int unsigned NOT NULL DEFAULT 0, #ユーザーID
-> `create_time` int unsigned NOT NULL DEFAULT 0 , #スタック時刻
-> PRIMARY KEY (`big_id`,`mid_id`, `min_id`, `stack_id`)
-> ) ENGINE=InnoDB;

ERROR 1075 (42000): Incorrect table definition;
there can be only one auto column and it must be defined as a key

・・・ま、当然ですね。
InnoDBでは複合キーは利用できません。
あきらめましょう。


まぁ、今回はトランザクションが効かないので利用しないのが前提なのですが
一応MYISAMでも試してみます。
mysql> CREATE  TABLE `test_stack` (
-> `big_id` int unsigned NOT NULL DEFAULT 0 , #大カテゴリ
-> `mid_id` int unsigned NOT NULL DEFAULT 0 , #中カテゴリ
-> `min_id` int unsigned NOT NULL DEFAULT 0 , #小カテゴリ
-> `stack_id` bigint unsigned NOT NULL AUTO_INCREMENT, #+1で管理する
-> `user_id` int unsigned NOT NULL DEFAULT 0, #ユーザーID
-> `create_time` int unsigned NOT NULL DEFAULT 0 , #スタック時刻
-> PRIMARY KEY (`big_id`,`mid_id`, `min_id`, `stack_id`)
-> ) ENGINE=MYISAM;

Query OK, 0 rows affected (0.02 sec)

おぉ。
複合キーでも大丈夫★

トランザクションさえ使えれば完璧に想定通りなのに・・・


mysql> drop table test_stack;

ま、今回は使えないのでサヨナラします。
ということで、なんの情報元にもならない第一弾お疲れ様でした。
イントロダクションっつーことでwwww


参考までにエンジンを簡単にまとめると、

MYISAMは、トランザクションがなく、
InnoDBは、トランザクション制御に強い。

MYISAMは、テーブル単位のファイルによるデータ構造で
InnoDBは、テーブルスペース内に複数のテーブルやインデックスを格納するデータ構造

MYISAMは、AUTO_INCREMENTに複合キーが使える
InnoDBは、AUTO_INCREMENTに複合キーが使えない

↑の差についてはこちら(http://www.thinkit.co.jp/free/article/0608/1/2/)が詳しい。
ただし、データとしては少々古く、MySQL 5.0.22をベースに書かれている模様

マルチタスクにどっちを使うかといわれると言わずものがな・・・

ちなみにFalconなんてものもある。
↑はMySQLの新ストレージエンジン Falcon徹底リサーチ参照
http://journal.mycom.co.jp/special/2007/falcon/004.html
(InnoDBとの対比でInnoDBのトランザクションにもめっちゃくわしい)


ま、このあたりの簡単な検証は[●InnoDBとMYISAMのAUTO_INCREMENT値の検証]でやってみてます。


次ではAUTO_INCREMENTをつかわずAUTO_INCREMENTを再現させる方法をいくつか。

●Select~for updateによるID生成②
●シーケンスによるID生成③

結局、今の仕事では③を採用しました。
②はfor updateにより排他ロック範囲が広くなる為マルチタスク処理には不向きだったのですよ。

まぁ、③をやる場合にもシーケンステーブルはMYISAMにするなどして、トランザクション範囲の外で更新できないと
マルチタスクとしては意味を成さないことになっちまうと思います。


今回の仕事は色々勉強になった・・・(--;