2010/11/29

DECOLOGでのMySQL Archiveエンジンの使い方


こんにちわ、stoneです。

今回は、MySQLのストレージエンジンの中の1つ、ArchiveエンジンのDECOLOGでの使い方をご紹介したいと思います。
※「DECOLOGでのMySQL BlackHoleエンジンの使い方」も合わせてどうぞ

Archiveエンジンの概要

MySQLのマニュアルをご覧いただくのが正確なのですが、その特徴を簡単にまとめると。。。。
  • insert/selectは出来るが、update/deleteは出来ない
  • order byはサポートされない
  • blobもサポートされない
  • データは圧縮されてディスクに保存される

まぁ、最初にこのマニュアルを読んだときの、正直な感想は、
「どうやって使うんだ、これ?」
って感じでした。
deleteが出来ないので、データは溜まる一方だし、データは圧縮して保存されているので、selectもそんなに速くないことは容易に想像できます。


アクションログ

DECOLOGでは、ユーザーのアクティビティの統計を取るため、action_logというデータを取っています。
create table action_log (
  action_name varchar(16),
  blog_id     int unsigned,
  param       varchar(64),
  reg_date    datetime
) engine=archive;
action_nameには、例えば、"entry"(記事の投稿)、"comment"(記事へのコメント)等が入ります。このaction_logは、日次で集計を行っています。


テーブルのスワッピング

で、archiveエンジンの運用をどうするのかというと、上記のアクションログの場合、集計の前に、以下の様なSQLを発行します。
drop table if exists action_log_yesterday;
create table action_log_new like action_log;
alter table action_log rename to action_log_yesterday;
alter table action_log_new rename to action_log;
つまり、
  • action_log_yesterdayをドロップ
  • action_log_newテーブルを作成
  • 既存のaction_logをaction_log_yesterdayにリネーム
  • action_log_newをaction_logにリネーム
こうすることで、ほぼ瞬間的に(1秒以下)で、action_logの切り替えが完了します。ユーザーのアクティビティは、引き続き、新しく作成した空のaction_logに蓄積されます。


データの集計

集計したいデータは、action_log_yesterdayに保存されています。このテーブルは、ユーザーからのアクセスから解放されているので、ゆっくり集計することができます。
select action_name, blog_id, param from action_log_yesterday
と、action_log_yesterdayを全件selectして、結果をタブ区切りで、ファイルに出力します。ファイルの出力されたデータは、HDFSに乗せてHadoopで、集計をしています。
(Hadoopでの集計の一例は、以前の記事、HadoopによるApacheのログ解析の実際で紹介しています)

サーバーの運用

DECOLOGでArchiveエンジンを使用してるデータには、以下のような共通点があります。
  • ユーザーのアクセスで、selectが発生しない
  • かなり頻繁にinsertが発生する
  • 集計は、daily、もしくは、hourlyで行えば十分
  • 集計された値は、内部で使用する値


また、DECOLOGでは、Archiveエンジンで、4つのデータを運用していて、かなりの頻度でアクセス(insert)があるため、サーバーは7台用意して、LVS(keepalived)により負荷分散させています。


実は。。。。。。。。。。
ここまで、書いておいてなんなんですが、最近、hiroshiが中心となって、archiveエンジンを使用しているデータを順次redisに移行していってます。そのうち、彼がレポートを書いてくれるでしょう。(笑)


次回は、daemontoolsを利用した運用をいくつか紹介しようかと考えています。
では、また次回に♪

2010/11/22

mysqlのテーブルの「のれん分け」


二週連続でこんにちは。夜は炭水化物は控え、朝はカレーが食べたいhiroshiです。

DECOLOGもそうでしたが、サービス開始するときに、たいがいのWEBサービスは大規模になることを前提としたインフラ構成は採りません。
たとえば、1000人も会員がいない状態でDBにシャーディングが採り入れられてたら、それはちょっとした事件です。

なのでだいたいは1台のサーバにWEBサーバもDBサーバも兼任させるような状態から始まるわけです。

だんだん人気が出てきて負荷がキツイ!となれば、まずはWEBサーバとDBサーバを分けるあたりが定石なのでしょうか。
その状態でも1つのDBサーバには、サービスで利用する全テーブルが入っている状態も珍しくないかと思います。

そして、さらに人気が加速していけば、いずれはテーブルごとにDBを分ける、という局面にぶち当たるわけです。
この「DBを分ける」作業のことををうちでは「のれん分け」と呼んでいますが、この手順を紹介したいと思います。


のれん分けイメージ

大まかな手順は以下です。

  1. 三段構成にする。
  2. slaveを切り替える
  3. masterを切り替える
  4. 余分なテーブルを落とす

の4ステップです。



1.三段構成にする。


これはつまりこんな状態にすることをいいます。

三段構成イメージ

この作業は基本的には単純なslave増設と新規レプリケーション構成を組むのの組み合わせでできるので、前回のエントリ「replicationしてるMySQLのslave増設手順」を参考にしてください。

通常のレプリケーション構築との違う、ポイントとしては
  • テーブル構成は最初は丸ままコピーすること
  • 「New master」my.cnfの設定にlog-slave-updatesとreplicate-do-tableでノレン分けしたいテーブルを設定しておくこと
です。


テーブル構成を丸ままコピーするのは、そうしないとレプリケーションが失敗するからです。replicate-ignore-dbやreplicate-do-tableなどのレプリケーション設定は、「指定のDBまたはテーブルのデータだけを受け付ける(無視する)」、のではなく、「いったん全部のデータを受け付けるけどreplicate-do-table等の設定に合わないものは破棄する」という意味合いの設定です。

もう1つは特に説明は要らないですね。具体的な設定例としてはこんな感じです。

[mysqld]
log-slave-updates
replicate-do-table=db.entry

この構成が作れてNewも含めた全slaveの同期がとれたことが確認できれば次のステップです。同期がとれたかどうかの確認は件数をとるとかそういうので確認します。



2.スレーブを切り替える


これまでは、blog、user、entryというすべてのテーブルへの接続先は同じでしたが、これからはentryだけ別にしたいわけなので、そのための準備をします。
まず、LVSに別のバーチャルIPを割り当て、そこ宛てのパケットは「New slave」に割り振られるようにしておきます。

そして次にアプリケーション対応です。今まではアプリケーション中のDBの接続先の設定はMASTERかSLAVEか、というところだけの切り分けでした。今回の場合、entryテーブルのみ別のDBグループに切り分けることになるので、接続先の設定にentryのslave(New slave)に割り振るようにします。

イメージ的にはこんな感じです。

class My_DB_Entry extends My_DB {

function __construct($connect_master=false) {
 $conn = null;

 if ($connect_master) {
  $conn = DBConnection::get(MASTER);
 } else {
  $conn = DBConnection::get(SLAVE);
 }

 parent::__construct($conn);
}

だったのを・・・

class My_DB_Entry extends My_DB {

function __construct($connect_master=false) {
 $conn = null;

 if ($connect_master) {
  $conn = DBConnection::get(MASTER);
 } else {
  $conn = DBConnection::get(ENTRY_SLAVE);
 }

 parent::__construct($conn);
}
 

こんな感じに。

この時点ではまだentryのmaster(New master)への割り振りはしません。
WEBサーバが冗長構成をとっている場合は特にそうなのですが、アプリケーションをデプロイしている間にどうしてもmasterとnew Masterの両方をentryのマスターとして捉えられてしまうタイミングができてしまいます。(DECOLOGの場合)その結果、データの整合性が取れなくなってしまうため、まずはスレーブのみ切り替えを行います。これはidにauto incrementを使っている場面を想像するとわかりやすいと思います。



一方でスレーブの場合はslaveでもNew slaveでもどちらを参照されてもデータの整合性的に問題ないので、サービスを止めずに切り替えることができます。

そのため、DECOLOGでは、まずはslaveのみ切り替え、後にメンテナンス時にサービスを停止して、DBアクセスのない状態にしてからマスタを切り替えるという運用をしています。

slave切り替え後はこんな感じのイメージです。


blog,userテーブルのreadはslaveへ。entryテーブルのreadはNew slaveへ




3.マスタを切り替える


前述のとおり、サービスを停止しマスタへのアクセスがなくなった状態になってから、マスタのアクセス先を切り替えたプログラムをデプロイします。
※ちなみにマスタのアクセスがなくなった状態の確認は、show master statusでpositionが動かなくなるのを確認しています。

class My_DB_Entry extends My_DB {

function __construct($connect_master=false) {
 $conn = null;

 if ($connect_master) {
   $conn = DBConnection::get(MASTER); ←ココ
 } else {
   $conn = DBConnection::get(ENTRY_SLAVE);
 }

   parent::__construct($conn);
}

マスタの切り替えが上手く入ったのを確認したら、このステップは終了です。



4.余分なテーブルを落とす


これで、アプリケーションからのentryテーブルへのアクセスは、完全に新しい構成のみとなりましたので、最後の仕上げにかかります。

最初にmaster←new masterのレプリケーションを切ります。

mysql> stop slave;
mysql> change master to master_host='';



mysql> show slave status;
Empty set (0.02 sec)
となったらOKです。

次にmasterからentryテーブルを落とします。
mysql> drop table entry;
slaveのはレプリケーションしているので勝手にentry tableはドロップされます。

最後にnew masterからblogテーブルとuserテーブルを落とします。
mysql> drop table blog,user;
ここのnew slaveからもレプリケーションしているので勝手にドロップされます。

ということで、これでのれん分けの完了です。

これは例によって「DECOLOGのやり方」ということでもっと良いやり方があるかもしれません。
よくはてブのコメントとかで「こうしたらいい」というコメントをいただいたりしますが、まさしくそういう要素を多分に含んでおりますゆえ。。。

では、また!

次回は弊社シェフによる第1回クッキング講座を予定しています。








2010/11/15

replicationしてるMySQLのslave増設手順



こんにちは、hiroshiです。おひさしぶりですね。
stoneが書いたhadoopの記事が打ち合わせとかで「見ましたよ。評判ですよ。」とか言われてジェラシーいっぱいです。
僕もがんばります。目指せホッテントり!

といっても、僕だと書けることに限界があるので、今日は半定常作業のMySQLの増設作業について書こうと思います。

下図のように、master1台←slave2台がLVS+keepalivedで負荷分散構成されているDBがあるとします。

図1.master-slave構成


この構成の組み方にしようかと思ったのですが、これはググったらいっぱいあったのでホッテントリは狙えないと思ってやめました。

なので、今回のテーマは「このテーブルはwriteは余裕だけどreadがきつくなってきたからslaveを増設しなければ!」となった場合のslaveを増設する手順について書いてみます。
下図のslaveCを追加するぞ!の場合です。

図2. slave Cを追加するぞ


※今回はengine=innoDBの前提で書きます。
※各種userとかIPとかその辺はそこそこ適当にかいてます。

大まかな手順は以下です。

  1. コピー元となるslaveを1台LVSから切り離す。
  2. slave→masterのreplicationを止める。
  3. dumpを取る。
  4. dumpファイルを移動させる。
  5. dumpファイルをインポートする
  6. LVSに参加させる

こんな感じです。

1. コピー元となるslaveを1台LVSから切り離す。


忙しいDBでいきなり切り離すと、sleepのプロセスが大量発生するので、weightを1にしてから外すことにしています。

keepalived.conf
# slave A
real_server     192.168.0.101 3306 {
 weight  1 ←1にする。(通常10にしてます)
 inhibit_on_failure
  TCP_CHECK {
   connect_port    3306
   connect_timeout 3
  }
}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}


weight1にして
svc -h /servie/keepalive


# slave A
#real_server     192.168.0.101 3306 {
#    weight  1 ←1にする。(通常10にしてます)
#    inhibit_on_failure
#    TCP_CHECK {
#        connect_port    3306
#        connect_timeout 3
#    }
#}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}


コメントアウトして
svc -h /servie/keepalive


で、LVSからの切り離しは完了です。

2. slave→masterのreplicationを止める。


slave A上のmysqlでshow processlistするなどしてアクセスされなくなったのをやわらかく確認します。
小心者のぼくは最終的にはtcpdumpとかでmysqlのパケットが流れてこないかを確認するのですが、この時点ではまだreplicationしたままなのでバリバリmysqlのパケットが流れています。

mysql> show slave status\G
~中略~
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
1 row in set (0.00 sec)


mysql> stop slave;
~中略~
Master_Host: master
Master_User: hoge
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000049
Read_Master_Log_Pos: 760141636
~中略~
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
1 row in set (0.00 sec)

ということで、この時点でreplicationが止まりました。
Seconds_Behind_Master: NULL
がreplicationが止まった合図です。

で、
Master_Log_File: mysql-bin.000049
Read_Master_Log_Pos: 760141636
の部分をメモっておきます。

これからdumpをとるのですが、そのデータはmasterのbinログファイルが「mysql-bin.000049」でそのファイルの位置が「760141636」の状態のものだという意味です。]
部分的なコピペでもいいんですが、だいたいこんな感じshow slave status自体をファイルに落としておいておくといいかもです。

mysql> \T slave_status.txt
mysql> show slave status \G
mysql> \t

で、ここでtcpdump -i eth0 port 3306とかやってパケットが流れないのを確認すると精神衛生上いいので僕はそうしてます。

3. dumpを取る。


容量のでかいDBの場合、dumpに半日以上かかることもざらなので、screenを使います。screenじゃなくてもnohup使うでもなんでもいいんですが、不慮のネットワーク切断に備えた何かをやっておいたほうが賢明です。

$ screen

$ mysqldump -u root -p database_stage > dump_database_stage.sql
$ mysqldump -u root -p database_release > dump_database_release.sql
※テスト環境用のDBもあるので、一緒にダンプします。

これでひたすら待ちます。Zabbixのロードアベレージとかがグイーンとあがるので、それが下がったら「終わったかな」と思って見に行きます。dumpが終わったら通知メールを送るようなスクリプトを書いてもいいんですが、必要に思ったことがないので今はそういうのはないです。

ちなみに、だいたいの場合において、このdumpをとっているスキに増設先のmysqlの構築を済ませておきます。

dumpが終わったらreplicationを再開させます。

mysql> start slave;

replicationをとめてた時間などにもよりますが、

mysql> show slave status\G
~中略~
Seconds_Behind_Master: 1348615

と、こんな感じになっているので、0になるのを待ちます。これも見つめていてもなかなか終わらないので、Zabbixを利用します。Seconds_Behind_Masterの値は、常時監視項目としてZabbixで監視+閾値でアラートを設定しているので、これを利用してreplicationが追いつくタイミングを知ることもできます。

ここでちょっとワンポイントなのですが、replicationが追いついた後、

mysql> select count(*) from table;
をします。
innodb限定の手順なのですが、これでinnodb_buffer_poolにデータを乗せようとしています。これをやらないでいきなりユーザーからのアクセスにさらすと、処理がおっつかずにreplicationのディレイが発生しちゃったりします。

ところで、上述のとおり、slave増設はslave1台をサービスから切り離してdumpを取る、という作業手順になります。
ですが、増設のきっかけは「負荷が厳しいから」だけではなくて「slaveが1台故障したから」というケースもあります。
故障のケースでmaster1台←slave2台の構成だと、故障後はmaster1台←slave1台という構成と同義になり、そこからslaveを切り離してしまうとサービスに利用できるslaveがなくなってしまいます。最悪負荷の低い深夜帯にmasterにreadも担当させてやるか、サービスを止めるかなどするしかなくなってしまうので、そこそこ途方にくれます。

ですので、slaveは3台が基本です。
masterがblackholeの場合を考慮すると「実体のあるDB3台が基本」という言い方もあります。

このことはid:naoyaさんの著書「大規模サービス技術入門」にも書いてあったのですが、僕たちは、実際に途方にくれた後にこの著書に出会い、「もっと早くこの本に出会えていれば」と悔やみました。

4. dumpファイルを移動させる。


sftpでもscpでもいいです。増設先のサーバに移動させるだけです。
ただ、うちではサーバ間のinとoutのトラフィックをZabbixで監視してます。で、特に何もしないとscpでも全力でファイル転送されるため、監視の閾値を超えてしまい、アラートメールが飛んできてしまいます。
そうすると、周りに冷たい目で見られるのでZabbixのトラフィック監視をOffにしておくことを忘れてはいけません。
この移動もファイルの容量次第ではscreenするなりしておきます。

5. dumpファイルをインポートする


$ mysql -u root -p database_stage < dump_database_stage.sql
$ mysql -u root -p database_release < dump_database_release.sql
これも相当時間がかかるケースがあるのでscreenなりをしておきましょう。 インポートが終わったら、replication設定をします。
mysql> change master to master_host='マスターホスト名', master_user='repl', master_password='repl', master_log_file='ログファイル名', master_log_pos=ポジション;
この「ログファイル名」と「ポジション」に2でメモったMaster_Log_FileとRead_Master_Log_Posを入れます。 その後は
mysql> start slave;
して、Seconds_Behind_Masterが0になるのを待ってselect count(*)やっての手順は先ほどまでと同じです。

6. LVSに参加させる


これで最後です。 keepalived.conf
# slave A
real_server     192.168.0.101 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}

# slave C
real_server     192.168.0.99 3306 {
 weight  1 ←1からはじめる
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}
新しく追加したDBのweightは1からはじめます。何かしくじってたときのダメージを低くするためです。アプリのエラーログを監視しながらこの作業は行い、エラーがでれば即戻します。各種グラフを見ながらweightを10にまで持っていけたら作業完了です。 以上が単純なslaveの増設方法です。

これ、役に立つと思うのでブックマークよろしくおねがいします!

では、また会いましょう。

次回予告:「1つのDBに複数テーブルが入っている場合において、一部のテーブルだけ別DBに移動させるの術の巻」

2010/11/08

DECOLOGでのMySQL BlackHoleエンジンの使い方


こんにちわ、ミツバチワークス stoneです。

DECOLOGでは、データベースにMySQLを使用しています。
ストレージエンジンのメインはInnoDBなのですが、他にもMyISAM、BlackHole、Archiveエンジンを使っています。
今回は、その中でBlackHoleエンジンについて、DECOLOG内での利用方法をご紹介したいと思います。


BlackHoleエンジンについて


BlackHoleエンジンは、何もしません。
insert、update、deleteを行っても、データは全く変更されませんし、selectをしても、データは何も返ってきません。
実際のデータファイルを見てみても、テーブル定義ファイルの.frm以外のファイルは作成されません。
/dev/nullと似ているイメージです。

が、BlackHoleのテーブルに対して発行されたinsert、update、deleteは、binlogには残ります。
そのため、レプリケーションを行っている場合、slave側で何か実体のあるストレージエンジンでレプリケーションしていれば、その変更は、slave側に保存されることになります。

また、内部的には、何もしていないので、サーバーからの応答は、すごく速いです。


記事データの配置


以前は、DECOLOGでも、データベースもそれほど系統分けされていなくて、記事に関連するデータは、すべて、1台のサーバーがマスターとして稼働していました。



記事内の画像は、もちろん、本文データも大きくなってしまう傾向にあるので、
別テーブルとして運用しています。


ユーザーの投稿の滞留が発生


DECOLOGはデコメを専用アドレスに送ることによって記事投稿するブログです。受信メールサーバでは、受信したメールをPHPに渡すことによってDBへの登録を行っています。
ユーザーがどんどん増えてきて、1日あたりの記事投稿の件数が増えてくると、受信したメールをデータベースへ登録するシーケンスでメールの滞留が発生するようになりました。
よくよく状況を調べてみると、新規のテキストデータ・画像データをinsertする際、MyISAMでのテーブルロックが発生していました。
ご存知のように、MyISAMエンジンの場合、データに何かしら変更が入る場合、テーブル全体がロックされます。
このテーブルロックが、多発していて、それにより投稿されたメールの処理が遅れ、滞留するようになっていました。


対策1 マスターをBlackHole化


記事のテキストデータ・画像データはともに、マスターのサーバーへselectすることはなく、
また、insertされてから、投稿したユーザーがページへアクセスして、記事を表示(select)されるまで、少なくとも数秒程度のタイムラグがあります。
この特性を利用して、masterをBlackHole化することにしました。

手順としては。。。。。
masterのサーバーで
mysql> alter table (テキストテーブル) rename to (テキストテーブル)_saved;
mysql> create table (テキストテーブル) like (テキストテーブル)_saved;
mysql> alter table (テキストテーブル) engine=BlackHole;
として、テーブルをBlackHole化した後、 slaveのサーバー上で、
mysql> drop table (テキストテーブル);
mysql> alter table (テキストテーブル)_saved rename to (テキストテーブル);
として、テーブルを元に戻します。



対策2 memcachedを併用


「対策1」を導入した後に気づいたのですが、投稿したユーザー本人はその記事を見るまでに、数秒のタイムラグがあるのですが、人気があって頻繁にアクセスされているブログの場合、投稿されたそのタイミングで、他のユーザーからのアクセスがある場合があるんですね。
そうすると、テキストデータ・画像データのレプリケーションがまだ追いついていないケースがあって、その記事が正常に表示できないという問題が出てしまいます。

そこで、記事テキストと記事画像については、投稿のタイミングで、データベースよりも先に、memcachedにもデータをストアしておいて、レプリケーションのタイムラグを表面化しないようにしました。



次回は、DECOLOGでのArchiveエンジンの使い方について、ご紹介する予定でいます。

では、また次回に♪

2010/11/01

redis導入後にトラブル発生、そのレポート


こんにちわ、ミツバチワークス stoneです。

今回は、redisシリーズ第3弾、実際にredisをサービスの投入してみて、うまく行かなかった事例についてご紹介します。

redisの使用用途


今回、いくつかあるセッションデータのうち2つをMySQLからredisへ移行させました。
これらのセッションデータ、MySQL上では、セッションIDの他に複数のカラムから構成されているのですが、redis上では、この複数のカラムをserialize()して、
key(string) => value(string)
という形で格納するようにしました。

ちゃんとソースコードで確認はしていないのですが、memcachedでも、TTLが設定できますが、TTLを過ぎたデータを監視してクリアしていないですよね。
また、memcached内部のslabの構成次第では、TTLまでデータが保持されずに、データがクリアされてしまうケースにも遭遇したことがあります。
(まぁ、キャッシュだから当たり前の話なのですが)
反面、redisの場合、TTLまでデータが残っていて、かつ、TTLを過ぎたデータは、定期的にクリアされています。
(これは、ソースコードレベルでも、実際の状態監視でも確認できました。)

運用中の状態


サービスに投入後の、ピークタイムでのredisの状態です。

ロードアベレージ

redisのロードアベレージは、MySQLより低め、memcachedよりは高めで推移します。
こんな風にギザギザのロードアベレージは、初めて見る形です。
瞬間的にロードアベレージが上がるのは、バックグラウンドでのsnapshotの保存によるもののようです。

トラフィック

こちらも、初めて見る形のトラフィックグラフでした。
どうやら、バックグラウンドでsnapshotを保存するタイミングで、リクエストが滞留したり、滞留していたリクエストが一気に解消したり、というのを繰り返しているようです。
コレはこれで問題だったので、後ほど取り上げます

メモリの使用量

このグラフは、時間の尺が12時間なのですが、ピークタイムを過ぎて、TTLを過ぎたデータが、クリアされて行くのが、よくわかります。
最初にグラフを見たときには、とても感動しました。


問題1: リクエストが詰まる


現象

上記のredisのトラフィックが、サイト全体のパフォーマンスにまで、影響していました。
redisのトラフィックが詰まるタイミングで、サイト全体のトラフィックも低下する現象が見られるようになってきたのです。
下のリバースプロキシのグラフで、ざくっと切れ込みの様にトラフィックが下がっている部分は、redisの「詰まっている」タイミングと同じタイミングで起きていました。

対処

少し調べてみたのですが、なかなか原因はつかめません。
ひとまず、わかっているのは、
・snapshotの保存のタイミングで、トラフィックが低下する
・snapshotの保存のタイミングでは、CPUの使用率が上がる
の2点のみ。
そこで、保存の際に少しでもCPUの使用率を下げる為に、
rdbcompression no
(デフォルトはyes)
としてみました。

また、事前に別サーバーで、redisを立ち上げて、
1) "rdbcompression yes"で1万件程度のデータをストア
2) redis-serverをシャットダウン
3) redis.confを"rdbcompression no"に変更
4) redis-serverを起動
としても、データは保全されていることを確認しました。

結果(解決)






上記の問題があった時間帯の翌日のredisとリバースプロキシのトラフィックのグラフです。
ほぼ同じような形で見分けがつきにくいですが。。。。

redis

rev-proxy

問題だった詰まる→解消→詰まる→解消の現象が解消されています。ひとまず、ほっとしました。


問題2: 応答が極端に遅くなる


こちらは、残念ながら、解消できていない問題です。

現象

問題が起きた時間帯のredisのトラフィックのグラフです。
グラフを見ると一目瞭然なのですが、ある瞬間を境にして、トラフィックがかなり乱れているのがわかります。
同じ時間帯のロードアベレージを見てみると。。。。

これらのグラフから見て取るに、問題の現象は、
1) 内部的に「何か」が起きる
2) ロードアベレージがじわじわ上がる
3) その「何か」が分水嶺を超える
4) トラフィックが乱れる
というシナリオのようです。

この状態になると、redis-cliでも、応答が悪くなりました。
それにつれて、サイト全体のパフォーマンスが悪化して行きました。

対処

この現象が起きた場合、redis-serverを再起動すると、すんなりと問題が解消します。
が、TERMシグナルを発行しても、サさってシャットダウンできない場合もあり、実際に人のオペレーションが必要な状況でした。
問題が発覚してから、数日は、夜中まで監視を行い、問題が起きた場合はリスタート、という運用をしていたのですが、さすがに数人で運用してる現状では、無理がかかりすぎるため、MySQLに戻しました。



redis導入後、このトラブルが起きていないケースもあるので、原因の特定がまったくできていない状態です。
問題が起きていないケースでは、トラブルがあったケースに比べて、
・データのTTLが長いため、順次データがクリアされる状態になっていない
・データ件数が1/10程度
となっていて、今後、データのTTLが切れ始めるタイミングが要注意だと考えています。

今回は、トラブルに合いましたが、やはり、redisのスピード感は魅力です。
そのため、今後は、TTLが設定されない、永続的なデータについても、実験をしてみることにしています。

では、また次回に♪