『WEB+DB PRESS Vol.94』読書ノート

WEB+DB PRESS Vol.94』を読んだので、学んだ点をまとめる。

特集1 スケーラブルAWS

第1章 スケーリングの基本戦略

インスタンスサイズの決定

1台がサービスアウトしても全体の処理に問題ないリソースを確保する。
CPU使用率が50%を越えて100%に近づいていくと、CPU使用率と処理能力が比例しなくなっていくので、2台構成であればピーク時のCPU使用率は30~40%が限度。3台以上の構成であれば50%程度を限度とする。

第2章 監視

死活監視とメトリクス監視

死活監視はping確認やHTTPでのリクエストによる確認など。監視する側の要因による誤検知の可能性もあるので、複数回の試行を行い、全て失敗の時のみNGとみなすべき。
メトリクス監視は、ディスク使用率など、継続的にある指標の値を収集する。CPU、ディスク、ネットワーク転送量、SQL発行数、HTTPリクエスト数、レスポンスタイムなどが収集対象。

Zabbix

zabbix-agentが起動してZabbixサーバと通信したタイミングで監視対象の自動登録を行うことができる。zabbix-agentd.confに記述されたHostnameでホストが識別されるため、既存のホストは異なるものに設定する必要がある。
自動登録とは反対に自動削除は機能として用意されていないため、host.deleteというAPIへのリクエストを発行する必要がある。

複数の値の集約

監視は1台1台のメトリクスを個別に把握するよりも、合計でCPUをどれくらい使用しているのかといったメトリクスを監視する方が有用な場合もある。
ZabbixではAggregate checksという型のアイテムを定義することで、ホストグループに所属している複数のホストのメトリクスを集約した値を保存できる。
pingで応答するインスタンスがホストグループの65%を下回ったときに通知する場合は、grpavg["App", "icmpping[]",last,0].last(0) < 0.65のようになる。

Webサーバ/アプリケーションサーバのスケール

CPU

%iowaitが高い場合は、処理をバッチサーバに移譲して%userの割合を増やす。大容量ファイルの配信でも同様の問題が発生するため、CDNの活用を考える。

状態を持つサーバ

セッションを扱う場合は、memcachedやRedisといったKVSを使用したり、DBに保存する。
ファイルを扱う場合も状態を持つことになるが、AWSのS3を使用することでサーバ間のファイル共有が容易に行える。NFSやGlusterFSなどの分散ファイルシステムでは、信頼性やIO性能を担保することが難しい。

ロードバランサ

AWSのELBを使用する。スティッキーセッションを使用してセッションデータのサーバ間での共有を行わないという方式は、あるサーバに障害が起こった場合、そのサーバに接続していたユーザ全てのセッションが消滅してしまうので、使用しない方がいい。

第4章 キャッシュサーバのスケール

キャッシュヒット率

memcached: statsコマンドのget_hitsとget_misses
Redis: INFOコマンドのkeyspace_hitsとkeyspace_misses
アプリのロジックに問題がないのにキャッシュヒット率の下がる要因は、メモリ不足。

CPU

キャッシュサーバはCPUをあまり使わない。
Redisはさらにシングルスレッドで動作することに注意。計算量の多いコマンドを発行してしまうと、次のコマンドが待たされるし、CPUのコア数を上げても意味がない。

メモリ

メモリ使用量は次のコマンドで確認できる。
memcached: statsコマンドのbytes
Redis: INFOコマンドのused_memory(またはused_memory_human)
Redisでは加えて確保したメモリ量(used_memory_rss)とそれとused_memoryの比率(mem_fragmentation_ratio)を取得できる。

コネクション数

memcached: statsコマンドのcurr_connections
Redis: INFOコマンドのconnected_clients
Redisのtimeoutを0に設定していると、TCPコネクションを切断しないままサーバがダウンするなど正常にコネクションを閉じられない場合に、コネクションが滞留してしまう問題がある。

整合性

以下でデータの更新をアトミックに行うことができる。どちらも楽観的ロック。
memcached: CAS(Check And Set)
Redis: トランザクション

データベースのようにロールバックができないので、データベースでロールバックされた場合などに備えて、あらかじめキャッシュデータを取得しておき、ロールバックされた際にキャッシュに元のデータを書き戻すなどの対応を検討する。

プロキシ

twemproxy(nutcrackerとも。yum install nutcrackerだから。)はTwitterが開発したmemcached/Redisのプロキシ。分散や監視を自動で行ってくれる。複数サーバ間でのシャーディングが可能で、コンシステントハッシュ法(/etc/nutcracker/nutcracker.ymldistribution: ketamaを指定)にも対応している。
さらに同じ設定で稼働させたtwemproxyでは同じキーが同じサーバに振り分けられるため、twemproxyを何台用意しても同じように分散できる。そのためtwemproxy専用のサーバを用意せず、アプリケーションサーバに同居させることが可能であり、twemproxy自体の可用性を確保できる。
22222番ポートで稼働twemproxyの情報を返す。
twemproxyはキャッシュ用途なので、Redisを永続データストアとして利用することは想定されていない。トランザクションと一部コマンドは使えない。

データベースサーバのスケール

ディスクI/O

iostat -x 1でread,write,util等を確認しておく。

レプリケーション遅延

MySQLであれば、SHOW SLAVE STATUS\GのRead_Master_Log_PosからExec_Master_Log_Posを引いた値やSeconds_Behind_Masterの値を確認する。
Aurora(MySQL5.6と互換性のあるAWS独自DB)の場合は、スレーブがマスタと同じディスクを参照するアーキテクチャなのでレプリケーション遅延はほとんど発生しない。

メモリ

SHOW ENGINE INNODB STATUS\Gで確認できるFree buffersとSHOW STATUS LIKE 'innodb_page_size';で確認できるinnodb_page_sizeを掛け合わせた値が空いているbuffer_poolのサイズであり、監視対象とすると良い。

プロキシ

スレーブへの参照クエリ分散はHAProxyを使う。TCP/HTTPロードバランサであり、MySQLへのヘルスチェック機能を備えている。
twemproxyと同じようにアプリケーションサーバに同居させ、HAProxy自体の可用性を確保する設計が良い。
アプリケーションはマスタとHAProxyへの2つのコネクションを持つことになる。

垂直分割と水平分割

垂直分割も水平分割も書き込みの負荷分散に有効。
書き込み負荷の高いテーブルが2つあったとして、片方を別のDBに移す垂直分割の負荷分散ができる。
一方、書き込みが集中しているテーブルが一つしかなかった場合、主キーの値等でテーブルを分割して別のDBに水平分割するしかない。

フェイルオーバー

mysqlfailoverやMHA for MySQLを使用する。
コネクションプーリングをしているとフェイルオーバー時にコネクションを再接続しないと新しいマスタに接続できなくなってしまうため注意が必要。

第6章 オートスケール

EBSボリューム

EBSボリュームに書き込んだファイルはインスタンス削除時に失われてしまうため、Fluentdなどのログ転送エージェントを用いて、ログファイルへの追記が行われたあとになるべくリアルタイムに近い形で、別の永続的なEC2インスタンス等にログ送信する構成が必要。

特集2 はじめてのKotlin

第3章 ラムダ式と関数

クロージャ

関数の外側の環境を関数のスコープに含めることができる。クロージャを利用すると、次のように環境を含んだ関数を作ることができる。

ラムダ式とインライン関数

ラムダ式や無名関数を書くとその分無名クラスが作られてしまう。無名クラスの生成を抑えるために、関数定義にinlineを付けて、インライン関数にする。インライン関数は、関数本体の処理と引数として受け取った関数本体を、呼び出し元に展開する。

Javaの新定石

JPA, EntityManager

JPAでの1件のレコードに対するCRUD操作は、EntityMangerを使用してエンティティインスタンスを操作するだけで完結する。
INSERTには、EntityManager.persist。
SELECTには、EntityManager.find。
DELETEには、EntityManager.remove。
UPDATEには、EntityManagerのメソッドを呼び出す必要はない。EntityManagerで取得したエンティティに対して値を変更すれば、自動的にUPDATEされる。

JPAには永続コンテキストという概念があり、EntityManagerを介して行われる一連の操作は、DBに対してというよりも永続コンテクストに対してのもの。EntityManagerは使用を終えるとflushされ、closeされるが、その際にDBに必要な更新がかかる。

CDI

JAX-RSやJPAといったそれぞれ別の仕様を使って実装されたクラスを、CDIの管理下に置くことでつなぐことができる。

UberJAR

warファイルとしてデプロイするのではなく、jarファイルとして実行できるようにする。Payara MicroやWildFly Swarmというプロダクトで実現できる。Spring BootでいうとことのFully Executable Jar。
jarファイルで実行可能にするメリットとして挙げられるものが、Infrastructure as Codeやマイクロサービスを簡単に実現できるようになるという点。複雑なインストールや設定なしにアプリケーションを実行できることが重要になっている。

WebRTCの現在と未来

WebRTC振り返り

WebRTCはブラウザ間で音声・ビデオ通話を実現する技術。Web標準技術でプラグイン依存なし。
P2P接続である。

データベースのバックアップとリストア

mysqldumpの問題点

SLAVEからバックアップを取得する場合、--master-dataオプションを使用しても実行しているSLAVEに対するSLAVE(つまりMASTERからみてSLAVEのさらにSLAVE)を作るためのバイナリログに関する情報しか取れない。
--dump-slaveオプションを使用すると、上記問題は解決するものの、バックアップ中はレプリケーションが停止することになるため、バックアップ頻度によっては次のバックアップまでにレプリケーション遅延が解消しない事態に陥る。

物理バックアップとXtraBackup

Percona XtraBackupはInnoDBのオンライン物理バックアップツール。クラッシュリカバリの仕組みを応用して、バックアップ期間中のデータ更新に対してもデータの一貫性を維持する仕組みになっている。トランザクションログは複数ファイルでローテーションしているため古い情報は消えてしまうが、XtraBackupはバックアップ期間中トランザクションログを監視して変更点を全て記録し、リストア時に全記録したトランザクションログを--apply-logオプションによりロールフォーワードさせている。

Pumaを使ってみよう

Ruby製のRails5標準サーバ。スレッドベース。

サーバの種類

プロセスベース

プロセスごとにリクエストを受け付ける。
最もメモリを使用する。
スロークライアント問題の影響を最も受けやすい。

スロークライアントに接続されてしまったプロセスはレスポンスが完了するまで他のクライアントの接続を受け付けられないため、nginx等のリバースプロキシを前面に立てるのが定石。リバースプロキシを立てた場合、サーバはスロークライアントと直接通信することが無くなる。
リバースプロキシはスロークライアントから受け取った内容をバッファに格納し、全て受け取ってからサーバに送信する。またサーバから受け取ったレスポンスもバッファ位に格納し、少しずつスロークライアントに送信する。

スレッドベース

プロセス内に複数のスレッドを作り、スレッドごとにリクエストを受け付ける。
メモリは少なめ。

イベント駆動

nginxやNode.jsで採用されている。
1プロセス1スレッド中で複数のリクエストを並行して受け付ける。
最もメモリを使用しない。
接続をブロックしないため、スロークライアント問題の影響を受けない。
イベント駆動の恩恵を受けるにはアプリのコードを含めて全てI/Oノンブロッキングで実行する必要がある。

-読書ノート