MySQLでマテリアライズドビュー3 実装編 即時反映(on commit)
まずは簡単な履歴系のテーブルから実体化してみましょう例としてユーザのテーブルと紐づいているコメント機能を提示します。
まずは例によってテーブル定義とデータ作成から
- ユーザテーブル
CREATE TABLE `user` ( `user_id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ユーザID', `user_name` VARCHAR( 255 ) NOT NULL COMMENT 'ユーザ名' ) ENGINE = MYISAM COMMENT = 'ユーザ';
- コメントテーブル
CREATE TABLE `comment` ( `comment_id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'コメントID' , `from_user_id` INT NOT NULL COMMENT '送信元ユーザID', `to_user_id` INT NOT NULL COMMENT '送信先ユーザID', `comment` VARCHAR( 255 ) NOT NULL COMMENT 'コメント' , INDEX ( `from_user_id` ),INDEX ( `to_user_id` ) ) ENGINE = MYISAM COMMENT = 'コメント';
- ユーザのデータを10万件用意します
call insert_generate_series('user(user_name)','md5(?)',1,100000,1);
- コメントのデータを100万件用意します
call insert_generate_series('comment(from_user_id,to_user_id,comment)','ceil(rand()*100000),ceil(rand()*100000),md5(?)',1,1000000,1);
まずは作成したデータを結合してデータを表示してみます。
SELECT SQL_NO_CACHE comment_id , fu.user_name form_user_name , tu.user_name to_user_name , comment FROM `comment` cm,user fu,user tu WHERE fu.user_id=cm.from_user_id AND tu.user_id=cm.to_user_id LIMIT 900000,30
30 rows in set (16.19 sec)
この手のテーブル構造は、ユーザが10万件、コメントが100万件を超えたあたりから性能が劣化してきます。Open Social系でウインク履歴、ガチャ履歴、あしあと履歴、twiter系のサービスで、つぶやき等膨大なレコードを扱う場合、さすがのMySQLでもJOINが厳しくなってきます。逆に中規模程度のサービスであれば対策は必要ではないです。また、マテリアライズ化する以外にJOINしない手法はいくつか存在します。
ビューを作成
CREATE OR REPLACE ALGORITHM = UNDEFINED VIEW `v_comment` AS SELECT comment_id, fu.user_id from_user_id , tu.user_id to_user_id , fu.user_name from_user_name , tu.user_name to_user_name , `comment` FROM `comment` cm, user fu, user tu WHERE fu.user_id = cm.from_user_id AND tu.user_id = cm.to_user_id
SELECT SQL_NO_CACHE comment_id , from_user_name , to_user_name , comment FROM `v_comment` LIMIT 900000,30;
30 rows in set (12.07 sec)
当然のことながら、ビューにしたところで速度は早くなりませんね
擬似マテリアライズドビューのテーブルを確保します。
CREATE TABLE mv_comment ( PRIMARY KEY( `comment_id` ), INDEX ( `from_user_id` ), INDEX ( `to_user_id` ) ) SELECT comment_id , fu.user_id from_user_id , tu.user_id to_user_id , fu.user_name from_user_name , tu.user_name to_user_name , comment FROM `comment` cm,user fu,user tu WHERE fu.user_id=cm.from_user_id AND tu.user_id=cm.to_user_id
- v_commentをmv_commentにしてアクセス
SELECT SQL_NO_CACHE comment_id , from_user_name , to_user_name , comment FROM `mv_comment` LIMIT 900000,30
30 rows in set (0.73 sec)
めちゃめちゃ早くなってます。JOINしてないんだもの、そりゃそうだ。
トリガーを張ります。
次にcommentに行が追加された場合にmv_commentにも追加されるようにトリガーを張ります。
DROP TRIGGER IF EXISTS comment_materialized_insert DELIMITER // CREATE TRIGGER comment_materialized_insert AFTER INSERT ON comment FOR EACH ROW BEGIN INSERT INTO mv_comment ( comment_id , from_user_id , to_user_id , from_user_name , to_user_name , comment ) SELECT NEW.comment_id , fu.user_id from_user_id , tu.user_id to_user_id , fu.user_name from_user_name , tu.user_name to_user_name , NEW.comment FROM user fu,user tu WHERE fu.user_id=NEW.from_user_id AND tu.user_id=NEW.to_user_id; END; // DELIMITER ;
以上で基本的なところは完了です。mv_commentで高速にアクセスできるようになりました。update、deleteトリガーも必要であれば適時張っておきます。