LoginSignup
2
1

More than 5 years have passed since last update.

InnoDB memcached プラグイン利用時にはdelete()メソッド利用で未COMMITデータが失われる事がある

Last updated at Posted at 2017-07-31

結論

InnoDB memcachedプラグインにて、存在しないキーに対するdelete()メソッドを利用すると未COMMITのデータが失われてしまうことがある。

前提条件

パフォーマンスチューニングとして、daemon_memcached_r_batch_size および daemon_memcached_w_batch_size の値を両方共増やした状態であるとする。
https://dev.mysql.com/doc/refman/5.6/ja/innodb-memcached-tuning.html

この時、システムがクラッシュすると未COMMITのデータが失われるのは当然の話なので許容範囲としますが、クラッシュしなくても未コミットのデータが失われるケースがある。

検証コード

以下、検証はRDS for MySQLにおける結果です。

検証コード1:正しい動作

test1.php
<?php

  define ("RDS_HOST_NAME",  "******");

  $memcache_obj = new Memcache;
  $memcache_obj->addServer(RDS_HOST_NAME, 11211);

  $memcache_obj->set("key1","value1", 0 ,10);

  $memcache_obj->set("key2","value2", 0 ,10);
  $memcache_obj->delete("key2");  // set()したキーを削除する

  echo "rtn = ". $memcache_obj->get("key1")."\n";

→ rtn = value1 が出力される

検証コード2:set()したばかりのkey1の値が取得できない

test1.php
<?php

  define ("RDS_HOST_NAME",  "******");

  $memcache_obj = new Memcache;
  $memcache_obj->addServer(RDS_HOST_NAME, 11211);

  $memcache_obj->set("key1","value1", 0 ,10);

  // $memcache_obj->set("key2","value2", 0 ,10);
  $memcache_obj->delete("key2");  // set()していないキーを削除する

  echo "rtn = ". $memcache_obj->get("key1")."\n";

→ rtn = が出力される

対策

以下のいずれかの対策が必要

  1. daemon_memcached_r_batch_size および daemon_memcached_w_batch_size の値を1にする
  2. delete()を行わずに、削除済みを表現するデータを1秒間だけset()する。

※2. のケースでnullを入れる場合にはnullを明示的にキャッシュするパターンとの検証は必要です。

[おまけ]InnoDB memcached プラグインをセッションストレージとして利用したい理由

一言で言うと、お手軽にそれなりに信頼性のあるセッションストレージを準備出来るため。

※通常のMemcachedをセッションストレージとして利用したい場合には、冗長化対応が難しい。
参考:http://qiita.com/taruhachi/items/a844bf373623991873ff

メリット

  • MultiAZ環境であればノード障害時に自動フェイルオーバーが実行される
  • スケールアップ、スケールイン時にキャッシュが揮発しない
  • 能力を超えたスパイク時にもレイテンシが低下するだけで、深刻な障害に直結しにくい
  • memcachedプロトコルを利用可能なので開発環境を整えやすい

デメリット

  • スケール変更時に若干のダウンタイムが発生する
  • 自動でのスケール変更ができない
  • memcachedと比較した場合にはコストパフォーマンスが悪い

他のセッションストレージとの比較

ElastiCache Memcached ElastiCache Redis DynamoDB RDS for MySQL memcached plugin
ノード障害時の挙動 該当キャッシュが揮発する
該当のキャッシュの汚染が発生する
自動フェイルオーバー実行 自動フェイルオーバー実行 自動フェイルオーバー実行
性能上限 比較的高速 比較的高速 比較的高速 スケールアップの上限あり
オートスケール 出来ない 出来ない 出来る 出来ない
スケール変更時のデータ担保 "該当キャッシュが揮発する
該当のキャッシュの汚染が発生する
キャッシュが全て揮発する 影響が発生しない 影響が発生しない
スケール変更時の挙動 そもそもデータが担保されない そもそもデータが担保されない ダウンタイムが発生しない ダウンタイムが発生する(数十秒)
メンテナンスウインドウ あり(キャッシュが揮発する) あり(キャッシュが揮発しない) あり(キャッシュが揮発しない) あり(キャッシュが揮発しない)
スパイク時の挙動 レイテンシが低下する レイテンシが低下する キャパシティユニットの追加が完了するまでの間、スロットルされた以上のリクエストに対してエラーが発生する レイテンシが低下する
特記事項(メリット) memcachedプロトコルを利用可能 使用頻度が低い時に低コスト memcachedプロトコルを利用可能
特記事項(デメリット) 高負荷時にはノード間のデータ汚染はかなりの頻度で発生する。 スケール変更ができないので、高コストになりがちである。 ・独自プロトコルであるため変更しにくい
・使用頻度が高い時に高コストである
・一度パーティション分割が進んでしまうと、その後恒常的にスロットルが発生し始めることがある。
del()利用時にロールバックされる。
同時接続数を1024より増やせない。
永続的接続時に接続の切断が発生する。

デメリットとしてコストパフォーマンスの悪さがあるが、スケール変更や障害時におけるデータの担保のしやすさなどからのメリットも多そうである。

追記

アプリケーションを構築し、負荷試験を実施したところ、2点問題が発覚しました。

問題1

永続的接続を利用した際に、サーバからの接続の切断が頻繁に発生する。(そもそも永続化されていない可能性あり??)

問題2

順次負荷を増やしていったところ、
MemcachePool::set(): Server [HOSTNAME] (tcp 11211, udp 0) failed with: Connection refused (111)"
というエラーが頻発するようになりました。
接続周りのエラーであることから、
MAX_SIMULTANEOUS_CONNECTIONS
を変更しようとしたのですが、RDSではこの値の範囲は10-1024の範囲でしか設定できないようです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1