アウトプットは砕けない

21卒学生Webエンジニアのアプトプット

ISUCON9予選問題に挑戦する

isucon-logo

isucon-logo

GW中にちょいちょいISUCON9の予選問題に取り組んでみました。

 パフォーマンスチューニングとしてやったことを残しておきたいと思います。

 

環境

https://github.com/matsuu/vagrant-isucon

こちらを利用してvagrantでstandaloneな構成を構築しました。

実際の予選はマシン3台の構成で、おそらくスペックも異なると思うのですが、今回は1台でできる限りのことをやったという感じになります。

使用した言語はGoになります。

 

成果物

https://github.com/daleksprinter/isucon9-qualifier-golang

一応自分のリポジトリを貼っておきます。

 

やったこと(かなり他の人の記事とかを参考にしました。) 

1. getTransactionsのN+1潰し

pprofを用いてプロファイリングしたところ、getTransactionsが遅いようでした。

コードを読んでみると、4つくらいN+1が発生しているのがわかりました。

とりあえずItemとCategoryとUserをjoinしておこうと思って、SQLを修正しました。

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/7b59ff7e8a306215b01b00ca59f8b7b78b1ebb9e

書いている途中に嫌になりました。

 

2. Categoryを埋め込む

Categoryの更新が発生しないことに気がつき、メモリにキャッシュしようとしました。

けど解説なんかを読んでいると、コードに埋め込んだ方がいいことに気がつきます。

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/80dad1384cb2b90412dc73ccf7be16768481f02f

それに伴い、1の修正をきれいに改善します。

 

3. UNION句を用いた高速化

getTransactionsのWHERE(`seller_id`  = ? OR `buyer_id` = ?)という書き方だと、indexが効かないらしくて、修正しました。

SELECT * FROM items WHERE seller_id = ?

UNION

SELECT * FROM items WHERE buyer_id = ?

というように修正したら良いそうです。

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/e0c603688c1895311c36c56b9f356b0b052c29d8

 

4. Userをインメモリ化

sync.RWMutexを用いてMapを安全に処理します。

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/ba768ade6d8c16b5ff491fa5edc79371c5d41099

それに伴ってまたSQLも修正

 

参考 https://qiita.com/TsuyoshiUshio@github/items/c3234f3705949d8cf413

 

5. getTransactoins内の残りのN+1をなくす

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/9aca44a9732bcca2cd198e37104bbb8cdacf0378

 

6. 外部APIへの通信を並列、非同期処理

これがgetTransaction内の最後の修正です。

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/f807b53d0e6dd3a3117fe59c80927ee26a99c547

一番点が上がった改善でした。

 

マジでGoの並列処理勉強しないといけないなと思いました。 

Go言語による並行処理

Go言語による並行処理

  • 作者:Katherine Cox-Buday
  • 発売日: 2018/10/26
  • メディア: 単行本(ソフトカバー)
 

 ↑欲しい

 

7. postBuyの外部APIアクセスも非同期に

https://github.com/daleksprinter/isucon9-qualifier-golang/commit/7215f25e3afb1a72f012dca584d1224e0c7225bb

 

8. その他

Index貼ったり、インメモリ化した部分のSQLしました。

UserやCategoryをインメモリ化したので、全体のSQLも修正しようとしたのですが、

SELECT ~ FOR UPDATEになっているクエリを消して、syncでメモリ参照しようとするとベンチが失敗してしまいました。ロック周りの問題らしいのですが、よくわからず...

 

結果

最終的に7500点くらいになりました。

 

学び

・N+1の改善はjoinで結合して持ってくるだけでなくIN句を用いるとわりとすっきりするかも

・WHERE(`seller_id`  = ? OR `buyer_id` = ?)はIndexが効かない。(Indexについて要復習)

・並列、非同期処理の勉強が必要

 

感想

3台構成で続きやりたい。