memcachedの概要と動作確認

## はじめに
インターン先でMemcashedに触れる機会があったので、どんなものか調べてみました。
Memcashedについて様々な素晴らしい記事があり、それらを参考に体型立ててまとめたものなので引用が多くなっております。

## memcachedとは
とてもわかりやすい記事があったので、そちらを引用させていただきます。

>memcachedは高性能な分散メモリキャッシュサーバ。データベースへの問い合わせ結果を一時的にキャッシュすることで，データベースへのアクセス回数を減らし，動的なウェブアプリケーションの高速化やスケーラビリティの向上のために利用されている。
引用元：
[memcachedを知り尽くす ~第1回 memcachedの基本~](https://gihyo.jp/dev/feature/01/memcached/0001)

## 導入方法
>参考：[macでmemcachedを導入する手順 - Qiita](https://qiita.com/makotok7/items/9998b15f79fc7a53af24)

###インストール
```shell:console
# brewでmemcachedをインストール
$ brew install memcached
```

###デーモンの自動起動設定
シンボリックリンクを作成し、launchctlで定期実行させるように設定します。

```shell:console
# シンボリックリンクの作成
$ ln -sfv /usr/local/opt/memcached/*.plist ~/Library/LaunchAgents

#　定期的にスクリプトを実行する設定
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist
```

## 動作確認
###0. 前提知識
memcachedがどのような仕組みで値を格納しているのかのイメージを持っていないと、これからのコマンド操作などが理解しづらいと思いました。
とてもいい記事を見つけたので、引用させていただきます。

>memcachedはSlabAllocatorという構造でデータを格納している。SlabAllocatorは、メモリを最初からある程度の量確保しておき、その確保したメモリを一定サイズの塊に分割してフラグメンテーションが起こらないようにした構造。memcachedはchunkという小さなメモリ領域に値を保存する。このchunkは決まったサイズので、chunkのサイズは複数ある。同一サイズのchunkをまとめたものがSlabと呼ばれる。memcachedはまずメモリをPageという単位で分割する。このPageはデフォルトで1MB。そのPageのなかにそれぞれのSlabが入り、Slabの中にはSlab固有のchunksizeのchunkたちが入っているという構造になっている。
引用元：[キー一覧を見るためにmemcached入門](https://shim0mura.hatenadiary.jp/entry/20140125/1390647044)

つまり、chunkというさまざまな大きさがある最小単位の入れ物があって、そのchunkを大きさごとにまとめたものがSlabっていうことみたいだ。格納する値の大きさによって使うchunkが変わってくるみたいです。

###1. プロセスが起動しているか確認
> 参考：[macでmemcachedを導入する手順 - Qiita](https://qiita.com/makotok7/items/9998b15f79fc7a53af24)

```shell:console
# デーモンの自動起動設定をしたため、ずっと動いているはず。以下のような出力されればOK
$ ps aux | grep memcached
username      93162   0.0  0.0  4962944    668   ??  S    水06PM   0:01.21 /usr/local/opt/memcached/bin/memcached -l localhost
username       5111   0.0  0.0  4427068    828 s000  S+   10:36AM   0:00.00 grep memcached
```

###2. memcachedが実際に使えるか確認
> 参考：[RailsからMemcachedを使いつつその実体を垣間見てみる - 株式会社ランチェスター](https://www.lanches.co.jp/blog/11245)

#### rails consoleでmemcacheにアクセスするためのインスタンスを作る

```ruby:rails&#x20;console
[1] pry(main)> a = ActiveSupport::Cache::MemCacheStore.new('localhost:11211', expires_in: 60, namespace: 'NS_ABC', compress: true)
=> #<ActiveSupport::Cache::MemCacheStore:0x007f9e886df938
 @data=#<Dalli::Client:0x007f9e886df690 @options={}, @ring=nil, @servers=["localhost:11211"]>,
 @options={:expires_in=>60, :namespace=>"NS_ABC", :compress=>true}>
```
引数で渡しているもの

- localhost:11211 > ポート番号の指定
- expires_in: 60 > 時間の指定
- namespace: 'NS_ABC' > タグと認識している
- compress: true > compressにtrueを指定した時は、データ容量が特定の閾値（compress_thresholdで指定も可能）を超えた際に圧縮するという設定のよう

#### データをキャッシュさせたり取り出したりする
```ruby:rails&#x20;console
# キャッシュ
[3] pry(main)> a.write('key', 'val')
=> 5044031582654955520
 
# 取り出し
[4] pry(main)> a.read('key')
=> "val"
```

#### telnetでmemcachedに接続し、実際に値が格納されているか確認する

```shell:ターミナル
# telnetでmemcachedに接続する
＄ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
```

まずは`stats items`でSlabの一覧を確認する。

```shell:ターミナル
stats items
STAT items:1:number 4
STAT items:1:number_hot 0
STAT items:1:number_warm 1
STAT items:1:number_cold 3
・
・
・
END
```

先程格納した値がどのSlabに格納されているかはstats cachedump [Slabの番号] [取得する要素数]で1つ1つ中身を確認していく。

```shell:ターミナル
#1つ1つのSlabを確認する stats cachedump [Slabの番号] [取得する要素数]
stats cachedump 1 100
stats cachedump 2 100
stats cachedump 3 100
・
・
・
stats cachedump 4 100
ITEM NS_ABC:key [102 b; 1638165259 s]
END
```

Slab番号4のところに、先程格納したものが[namespace]:[key]というファーマットで格納されていることがわかります。
`get <キー>`で値を確認してみましょう。

```shell:ターミナル
get NS_ABC:key
VALUE NS_ABC:key 1 102
o: ActiveSupport::Cache::Entr:
                              @valueIval:ET:@created_atf1638164899.004805:@expires_in6e1
END
```
よくわからない値が出ましたが、これは格納した値がシリアライズされたものです。

したがって、`ActiveSupport::Cache::Entry`にデータを詰め込んでシリアライズをすると、同じようなものが作れます。`rails console`の方で値(val)をシリアライズしてみましょう。シリアライズは`Marshal`というクラスを使うのがスタンダードらしいです。

```ruby:rails&#x20;console
[32] pry(main)> b = ActiveSupport::Cache::Entry.new('val', expires_in: 60)
=> #<ActiveSupport::Cache::Entry:0x007f8ee9c7e758 @created_at=1638165624.288047, @expires_in=60.0, @value="val">

# Marshalというクラスを使ってシリアライズする
[33] pry(main)> puts Marshal.dump(b)                                                                                                 o: ActiveSupport::Cache::Entr:
                             @valueIval:ET:@created_atf1638165624.288047:@expires_in6e1
=> nil
```
memcached側で出力されたものと同じになるため、key/valでしっかり値が格納されていることがわかります。

#### memcached側で値を格納し、rails consoleで確認するような逆方向もできる
```shell:ターミナル
# 値を入れる set <キー> <フラグ> <有効期間> <サイズ>
set NS_ABC:hoge 0 60 8
hogehoge
STORED


#　値をmemcached側で確認してみる　get <キー>
get NS_ABC:hoge
VALUE NS_ABC:hoge 0 8
hogehoge
END
```

しっかりと格納されていることがわかりました。
どうやら、インスタンスとnamespaceが対応づけられているみたいです。

#### また、実際に動いているアプリケーションのコンソールでも確認ができる

```shell:Railsアプリを起動しているターミナル
Cache read: "namespace"
Cache fetch_hit: "namespace"
```

casheが利用される時は、上記にあるように、`fetch_hit`と出力されます。
cacheは`config/environments/development.rb`で`memcached`が設定されています。

## 最後に
今回はローカル上での簡単な動作確認を行いました。速度改善などに役立ってくる機能なので、今後は実際の開発現場での活用法などをまとめていけたらと思います。至らない点が多くあると思うので、ご指摘などございましたらご教授いただけると幸いです。
最後まで読んでいただき、ありがとうございました。

##### 参考にさせていただいた資料一覧
> [第1回　memcachedの基本：memcachedを知り尽くす](https://gihyo.jp/dev/feature/01/memcached/0001)
[第2回　memcachedのメモリストレージを理解する](https://gihyo.jp/dev/feature/01/memcached/0002)
[macでmemcachedを導入する手順 - Qiita](https://qiita.com/makotok7/items/9998b15f79fc7a53af24)
[キー一覧を見るためにmemcached入門 - tumblr](https://shim0mura.hatenadiary.jp/entry/20140125/1390647044)
[RailsからMemcachedを使いつつその実体を垣間見てみる - 株式会社ランチェスター](https://www.lanches.co.jp/blog/11245)