いざという時に抑えておきたい Memcached の最低限を知る

Memcached の free_memory を使い果たした等のいざというときに抑えておきたいインフラエンジニアが問題を把握するためにまとめたものです。
Redis と同じように key を消したら治るかなと思っても治らない :scream: とならないように、Memcached がどういったメモリ管理をしているのか、操作方法を調べました。

## `memcached-tool host:11211`

- Item 毎の統計

```
$ memcached-tool host:11211
  #  Item_Size  Max_age   Pages   Count   Full?  Evicted Evict_Time OOM
  1      96B      2592s      36  385809      no        0        0    0
  2     120B      2009s     180 1565358      no        0        0    0
  3     152B  12340223s    3380 23315240     yes    64574  9477475    0
  4     192B      2397s      16   86704      no        0        0    0
  5     240B      4025s      10   43348      no        0        0    0
  6     304B     37589s       8   26855      no        0        0    0
  7     384B     47515s       7   17352      no        0        0    0
  8     480B     66223s       6   11697      no        0        0    0
  9     600B     28780s       6    8375      no        0        0    0
 10     752B      1927s      95   61493      no        0        0    0
 11     944B     36268s     633  702630     yes      572 21781327    0
 12     1.2K     55151s    1989 1760264      no     5244 21762430    0
 13     1.4K   7247718s       9    2159      no        0        0    0
 14     1.8K  11580483s       2     441      no        0        0    0
 15     2.3K  10254258s       2     351      no        0        0    0
 16     2.8K  21727816s       2     429      no        0        0    0
 17     3.5K  21724300s       5    1034      no        0        0    0
 18     4.4K  21723768s       3     514      no        0        0    0
 19     5.5K  21724292s       3     435      no        0        0    0
 20     6.9K  21725877s       4     418      no        0        0    0
 21     8.7K  21725833s       4     378      no        0        0    0
 22    10.8K  21727277s       4     329      no        0        0    0
 23    13.6K  21729927s       4     285      no        0        0    0
 24    16.9K  21729517s       5     205      no        0        0    0
 25    21.2K  21727119s       5     188      no        0        0    0
 26    26.5K  21730281s       5     184      no        0        0    0
 27    33.1K  21723728s       6     168      no        0        0    0
 28    41.4K  21731332s       9     191      no        0        0    0
 29    51.7K  21730647s       6     101      no        0        0    0
 30    64.7K  21738387s       4      40      no        0        0    0
 31    80.9K  21732714s       4      29      no        0        0    0
 32   101.1K  21785131s       4      23      no        0        0    0
 33   126.3K  21732741s       3      18      no        0        0    0
 34   157.9K  21794161s       3       7      no        0        0    0
 35   197.4K  21794146s       5       7      no        0        0    0
 36   246.8K  21794235s       4      10      no        0        0    0
 37   308.5K  21794219s       3       5      no        0        0    0
 38   385.6K  21794309s       4       1      no        0        0    0
```

| 表示項目 |内容| 
|:--|:--|
| # | Item の番号 |
| Item_Size | チャンクのサイズ|
| Max_age | スラブの中でもっとも古いデータの期間|
| Pages |  スラブに割り当てられたページ数|
|Count | スラブ内のレコード数|
|Full? |空きチャンクの有無|
|Evicted | 有効期限切れ前に削除された数|
|Evict_Time | 最後の追い出しからの期間|
|OOM | 値をスラブに格納できなかった数(-Mオプション指定時のみ)|

なるほど、これだけではわからん。

## Memcached メモリを整理して再利用する Slab Allocation メカニズムを知る

http://gihyo.jp/dev/feature/01/memcached/0002?page=1 がすごくわかりやすいです :pray: 

> Slab Allocationの仕組みは簡素で，確保したメモリをさまざまなサイズの固まり（chunk）に分けて，同じサイズの固まりをクラス（chunkの集合，またはchunkのサイズを定めるクラス）に整理します

> また，slab allocatorには「確保したメモリは再利用する」という目標もあります。したがって，memcachedは一度確保したメモリは解放せず，常にchunkを再利用します。

![image](https://user-images.githubusercontent.com/2929102/32487294-9fee5ef6-c3ec-11e7-81f1-4b3d65f29263.png)

## Slab Allocatorの主な用語

- Page

デフォルトで1MB確保され，Slabに割り当てられるメモリ領域。Slabに割り当てられた後に，slabのサイズに応じたchunkに切り分けられる。

- Chunk

レコードをキャッシュするためのメモリ領域。

- Slab Class

特定のサイズのchunkをまとめるクラス。

改めて `memcached-tool` でもう一度観てみる
--------------------------------------------

```
$ memcached-tool host:11211
  #  Item_Size  Max_age   Pages   Count   Full?  Evicted Evict_Time OOM
  1      96B      2592s      36  386380      no        0        0    0
  2     120B      2009s     180 1568707      no        0        0    0
  3     152B  13019692s    3380 12983397      no    68273  8800124    0
  4     192B      2397s      16   86910      no        0        0    0
  5     240B      4025s      10   43443      no        0        0    0
  6     304B     37589s       8   26968      no        0        0    0
  7     384B     47515s       7   17403      no        0        0    0
  8     480B     66223s       6   11744      no        0        0    0
  9     600B     28780s       6    8399      no        0        0    0
 10     752B      1927s      95   61508      no        0        0    0
 11     944B    327275s     633  702630     yes    16605 21521262    0
 12     1.2K    315557s    1989 1760265     yes    20207 21532979    0
 13     1.4K   7247718s       9    2153      no        0        0    0
 14     1.8K  11580483s       2     431      no        0        0    0
 15     2.3K  10254258s       2     336      no        0        0    0
 16     2.8K  21754727s       2     423      no        0        0    0
 17     3.5K  21755105s       5    1008      no        0        0    0
 18     4.4K  21755414s       3     506      no        0        0    0
 19     5.5K  21756708s       3     420      no        0        0    0
 20     6.9K  21759824s       4     413      no        0        0    0
 21     8.7K  21755540s       4     410      no        0        0    0
 22    10.8K  21751944s       4     339      no        0        0    0
 23    13.6K  21759305s       4     279      no        0        0    0
 24    16.9K  21757328s       5     200      no        0        0    0
 25    21.2K  21758053s       5     198      no        0        0    0
 26    26.5K  21744825s       5     181      no        0        0    0
 27    33.1K  21752603s       6     165      no        0        0    0
 28    41.4K  21755121s       9     204      no        0        0    0
 29    51.7K  21755534s       6      95      no        0        0    0
 30    64.7K  21741777s       4      41      no        0        0    0
 31    80.9K  21760308s       4      30      no        0        0    0
 32   101.1K  21785131s       4      24      no        0        0    0
 33   126.3K  21794214s       3      18      no        0        0    0
 34   157.9K  21794161s       3       7      no        0        0    0
 35   197.4K  21794146s       5       7      no        0        0    0
 36   246.8K  21794235s       4      10      no        0        0    0
 37   308.5K  21794219s       3       5      no        0        0    0
 38   385.6K  21794309s       4       2      no        0        0    0
```

上記の例で言うと今回に至っては、既にメモリを使い果たしている状態で、 item 番号の 3 と 11 と 12 が累計から Evict されていることがわかった。 Item(Slab) 毎の Pages が増えない限りは Count そのものも増えないことがわかった。

- item 番号の 3  については、 23315240 以上作れない
- item 番号の 11 については、 702630 以上作れない
- item 番号の 12 については、 1760265 以上作れない

状態になっており、そこだけ Evict が発生していた。
ソースコードやデータの中身から頑張って消していたが、たまたますべて Item 番号 3 のレコードを消しているとわかり、たしかに二回目見た時に Full? は no になって解決されていた。 しかし、 3 の Pages が基本的に使いまわす仕様のため、Count は減ってもそのまま保持し続けている。

そのため、 11 と 12 の Page を増やそうにも既にメモリがないため、ここに入るものはすべて Evict され続ける。 めんどくさいと思ったら再起動して全部真っさらからもう一度 Slab Allocation してもらうという手もあるが、中身を観て最適な対応をするしかなさそう。

以下、実際にどんなデータが入っているのか把握するために、コマンド操作をまとめています。

## Memcached コマンド操作

## 専用 CLI はないため、 telnet で接続する

```shell-session
# telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
```

## item 一覧

```shell-session
stats items
```

## item の中のキー一覧

```shell-session
stats cachedump [item番号] [件数]
```

## データの格納

```shell-session
set key1 0 0 6
value1
STORED
```

- set または add というコマンドでデータを保存できる。

```shell-session
<command> <key> <flags> <exptime> <bytes>
<data>
```

- key に対して、exptime 秒間、 bytes 分の領域を確保
- key に、 data を紐付けてメモリにキャッシュ
- exptime を 0 にすると、削除が行われない
- flags はデータを圧縮するかどうかのフラグ
  - 1 圧縮
  - 0 非圧縮

## データの取得

```shell-session
get key1
VALUE key1 0 6
value1
END
```

## データの削除

```shell-session
delete key1
DELETED
```

key に対してワイルドカードがつかえない :scream:

## 全部消す

Memcached 上の全てのキャッシュを削除したい場合は、以下のコマンドを実行する。

```shell-session
flush_all
```

## telnet 切断

```shell-session
quit
Connection closed by foreign host.
#
```