开发基础团队的目标是 #pixiv_night

当天,我们使用ESA的演示模式进行了展示。

(在福冈的pixiv之夜#02 – 一夜了解围绕pixiv的技术! – 这是connpass的演讲材料)

我是自己介绍。

    • 各種SNSをcatatsuyでやっている

かたついと呼ばれることが多い

ピクシブ株式会社で開発基盤チームと広告チームの兼任

2014年度新卒(2013/10入社)
pixivの技術的な改善が主な業務(後で詳しく)

単著『pixivエンジニアが教えるプログラミング入門(星海社新書)

ピクシブ社内の非エンジニア向けのプログラミング研修の書籍化

pixiv社内ISUCONやISUCON6本選の問題作成

Pixiv的团队分组

    • pixivというサービスは巨大

www.pixiv.net/touch.pixiv.net/スマートフォン用APIなどなど
提供しているサービスも多い

pixivという1つのサービスに対して複数のチームが存在

チーム分けは定期的に変わる
現在は投稿体験・web閲覧・アプリ閲覧・プレミアムなどで分けている

これらのチームはざっくり言うと『担当しているユーザー視点でのサービスを改善して伸ばす』のが目標

pixiv.git ->
pixiv的代码仓库

    • pixivのソースコードを管理しているgitリポジトリはpixiv.gitという1つのリポジトリ

 

    • pixiv.gitにはpixivだけではなく、社内の様々なサービスが入っている

sensei/pixivision/pixivFANBOX(pixivの一機能)などなど
複数のチームがpixiv.gitに対してコミットし、デプロイを行っている
共通して扱える処理はpixiv-libディレクトリ以下にまとめられている

ログイン用のaccounts.pixiv.net、OAuth、外部公開用のAPI、スマートフォン用APIなどもある

BOOTH/pixivFACTORY/pixivSketchなど社内のサービスでもOAuthやAPIで認証・pixivとユーザーデータなどのやり取りをしている
pixivコミックも別の仕組みでpixivとユーザーデータなどのやり取りをしている

サービスに直接関係せず、技術的な観点でpixiv.gitや、pixivというサービス、pixiv以外の社内の様々なサービスを改善するチームが必要

開発基盤チームの必要性

開發基礎建設團隊的目標是什麼?

    • 目標一例

機械学習
検索・おすすめ改善
pixivのHTTPS化
pixivが使用するミドルウェア刷新・PHPのバージョンアップ

Kyoto TycoonからRedisへの移行(後で詳しく)
PHP7化

開発効率の改善

ざっくり言うと技術的な観点でサービスを改善していくチーム

とはいえ開発基盤チームだけで全部やるには量が多すぎるので、一部のタスクは他のチームとも協力しながら分担している
インフラチームとの協力が不可欠な作業がかなり多いため、インフラチームとの関係を密にしている

開発基础构建团队的工作内容

    • 具体的で生々しい情報の方が面白いと思うので、実際に自分がやった(ている)ことを話します

 

    あくまでも仕事内容の一例です

KT停用的动向

    • pixivではキャッシュとしてKyoto Tycoon(以下KT)をずっと使っていた

memcachedプロトコルでマルチマスターレプリケーションが可能
オンメモリのmemcachedと違い、永続データを扱えるので再起動してもデータが消えない
pixivはキャッシュが消えるとまともに動作しない箇所がかなり多い(!)

キャッシュに頼りすぎるのはWebアプリケーションとして非常にまずい

とはいえやりがちな構成

pixiv自体が膨大な量のキャッシュに依存しているので今回修正は見送り
個人的にはいずれ開発基盤チームとしてキャッシュの使い方改善はやりたいタスクの1つ
現状の構成ではキャッシュをオンメモリのmemcachedに保存すると、memcachedが死んだときに長時間復旧が出来なくなる
pixivのキャッシュサーバーとしては永続データ・レプリケーションで複数台構成に出来ることが必須

KTがpixivの規模に耐えられなくなってきた

KT内部のGCに時間がかかる
KT内のデータが何故か壊れて、pixivが障害になることが何回かあった

KT自体のメンテナンスが停止しているため、修正される見込みがない
自分たちでメンテナンスするよりも、他のKVSに移行した方が未来がありそう

早急にKT以外のキャッシュサーバーに移行する必要性を認識

Memcached协议和PHP

    • memcachedプロトコルを扱うpeclモジュールはmemcachedとmemcacheの2つある

 

    • pixivではmemcacheモジュールを使ってきた

 

    • memcacheは2013-04-07から更新がない

PHP7に未対応

memcachedモジュールはPHP7に対応
PHP7化に向けてmemcacheモジュールを廃止する必要がある

memcachedモジュールへの単純な置き換えでは対応が出来ない
memcachedプロトコルはコマンド名・キー・flag・expire time・バイト数を指定する

 

flagの値はアプリケーションによって決められる

memcache・memcachedモジュールは圧縮形式などをflagの値から判断している
flagの対応に互換性が無いため、単純な置き換えでは正しく動作しない
並行して新しいキャッシュサーバーにもsetするようにして置き換えていくしかない
2度手間になるのでKTからの移行とmemcachedモジュールへの移行を同時にやりたい

从KT的迁移目的地

    • 当初はMySQL InnoDB memcached pluginが有力候補だった

memcachedプロトコルが使える
社内に大規模MySQL運用の知見があり、MySQLに統一できるのは運用上のメリットがある
レプリケーションやデータの永続化など求めている機能が全て揃っていた

パフォーマンス面・安定性などをインフラ部で検証

パフォーマンス面はMySQL5.7系を使い、my.cnfの設定次第で問題が無くなりそうだった
安定性についての問題が解決できず、MySQL InnoDB memcached pluginの導入を断念

Redisへの移行が決定

レプリケーション・永続データなど必要な機能が揃っている

プロトコルは独自で、単純なKVS以上に様々なデータ型やコマンドが使える

pixivのソースコードの変更量を考えると、memcachedプロトコルにこだわる理由が薄い

並列で2つのキャッシュサーバーにsetする必要があり、peclモジュールも変更する必要がある
今回Redis移行しても作業量として大差がないと判断
移るモジュールがphpredisかmemcachedかの違い

Redisは社内のRailsプロジェクトで採用事例が多く、検証の結果、パフォーマンス面・安定性にも問題が無かった

アプリケーションサーバーとKTのコネクションを減らすために使っていたtwemproxyはRedisにも対応していた
ただしtwemproxyで使えるRedisのコマンドには制限がある
memcachedプロトコルでも出来るレベルのことしかしないのでとりあえず問題なし

从KT迁移到Redis

    • pixivではキャッシュにしか存在しないユーザーデータが存在(!)

これはキャッシュではない

Redis移行の前に全てのユーザーデータをMySQLへ移行

pixivのデータストア/キャッシュ戦略 その2 – pixiv inside

古い記事だが、大筋は変わってない
KVSClientを通してキャッシュのキー名からsetするサーバーを指定する
KVSClient側でKTにsetする際にRedisにもsetするように
KVSClientを使わずにKTにsetするコードもあったので、getする際にKTとRedis両方からgetし、値が食い違っていたらログに流すように

fluentd経由でMongoDBにログを集めて、社内のログビューワーで見れるようなログ収集基盤があるのでそれを使用
ひたすら抜け漏れを探して修正

关于phpredis模块的Redis客户端的序列化问题。

$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);   // don't serialize data
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);    // use built-in serialize/unserialize
    • シリアライズオプションについて

デフォルトはRedis::SERIALIZER_NONE

全て文字列に変換されてSETされる
配列はArrayという文字列になる

Redis::SERIALIZER_PHP

数値1はi:1;というPHP組み込みのserialize関数の返り値が入る
文字列も同様にs:4:”abcd”;のようになる
getする際に自動でdeserialize関数を実行する

Redis::SERIALIZER_NONEとRedis::SERIALIZER_PHPを混ぜる

Redis::SERIALIZER_PHPでsetした後に、Redis::SERIALIZER_NONEでgetするとserializeの返り値の文字列が返ってくる
Redis::SERIALIZER_NONEでsetした後に、Redis::SERIALIZER_PHPでgetすると文字列として取得できる
誤作動するパターンは見つけられなかった

pixivでは配列(PHPなので連想配列を含む)をシリアライズせずにsetしている箇所が大量にあった

ライブラリ内部でset時にシリアライズ、get時にデシリアライズする挙動に頼っていた

pixivのコードではKVSClient経由でKTへのコネクションを使い回せるようになっている

PHPなので1リクエストの間で使い回される
シリアライズのオプションをリクエスト中に切り替えるのは予期しない事故につながりうる
常にRedis::SERIALIZER_PHPを使うことにした

发生的问题

    • memcachedとRedisにはインクリメントするコマンドがある

キーが存在しない場合の扱いが異なる
memcached

the item must already exist for incr/decr to work; these commands won’t pretend that a non-existent key exists with value 0; instead, they will fail.
既に存在するアイテムに対してしか使えない。存在しない場合、値に0が入っているキーとして振る舞うことはない。代わりに失敗する。
https://github.com/memcached/memcached/blob/master/doc/protocol.txt

redis

If the key does not exist, it is set to 0 before performing the operation.
キーが存在していなければ、そのオペレーションを実行する前に0をセットする

https://redis.io/commands/incr

これまでのコードはmemcachedプロトコルを意識していたため、インクリメントしたいキーで存在していない場合は0をsetするコードになっていた

Redis::SERIALIZER_PHPで0をsetするとi:0;になる
インクリメントできるのは数値として解釈できる文字列がvalueに入っている場合のみ、なのでi:0;はインクリメントできない
返り値はphpredisのドキュメントにはINTが返るとあるが、インクリメントに失敗したらfalseが返ってくる

接続に失敗するなど致命的な問題が起こった場合は例外が飛ぶ
特に例外が飛ぶわけではなかったので、動いていないことに気付くのが遅れた

pixivとピクシブ百科事典は別リポジトリだが、pixivからsetした値をピクシブ百科事典のリポジトリから参照していたため、動きを追うのが難しくなっていて、更に気付くのが遅れた

Redis::SERIALIZER_NONEにして0をsetすればインクリメントできるが、前述の理由から途中でシリアライズオプションを変更したくない

今は0をsetする場合はdelete、0以外の値をsetする場合はdeleteしてからincrByでその数値を指定している
事故防止のため、KVSClient側でインクリメント専用のキーにはincrementとdeleteしかできないように、インクリメントしないキーにはインクリメントできないように変更

KT的转型进展

    • 負荷が大きく、問題が起こっていたキャッシュ用途のKTはRedisに移行したが、セッションなどまだKTを使っている部分は多い

これから移行したい

将网站转化为HTTPS协议

    • 現在Redis化よりも、pixivのHTTPS化を最優先にしている

 

    • 近日中にtouchがフルHTTPS化する予定

www(PC版)はもう少し待ってください…

これについても様々な挑戦中

まだ途中なのでこれについては次の機会で!

最后

    • pixivというサービスは歴史があり、多くのユーザーを抱えるため、技術的に解決しなければならない問題も多い

今回紹介した事例はほんの一例です

技術面でサービスを支えることに興味を持ってもらえれば幸いです