2012-07-19
date()が無ければleft()を使えばいいじゃない
mysql | |
こんなテーブルに
mysql> select hoge_datetime from hoge_log limit 1; +---------------------+ | hoge_datetime | +---------------------+ | 2012-07-01 10:00:00 | +---------------------+
mysql> select date(hoge_datetime) from hoge_log limit 1;
こう打ったら
ERROR 1064: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(hoge_datetime) from hoge_log limit 1' at line 1
怒られて何でやねんとなったけどよくよく考えれば自分が触っているのは mysql 4.0 だった。
DATE() は MySQL 4.1.1 以降で使用できる。
http://dev.mysql.com/doc//refman/4.1/ja/date-and-time-functions.html
仕方ないので
mysql> select left(hoge_datetime, 10) from hoge_log limit 1; +---------------------------+ | left(hoge_datetime, 10) | +---------------------------+ | 2012-07-05 | +---------------------------+
で取った。group by にも使えた。
2012-02-09
レプリケーションフローチャート
mysql40dumpを使おう。
始まり。
DBMSはmysql?
- いいえ
- 知るか。帰れ
- はい
- 下へ
mysqldのバージョンは4.0以上?
- いいえ(=3.x以下)
- mysql40dumpがサポートしてるかは知らんが、そんなもの使ってるお前は帰れ
- はい
- 下へ
テーブルのEngine(4.xならType)はInnoDB?
- はい
- mysql40dumpで何も問題ないだろ、帰れそしてさっさとやれ
- いいえ(=MyISAMが存在する)
- 下へ
dumpしたバックアップが存在するか?
- はい
- 帰ってそれ使ってさっさとやれ
- いいえ
- 下へ
レプリケーションしたいDBに、そもそも既にslaveが存在するか?
2012-01-18
create table して ERROR 1005 (HY000): Can't create table '$DB_NAME.$TABLE_NAME' (errno: -1) と言われたら .ibd ファイルが何故か存在しているから消せばいい
mysql | |
タイトル読んでお疲れさまでした。以上。
メモ
innodb_file_per_table の設定をしておくと、テーブルを作ると .ibd ファイルが作られる。
drop table すればこのファイルも消える。
テーブルを作るとき、既存テーブルと同名だった場合は分かりやすいエラーが出る。
mysql> create database foo; Query OK, 1 row affected (0.11 sec) mysql> use foo Database changed mysql> create table bar(a int) engine=innodb; Query OK, 0 rows affected (0.01 sec) mysql> create table bar(a int) engine=innodb; ERROR 1050 (42S01): Table 'bar' already exists
が、何故かテーブルは存在しないが .ibd ファイルは存在するという不思議な環境のもとでは、以下のように言われる。
mysql> create table bar(a int) engine=innodb; ERROR 1005 (HY000): Can't create table 'foo.bar' (errno: -1)
おとなしく .ibd を消せばいい。というかそんな状況になってる時点で何かがおかしい。
# ls ~mysql/foo/bar.ibd /var/lib/mysql/foo/bar.ibd # rm ~mysql/foo/bar.ibd
という事を会社でしでかして解決してもらうなどしていました。
手間と迷惑をおかけしております。
2011-09-23
3000req / sec と戦う
- ざっくり概要
- ピークで3000req / sec
- 毎分コンテンツ更新要求
- コンテンツ更新の際は他所からデータをapi経由で受け取る
- コンテンツ更新にはTheSchwartzを使用
なコンテンツを色々してきたログ。
尚、ここに書く技術は大半が周囲のギークな方々にサポートしてもらったもので、僕自身が何かしたわけではない。残念すぎる。
構成
internet -> www(squid -> apache) -> app(memcached -> app) -> db
- フロントエンド
wwwサーバがapacheとsquidを動かしている。apacheがリクエストを受け、squidのキャッシュが有ればそれを返し、無ければバックエンドのappサーバへproxy。
- バックエンド
appサーバがmemcachedとアプリを動かしている。
それぞれ冗長化してるけど、リクエスト数の割に台数は少ない。
技術があれば台数なんて少なくて済むと学習。
apache
- 何はともあれkeep alive off
画像配信サーバでもない限り、これはoffが望ましい。
- mod_deflate でコンテンツ圧縮
転送量を抑える。
- メリット
- 転送量が減る
- デメリット
- サーバのcpu負荷が上がる
今回はメリットの方が勝ったので導入。一気に減った。
squid
そもそもは各wwwのsquid同士がsiblingしていたが、これをやめた。効率悪かった。
siblingしたら、自分以外のサーバのキャッシュも使えるけど、いちいち通信するのが難。
localhostのsquidしか見ないようにした。
app
ギークに色々教わりつつ、ロジック変えたり、memcachedに突っ込む個所を増やしたり。
nginx
あかんapacheでは捌き切れん。nginxならどうだ。
というわけでapacheやめてnginx導入。
400 bad request
nginxのログに 400 bad request なログが延々溜まる。
ぐぐってこちらを発見。
http://limilic.com/entry/930zlf7u95d876yg
パッチを適用。上記のurlはnginxのバージョンが古く、現在のheadに当てるには
if ( error != NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE ) {
を
if ( rc != NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE ) {
に。パッチ適用したところ、直る。
重複enqueue
TheSchwartzにenqueueする際に、今まで同じコンテンツの更新要求を重複して投げてしまっていたので、やめる。
- memcachedを使用
- enqueue前にmemcachedにunique keyをaddする
- expireは適当に5分とか。5分もありゃ処理されてんだろ、と。
- 次のenqueue時にaddが失敗したら、既にenqueueされてるものと見なし、enqueue中止。
これで大体よかったんだけど、もっとちゃんとする。
- TheSchwartz自体にそういう機能あるじゃん
- enqueue時に uniqkey ってカラムにunique keyを指定
これだけ。
ついでに。
TheSchwartzの仕様では、enqueueする際には select ... limit OFFSET NUMBER なクエリを発行する。
とても無駄です。
enqueue減らすのは大事。
更新処理
これはこちら側の話ではないけれど。
これまで更新要求の処理は、
- 他所にリクエストを投げる
- 待つ。待つ。
- jsonで結果を受け取る
だった。で、このリクエストも思い。そんだけの処理をしているので(当然キャッシュ機能はある)
相談したところ、何とコールバック形式のapiを用意してくれた。凄い凄い。
それに合わせてこちらも非同期でコンテンツ更新を行うように。
LWPからFurlに
apiを叩くときはLWPを使っていた。
駄目だもうLWPじゃ遅い!となったのでしれっとFurlに。
改めて、どんだけ過酷なんだこのアプリは…と思い知る。
worker調整
Parallel::Prefork を使ってworkerを作っていた。で、
- max_workers をとても減らした
- termするまでworkする回数をとても増やした
- apacheでいう MaxRequestsPerChild
つまり一度生成したプロセスは非常に多い回数jobをこなしてから死ぬ。
あまり頻繁にforkしてプロセス再生成してたら負荷になるし、今回はそんな必要もないので。
今回のように延々とjobが溜まるような場合は極力forkを避けるべし。
thundering herd対策
普段は安定してるのにたまに急激に負荷が上がる。色々コード見たり考えたりした結果
俺「フロントのキャッシュが切れて、バックに行って、キャッシュ作ってフロントに返してキャッシュされる前に、同じ様にバックにリクエスト沢山行ってんじゃね?」
多分これ。そしてこれを thundering herd と言う(ことを教わった)
で。
- バックエンド(memcached)のキャッシュ時間 > フロントエンド(squid)のキャッシュ時間 にした
- バックエンドのbacklogを減らした
- チューニングでは増やすことの多い値だけど、今回のケースでは多いと死ぬ
- やばいときは割り切ってリクエスト絞ってフロントのキャッシュを生成するのを最優先する
- そうでもしないと続々同じようにバックに行くリクエストが増える。落ちっぱなしより、ちょっと落として元に戻す方がずっといい
squid 罠
あるデプロイの後からsquidのメモリ使用量がおかしい。設定値以上に使ってる。
具体的には cache_mem で指定した量より、topで見て計算したsquidのメモリ使用量の方が多い。
どんどん増える。最終的にはfreeが50M切った。ギリギリだった。
原因多分これ。
というわけでギークな方がさくっとパッチ当ててrpm作ってアクセスの少ない時間帯に入れ替えという神対応をやってのけた。
ジェバンニと呼んでいいですか。
204 No Content
で、そもそも何でsquidがそうなったのかというと。原因となったデプロイで行った変更が、
- ある非同期なリクエストをFurlで行った際のレスポンスを 204 にして、contentは空文字列を返すようにした
だった。どうやらsquidさんがこれをキャッシュしてしまった様子。
200にして小さなcontent返すように変更。
mysql 5.5
リクエストの度にログをDBに非同期で取っていて、そのデータは用が済んだらdeleteしている。のだが。
deleteが遅い!遅すぎる!別に1日分溜めて早朝に一気にdeleteでもいいんだけどもっとさくっとしれっとやりたい!
そうだpartitioningしてpartitionごと消せば早いんじゃね!(ピコーン)
5.1でもできるけどどうせなら5.5使おうぜ人柱的な意味で!
すいません動機としては新しいもの使ってみたかったのが大きいです。
partitioning
http://nippondanji.blogspot.com/2010/12/mysql-55.html とか参考にしつつRANGE COLUMNSでパーティション作成。
今月分作成、試しに来月分作成…何かエラー。
- 原因: partition pmax values less than (maxvalue) 的なのを作っていたから
partitionの範囲が固定ならともかく、日毎のログみたいに際限なく増えていくならこのmaxvalueは作ってはいけない。
そして最初に作成するときは普通にcreate文でいいけど追加するときは ALTER TABLE table_name ADD PARTITION で。
utf8mb4
mysql 5.5では utf8mb4 とか使えるそうなのでせっかくだから使っとこうか。別に困ってはいないけど。
準同期レプリケーション / Semi-Synchronous
というのも使えるそうなので使ってみた。
- master
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; SET GLOBAL rpl_semi_sync_master_enabled = 1;
- slave
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; SET GLOBAL rpl_semi_sync_slave_enabled = 1;
が。
「それは多少遅くなるから、今回みたいに沢山アクセスがあってしかもすぐに応答を返さなきゃいけない場合には向かないと思うよ、課金とかなら向いてるかなあ」
すいませんメリットばっかり見てデメリット考えてすらいませんでした。
というわけで無効に。
- master
SET GLOBAL rpl_semi_sync_master_enabled = 0;
- slave
SET GLOBAL rpl_semi_sync_slave_enabled = 0;
そうそう5.5用のmy.cnf書いたのもジェバンニさんです。
この辺から、このサイト運用に直接は関わらないけど教わったり学んだこと。
クエリパラメータにバイナリデータ
を送りつける攻撃手法があるそうで。
どうなるかというと、もしサーバ管理者がroot権限で tail -f してたら任意のコマンドを実行され以下略。
怖い怖い。
静的コンテンツのキャッシュ
サイト上に置いたcssやらjsやらをクライアントにクライアント側のキャッシュを無視して強制的に更新させたいら、サーバ側で適当に hoge.css?123 とか、適当なクエリをつければいい。
が、「このタグ貼って使ってね!」な場合にはこれが使えない。
もしそういうことをするなら、cssやらjsを変更する場合は、互換性を維持する事。
世界中のクライアントからキャッシュが消えたと判断できたなら維持しなくてもいい。
つまりもしそういうことするならちゃんと考えましょうねという話。
今回ちょっとjsファイルの仕様を変えたけど、1週間くらいは、キャッシュされたjsが使われたログがあった。案外長いものです。
swapが溜まる
偉い人が手を付けたサーバでは発生しなかったswapが、僕があれこれいじったサーバでは発生する。
どう見ても僕が原因です本当に申し訳ありません。
とはいえ、そこまで気にする程でもないらしい。
vmstat 1 を実行して、si, so (swap in/out)が多く出てなければ大丈夫かと http://memo.officebrook.net/20080418.html これを 0 にするというチューニングもありますが..
と。別に目の敵にするほどではないそうな。
swapをクリアしたいなら
swapoff -a && swapon -a
で。関係有るか分からんけど、もしかして原因は /dev/shm を使っていたから?
でかめのarchiveを操作するのにディスク上でやりたくねーなーと思って、ここを使ってそのままにしていた。
試しに使ったファイル全部rmったらswapが6Mに減少。
いやだってtmpfs使ったらはえーんだもの…
damontools
これまでdaemontoolsを使うときはこうしていた。
exec setuidgid hirafoo \ start_server --port $port -- \ plackup -a server.psgi -s Starman --port $port --workers 1 2>&1
が。
それだとサーバ落とさずにworker数変えられなくね start_serverにはshell scriptを渡して、その中で設定べた書きにするといいよ
と教わった。一つ賢くなった。
start_server
書き換える最中に、start_serverの使い方をちゃんと学ぼう としたけど早速躓く。
俺「他の人のstart_serverの使い方見てみたんですけど、引数の中の -- の意味が start_server のmanにもソースにも Server::Starter のperldocにもソースにも載ってないの!」
「それ Getopt::Long の機能だよ」
すみませんでした。惜しかったね俺。
関係ないけどORM
どうも人は皆DBIに回帰していくらしい。
- 俺のここ数年の変遷
- DBI?へーこれ使うんだー
- ORM?おお楽だなこれ
- DBICは重いし黒いから色々軽いの試してみるか
- 駄目だ重い…結局DBIのハンドラ取ってきて使いまくってる…
- もうDBIでいいや
mysql
俺自身に5.5の運用実績も知識も無いのに「5.5に変えたい!」なんていう無茶が実にあっさり通ることに感謝しつつ、さっそく躓いてる俺。
移行の手順は
現DB(M) -> 現DB(S) -> 新DB(M) -> 新DB(S)
とレプリケーションし、アプリは現Mをmasterとし、それ以外をslaveとする。どっかのタイミングでアプリを一旦停止、様子見て新DBしか見ないようにする、と。
で、新DBは投入前は色々いじることができた。素敵なのは新DBにalter投げてもレプリケーションが壊れない事。
あんなindexやこんなalterを投げることができる!!!いや別にそれまでだって早朝に投げればまぁいけたけど。
で。mysqldを色々再起動するたびに気付いたのが、新DBMでstop slaveしてからmysqldを停止しても、起動したら勝手にレプリケーションが走る。
いや別にいいっちゃいいのだけど。本番投入したらまず再起動しないだろうし、したとしてもその頃には現DBなんて無くなってるから。
とはいえ気持ち悪いので調べるも頭が足りず分からなかったので助けて。
skip-slave-start とか reset slave とか でもstop slaveしたならmysqld再起動しても勝手に走らないと思うんだけどなあ
ええと…
[あとでやる」
こんなところか。
もう一度言うと実際にあれこれ教えてくれたり助言してくれるのが周囲のギークな方々で、俺一人じゃどれも出来てない。
そんなこんなで楽しく過ごしている。
2011-08-16
log-binのデフォルト値を使わない
mysql | |
mysqlでレプリケーションするときは my.cnf の mysqld セクションに
[mysqld]
log-bin
などと書いてバイナリログを吐く。
このように log-bin とだけ書いた場合、バイナリログの名前は5.1では HOSTNAME-bin.000001 のようなものになる。
--log-bin[=base_name] オプションで起動すると、mysqldで、データ更新に関わる SQL ステートメントのすべてをログ ファイルに書き込みます。base_name の値を指定しない場合、デフォルト名は、-bin を元にするホスト マシンの名前になります。
5.0では mysqld-bin.000001 だったはず。
まあ、5.1(以降?)では、 log-bin とだけ書いたらバイナリログの名前にホスト名が使われる。
で、もしdbの再起動などが起こった場合。レプリケーションが再開されるときには、当然ながらそれまで使っていたファイル名のバイナリログが使われる。
で。dbの再起動前後でホスト名が変更されていると、レプリケーションが開始されない。
そりゃそうだ、それまで使ってたファイルがどっかいったのだから。
「ホスト名なんて変更するもんでもないし、バイナリログの名前は何でもいいし」のような考えが有ろうが無かろうが、db再起動前後でホスト名が何故か変わるような環境だった場合に加えて
- サーバを再起動した場合
- mysqldを再起動した場合
- 他、何かが起こった場合
に、レプリケーションがはじまらねぇーな事態になって悲劇のもととなるので、何でもいいから指定しておきましょう。
log-bin=hoge
とかでもいいから。サービス名とかでもいいから。とにかく未指定はやめましょう。
分かりやすく言うとすいませんでした。

補足ありがとうございます。