がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

MySQLのmemcachedインターフェース(InnoDB Integration with memcached)を実験してみた:チューニング周りの備忘録

あんまりちゃんと調査してないんで、本気で備忘録程度。


とりあえず、memslapで簡単にベンチマーク確認しておきませう。

# 10,000回のテスト
memslap --servers=localhost --test set
memslap --servers=localhost --test get


以下のパラメタがチューニングと関連あるぽいのですが、全く以て未検証です。
後日、もし気が向いたらなにか記事書き足しますw

daemon_memcached_w_batch_size
daemon_memcached_r_batch_size
innodb_api_trx_level
innodb_api_disable_rowlock
innodb_api_enable_mdl
SELECT @@tx_isolation;

MySQLのmemcachedインターフェース(InnoDB Integration with memcached)を実験してみた:準備変

とりあえず、以下を先にやっておきましょう。

・必要なconfigの設定(SQL文が流れる)

cat ./plugin/innodb_memcached/innodb_memcache/scripts/innodb_memcached_config.sql | mysql -u root


プラグインの設定(SQL コンソールにて)

install plugin daemon_memcached soname "libmemcached.so";

mysql.plugin に情報が入る(から、以降、restartしても問題ない)


memcachedは認証が存在しないため、とりあえずiptablesでブロック(本番は、ロードバランサあたりでフィルタリング、が適切)
記述は、iptablesへの設定内容。

-A INPUT -p tcp -m state --state NEW -m tcp --dport 11211 -j DROP


PHPでmemcacheまたはmemecachedが使えるように設定
php.iniとかで適宜。
おいちゃんはこんな手順でやりました。適当に妄想補完してください。

wget https://launchpad.net/libmemcached/1.0/1.0.18/+download/libmemcached-1.0.18.tar.gz
tar zvxf libmemcached-1.0.18.tar.gz
cd libmemcached-1.0.18/
./configure
make
wget http://pecl.php.net/get/memcache-2.2.7.tgz
tar zvxf memcache-2.2.7.tgz
cd memcache-2.2.7
phpize
./configure
make -j 4

sudo cp modules/memcache.so /usr/local/lib/php/extensions/no-debug-non-zts-20121212/


php.ini

extension=memcache.so

MySQLのmemcachedインターフェース(InnoDB Integration with memcached)を実験してみた:概要

ほぼ完全に備忘録な上に「やった手順に沿って」の内容なんで、激しく長いです(苦笑


先に結論だけ書いておくと
・格納先テーブルは任意に選べる
・keyについては…
 *charとvarcharあたりが相性がいいんだけど、intもいける
 *varbinaryが相性悪くて、故に「'A' == 'a'」「'D1' == 'd1'」が成り立つ状況がちょっと怖い
 *以外なことにintがいける
 *datetimeとかはまぁ予想通りNG
 *keyの値のうち、スペース(0x20)はアンダースコアに自動変換される。ために、元々のデータに「スペースを含むkeyの値」がある場合、取得不可能になるので注意
 *アクセス用の名前(innodb_memcache.containers.name)と値とのセパレータに.(ドット)が用いられているため、keyカラム名にはドットを入れないようにする。値に混入した場合にどうなるかは未調査
valueについては…
 *単一カラムの場合、char、varchar、varbinary、text、blob、intが利用可能。datetime系は利用不可
 *intは使えるけど、戻ってくる値がstringになってるので、微妙に注意
 *実は「複数カラム」が可能。各カラムのデータは | によって仕切られる。仕切り文字の変更が可能かどうかは不明*1。また、エスケープなどが存在するかは不明。なので、データに「 | が含まれないこと」を保証する必要がありそう
 *valueを「複数カラム」指定することも可能だが、その場合、varbinaryがまざると使えなくなる
 *日本語は問題なく使用可能
 *テーブルを「CHARACTER SET 'binary'」で作ると使用不可になるため、「CHARACTER SET 'utf8mb4'」が無難なところ


あと、未検証だけどいけそうなのが
・多分、複合キーがいけるぽい雰囲気がある


先に雑感。
PKのカラムがintまたはvarcharのテーブルにおいて、いわゆるコードテーブル(マスターテーブル)のreadであれば、利用は比較的容易かもしれない(特に引っ張ってくるべき値が単一カラムの場合)。
それ以外でも「とりあえず見たいだけ」というような、単純なread用途であれば、適宜置き換えるのは、場合によっては有益かもしれない。

おおざっぱに基本

「準備( http://d.hatena.ne.jp/gallu/20140511/p2 )」をすると、innodb_memcache っていうdatabaseが出来るです。
で、ここに
cache_policies
config_options
containers
の3つのテーブルが出来ます。
cache_policiesとconfig_optionsについては、とりあえず「用途不明」。
中を見てるとなんか出来そうなんですが、config_optionsの中身を「軽く変えた」程度だと意図する挙動にはならなかったので、ここは「眺めておく」程度が無難。


んで。
ポイントはcontainersテーブル。
いくつかよく分からんカラムもあるんだけど。
基本的には「(db_schema)にある(db_table)ってテーブルの、(key_columns)をkeyに、(value_columns)をvalueに持ってる」程度の感じ。


テーブルは、一番分かりやすいのを一つ書いておくと、こんな感じ。

create table test1 (
  `key` varchar(256),
  `val` varchar(256),
  `flags` int,
  `cas_column` bigint UNSIGNED,
  `expire_time_column` int,
  PRIMARY KEY(`key`)
);

flags、cas_column、expire_time_columnは一端「おまじない」だと思って頂けると。


このテーブルが、furu_testっていうdatabaseに入っている、と仮定すると。
まず、containersテーブルに、情報をぶちこみます。

insert into innodb_memcache.containers 
   set name='test1', db_schema='furu_test', db_table='test1', key_columns='key', value_columns='val',
       flags='flags', cas_column='cas_column', expire_time_column='expire_time_column', unique_idx_name_on_key='PRIMARY';

nameは「識別用の名前」なんで、ある程度適当に(っても使うから、ある程度は考えて)。
db_schema、db_table、key_columns、value_columnsは前述の通り。
flags、cas_column、expire_time_column、unique_idx_name_on_keyは一端「おまじない」で覚えてください。


そうすると後は

<?php
// 接続
$m = new Memcache();
$r = $m->addServer('localhost', 11211);
// データの取得
$v = $m->get('@@test1.d1');
var_dump($v);
// データの設定
$r = $m->set('@@test1.d1', (string)mt_rand(0,1000));

こんな感じで使えます。
setとかgetとかaddとかの、keyの値を「'@@' + containers.nameの値 + '.' + keyの値」にするのがコツ。

*1:innodb_memcache.config_options が怪しいが、簡単にテストした程度だと、変更は出来なかった

あぁ2038年問題か

MySQLにデータを入れようとしたら


Incorrect datetime value


で怒られた。
…なんでだろ? って思ったら、timestamp型で、'2054-1-1 22:33:44'って値を入れようとしてた。
timestampって「TIMESTAMP データ型の範囲は、'1970-01-01 00:00:01' UTC から '2038-01-19 03:14:07' UTC です」だから、うん、確かにアウトだ(笑


ちっちゃい話なんだけど、意外と忘れそうなので、memo。

PDO関連備忘録

ちと確認したいことがあったので。
もっそメモ書きなので、適宜脳内保管しつつ読んでくださりませ。


とりあえず

set global long_query_time = 0;
set global slow_query_log = 'on';

して、MySQL側に渡っているものを覗いてみようかなぁとか画策。

create table test(
  a int,
  b varbinary(16),
  c blob,
  d DOUBLE,
  e datetime
);

という、大変にやる気のあふれたテーブルを作成w


以下

<?php
$o = new PDO('mysql:dbname=test;host=localhost', ユーザアカウント, ぱすわぁど);
$o->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

を前提に。

$sql = 'INSERT INTO test(a,b,c,d,e) VALUES(:a, :b, :c, :d, :e);';
$pp = $o->prepare($sql);

// intをintで
$pp->bindValue(':a', 1, PDO::PARAM_INT);
$pp->execute();

// intをSTRで
$pp->bindValue(':a', 2, PDO::PARAM_STR);
$pp->execute();

// intをLOBで
$pp->bindValue(':a', 3, PDO::PDO::PARAM_LOB);
$pp->execute();

ってテストしたら

Invalid parameter number: number of bound variables does not match number of tokens

って怒らりた。パラメタ数、結構まじめにチェックしてやがんの。てっきり「無指定はNULL」とかぬるめな事考えてたのに。


気を取り直して。

$sql = 'INSERT INTO test(a) VALUES(:a);';
$pp = $o->prepare($sql);

// intをintで
$pp->bindValue(':a', 1, PDO::PARAM_INT);
$pp->execute();

// intをSTRで
$pp->bindValue(':a', 2, PDO::PARAM_STR);
$pp->execute();

// intをLOBで
$pp->bindValue(':a', 3, PDO::PDO::PARAM_LOB);
$pp->execute();

あんまり取り直してない気もするけど気にしない。


結果。

INSERT INTO test(a) VALUES(1);
INSERT INTO test(a) VALUES('2');
INSERT INTO test(a) VALUES(3);

まぁ予想通り。

select * from test;

                                                                      • +
a b c d e
                                                                      • +
1 NULL NULL NULL NULL
2 NULL NULL NULL NULL
3 NULL NULL NULL NULL
                                                                      • +

こっちもまぁ。


お次。

$sql = 'INSERT INTO test(b) VALUES(:b);';
$pp = $o->prepare($sql);

// intをintで
$pp->bindValue(':b', 1, PDO::PARAM_INT);
$pp->execute();

// intをSTRで
$pp->bindValue(':b', 2, PDO::PARAM_STR);
$pp->execute();

// intをLOBで
$pp->bindValue(':b', 3, PDO::PARAM_LOB);
$pp->execute();

コメント直しミス。気にスンナ。

INSERT INTO test(b) VALUES(1);
INSERT INTO test(b) VALUES('2');
INSERT INTO test(b) VALUES(3);

こっちもおおむね。…PARAM_LOBがシングルクォートで囲ってくれてないのが、不思議っちゃぁ不思議。LOBって文字列の系譜ぢゃないのかしらん?

select * from test;

                                                                      • +
a b c d e
                                                                      • +
1 NULL NULL NULL NULL
2 NULL NULL NULL NULL
3 NULL NULL NULL NULL
NULL 1 NULL NULL NULL
NULL 2 NULL NULL NULL
NULL 3 NULL NULL NULL
                                                                      • +


お次は小数点。

$sql = 'INSERT INTO test(a, d) VALUES(:a, :d);';
$pp = $o->prepare($sql);

// intをintで
$pp->bindValue(':a', 1.1, PDO::PARAM_INT);
$pp->bindValue(':d', 1.1, PDO::PARAM_INT);
$pp->execute();

// intをSTRで
$pp->bindValue(':a', 2.2, PDO::PARAM_STR);
$pp->bindValue(':d', 2.2, PDO::PARAM_STR);
$pp->execute();

// intをLOBで
$pp->bindValue(':a', 3.3, PDO::PARAM_LOB);
$pp->bindValue(':d', 3.3, PDO::PARAM_LOB);
$pp->execute();

INSERT INTO test(a,d) VALUES(1.1, 1.1);
INSERT INTO test(a,d) VALUES('2.2', '2.2');
INSERT INTO test(a,d) VALUES(3.3, 3.3);

select * from test;

                                                                      • +
a b c d e
                                                                      • +
1 NULL NULL 1.1 NULL
2 NULL NULL 2.2 NULL
3 NULL NULL 3.3 NULL
                                                                      • +

そ〜だよね〜的な状況。


少しひねる。

$sql = 'INSERT INTO test(a) VALUES(:a);';
$pp = $o->prepare($sql);

// intをintで
$pp->bindValue(':a', 'a', PDO::PARAM_INT);
$pp->execute();

// intをSTRで
$pp->bindValue(':a', 'b', PDO::PARAM_STR);
$pp->execute();

// intをLOBで
$pp->bindValue(':a', 'c', PDO::PARAM_LOB);
$pp->execute();

エラーでるかしらん? むりしゃりintにするかしらん?

INSERT INTO test(a) VALUES('a');
INSERT INTO test(a) VALUES('b');
INSERT INTO test(a) VALUES('c');

…そ〜きたか。ある意味素直だ。

select * from test;

                                                                      • +
a b c d e
                                                                      • +
0 NULL NULL NULL NULL
0 NULL NULL NULL NULL
0 NULL NULL NULL NULL
                                                                      • +

まぁ値が0になるのは当然だらう。


お次は「無指定」のケース。気になるしねぇ。

// あと「無指定」の場合の挙動も確認
$sql = 'INSERT INTO test(a,b,c,d,e) VALUES(:a, :b, :c, :d, :e);';
$pp = $o->prepare($sql);

//
$pp->bindValue(':a', 1);
$pp->bindValue(':b', 2);
$pp->bindValue(':c', 3);
$pp->bindValue(':d', 4);
$pp->bindValue(':e', 5);
$pp->execute();

//
$pp->bindValue(':a', 'a');
$pp->bindValue(':b', 'b');
$pp->bindValue(':c', 'c');
$pp->bindValue(':d', 'd');
$pp->bindValue(':e', 'e');
$pp->execute();

INSERT INTO test(a,b,c,d,e) VALUES('1', '2', '3', '4', '5');
INSERT INTO test(a,b,c,d,e) VALUES('a', 'b', 'c', 'd', 'e');

どうも「文字列でふぉ」らしい。確かどこかにンなこと書いてあったよねぇ、的な。

                                                                                                    • +
a b c d e
                                                                                                    • +
1 2 3 4 0000-00-00 00:00:00
0 b c 0 0000-00-00 00:00:00
                                                                                                    • +


ほんのりとクラックっぽいことを。

$sql = 'INSERT INTO test(b) VALUES(:b);';
$pp = $o->prepare($sql);

//
$pp->bindValue(':b', "'", PDO::PARAM_INT);
$pp->execute();

//
$pp->bindValue(':b', '"', PDO::PARAM_INT);
$pp->execute();

//
$pp->bindValue(':b', '--', PDO::PARAM_INT);
$pp->execute();

INSERT INTO test(b) VALUES('\'');
INSERT INTO test(b) VALUES('\"');
INSERT INTO test(b) VALUES('--');
INSERT INTO test(b) VALUES(';');

まぁめげませんねぇ当然です。

select * from test;

                                                                                                    • +
a b c d e
                                                                                                    • +
NULL ' NULL NULL NULL
NULL " NULL NULL NULL
NULL -- NULL NULL NULL
NULL ; NULL NULL NULL
                                                                                                    • +


最後、今ひとつ不明なのが「PARAM_STMT」。
いくつか情報はチラ見したんだけど、今ひとつ。情報ある人いらっしゃったら教えてくださいませ ノ


さて…本格的にdata_clumpのほうで対応していかないとねぇこの辺。


追記
そういや「使える文字種別」ってどのくらいまでなんだろう?
ハイフン(-)とアンダースコア(_)、できればイコール(=)とアット(@)あたりくらいまでいけるといいんだけどなぁ。
そのうち実験してみませう。

MySQL5.5系インストールメモ

そういえば。cmake使うようになって、わずかながらに面倒だったので、メモ。
installしたのは…2月の頭ごろだったので。バージョンは適宜読み替えてちょ。

wget http://www.cmake.org/files/v2.8/cmake-2.8.7.tar.gz
tar zvxf cmake-2.8.7.tar.gz
cd cmake-2.8.7
./configure
make
sudo make install
cd ../


wget http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz
tar zvxf ncurses-5.9.tar.gz
cd ncurses-5.9
./configure
make
sudo make install
cd ../


wget http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.20.tar.gz/from/http://ftp.iij.ad.jp/pub/db/mysql/
tar zvxf mysql-5.5.20.tar.gz
cd mysql-5.5.20
vi ./mysql5_ccc
--
rm CMakeCache.txt
make clean
cmake \
  -DCMAKE_INSTALL_PREFIX=/opt/db/mysql-5.5.20 \
  -DDEFAULT_CHARSET=utf8 \
  -DDEFAULT_COLLATION=utf8_general_ci \
  -DWITH_EXTRA_CHARSETS=all \
#  -DWITH_PIC=1

--
sh ./mysql5_ccc
make
sudo make install
sudo ln -s /opt/db/mysql-5.5.20 /opt/db/mysql
cd ../

レプリケーション覚書

MySQLレプリケーション周り。
今度まとめるとして、参考になった数々のURIを列挙。
http://wiki.bit-hive.com/tomizoo/pg/MySQL%20%A5%EC%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A4%CE%C0%DF%C4%EA
http://www.irori.org/doc/mysql-rep.html
http://nippondanji.blogspot.com/2009/03/mysql10.html
http://wadslab.net/wiki/index.php?MySQL%20%A5%EC%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A4%CE%A5%BB%A5%C3%A5%C8%A5%A2%A5%C3%A5%D7%BC%EA%BD%E7
http://www.limy.org/program/db/mysql/use_replication.html
http://www.maruko2.com/mw/MySQL_%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%A8%AD%E5%AE%9A
「0から作る」のはそんなに面倒じゃないと思う。やり直しもきくし、せっかくだから色々試してみたいところw
「現在稼働中のサーバをレプリにする」場合は、しっかりと計画を練ってから。ちゃんと作業手順固めておけば、作業自体は10分程度だと思うので、検証と念のための蒔き戻しを想定しても、30分のサービス停止、で、どうにかなると思うんだけどなぁ。
どだろ?


あと、分散接続、LVSでもいいんだけど、興味あるんで「MySQL Proxy」つかってみた。
この辺が参考。
http://nippondanji.blogspot.com/2009/02/mysql-cluster.html
Proxy君、デフォルトのポートが4040(だったか4041だったか)なので。
「ふつ〜」に接続したいときは、起動時の引数をちょっとだけ注意。
あと、「--proxy-backend-addresses」を複数重ねたときは、手前のほうから順繰りに接続するぽ。なので、手前のほうに「比較的パワーのあるサーバ」をおいておくと、ほんの0.1mmほど幸せになれる、かも、しれない。

コンパイルは、こないだは、挫折した orz
今度、腰すえてコンパイルってみたい。


もういっこ。
若干特筆したいところ。
http://arinux.net/?eid=87

read_only # スレーブは read only にします

ん…今まで、アカウントの権限とかでやってたことが多かったんだけど。
my.cnfにこれ書けるんだと、すげぇ楽だなぁ、っておもた。


そのうち、なんかいろんな状況で叩いて、パターン作ってみたいんだけど。
まぁとりあえず一端、備忘録程度に。