ByteByteGoでななつのAPI性能向上の方法をYoutubeで投稿したのでここで簡単にまとめます。
1. Pagenation
大きいでーたは適当なサイズ(ページ)でわけて伝送します。Youtube見たいの動画を扱う場合には必ず使われている手法です。
2. Async Logging
システムはランタイム時にログを記録する必要があります。しかし、状況によってロギングするべきデータが大きいものであったり、小さいものであったりします。大きなものをロギングしているときに次のロギングがとまってしまったら…
これのためには、非同期のロギングが必要です。
3. N+1問題
クエリ1回でN件を取得したが、関連するカラムを取得するためにクエリをN回追加で実行する問題がN+1問題です。これはJOINなどを使用したクエリの改善によって可能です。
問題状況
$books = query_rows("SELECT * FROM books");
foreach( $books as &$book ) {
$book['author_name'] = query_one("SELECT name FROM
authors WHERE id=?", $book['author_id']);
}
上記のコードは、一つのidに対応するauthorを見つけるために、booksのレコード数だけループを回して探します。
これはJOINを使用して解決できます。(クエリ数は1回)
$books = query_rows("SELECT b.*, a.author_name
FROM books b LEFT JOIN authors a ON b.author_id=a.id");
イーガーローディングを行うと、クエリ数を1+1回に減らすことができます。
SELECT * FROM books;
SELECT * FROM authors WHERE id=1;
SELECT * FROM authors WHERE id=2;
SELECT * FROM authors WHERE id=3;
...
を
SELECT * FROM books;
SELECT * FROM authors WHERE id IN (1, 2, 3, ...);
にします。
4. caching
これは、DBを経由する前に、RedisなどのメモリDBでキャッシュを作成しておく方法です。
if redis_cache.contains(query):
return redis_cache.get(query)
else:
result = backend_database.query(query)
redis_cache.put(query, result)
return result
5. Connection Pool
DBコネクションをあらかじめ複数作成し、プールとして管理します。コネクションが発生する度に直ちに対応します。
代表的なCPにはHikari CPがあります。Hikari CPでの推奨プールサイズは
$T_n \times (C_n-1) + 1$
(ここで、$T_n$は全スレッド数、$C_n$は一つのリクエストで同時に必要なコネクション数を示します)
でもこの数値は探しにくいなので以下の公式がよく使われています。
(core_count×2) + effective_spindle_count
(core_countはCPUのコアの数、effective_spindle_countはハードディスクの数)
6. Payload Compression
データの送信時にデータを圧縮して送信する技術を指します。
圧縮の方法はgzipもあるんですが最近はBrotli方法がもっと効率がいいのでよく使われてます。
7. Using proper JSON Serialization Library
JSONデータの転送時に適切なパフォーマンスの直列化ライブラリを使用すると、転送負荷を大幅に軽減することができます。
言語に適した最適化されたライブラリを使用すれば良いでしょう。Googleのprotobufは言語が提供する内蔵ライブラリよりも性能は良いですが、使用するのが複雑であるという欠点があります。