Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Update Billion Records
Search
ta1kt0me
October 27, 2023
Programming
5.5k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Update Billion Records
Kaigi on Rails 2023
ta1kt0me
October 27, 2023
More Decks by ta1kt0me
See All by ta1kt0me
過去の改善から考える オブザーバビリティの必要性
ta1kt0me
0
72
Running with version up
ta1kt0me
0
86
omotesandorb_8.pdf
ta1kt0me
1
440
Rubyでのプロファイリング
ta1kt0me
0
140
開発環境でdockerを使ってみた
ta1kt0me
0
470
Other Decks in Programming
See All in Programming
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
速習iPhone Duo対応
yuukiw00w
2
990
Workers Cache を知る
syumai
0
360
contenteditable と日本語入力に向き合う
colorful12
0
200
PumaからFalconに移行したRailsアプリケーションでの実践
falcon8823
0
160
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
490
Simple Storage Service(S3) is not simple
iwatsukayura
0
290
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
0
350
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
240
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
390
AmebaブログはKMPから卒業します
kmars0605
0
160
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
270
Featured
See All Featured
The Spectacular Lies of Maps
axbom
PRO
1
1.1k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Ruling the World: When Life Gets Gamed
codingconduct
0
400
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
450
Designing for Timeless Needs
cassininazir
1
520
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.9k
Believing is Seeing
oripsolob
1
240
Abbi's Birthday
coloredviolet
4
10k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
630
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
58k
Transcript
Update Billion Records Kaigi on Rails 2023
Kaigi on Rails 2023 開催 🎉
• ⾃⼰紹介 • 課題と前提 • レコード更新チャレンジ • 学び 写真・画像 Agenda
写真・画像 Hiroki Tokutomi 株式会社TimeTree • CTO室所属、Backendチーム/SREチーム https://twitter.com/talkto_me https://github.com/ta1kt0me ⾃⼰紹介
写真・画像 • 簡単に予定を共有できる • カレンダーの中で気軽に相談できる スマホの中で⾒れる壁掛けカレンダー カレンダーシェアアプリ
None
begin
写真・画像 課題と前提
きっかけ IUUQTUJNFUSFFBQQDPNJOUMKBOFXTSPPNTIBSFEFWFOUDMPTF
👦 < ちょっとモデルの関連キー⾒直したいんだよね!
👦 < 新キーのカラムは追加してるよ!数⼗億件あるけど! 👦 < ちょっとモデルの関連キー⾒直したいんだよね!
👦 < 新キーのカラムは追加してるよ!数⼗億件あるけど! 👦 < ちょっとモデルの関連キー⾒直したいんだよね! 👦 < 少し前にも全件更新したし、いけるいける!
👦 < 新キーのカラムは追加してるよ!数⼗億件あるけど! 👦 < ちょっとモデルの関連キー⾒直したいんだよね! 👦 < 少し前にも全件更新したし、いけるいける!
整理してみる • 解消する価値のある技術的負債 • 対象は1テーブル、レコード数は50~60億 • 変更内容はデータを埋めるだけ • 作業の1年前に⼤体数⼗⽇かけて同じテーブルの全データ更新を実施 •
更新作業に時間がかかることはチームで認識している • 更新期間も安定してサービスを提供したい 要求
• Monolith Ruby on Rails • Sidekiq • AWS •
CloudFront • S3 • ECS • Aurora MySQL • DynamoDB • Elasticache for Redis • etc … Backend構成要素
以前はどうやってたの? 複数プロセス起動して並列に更新する • rails runner • grosser/parallel • zdennis/activerecord-import ⼤量データを⼀括で更新するバッチ処理でよく⾒かけるパターン
巨⼤テーブルでなければこのアプローチを使うことが多い • 更新対象の from/to • parallelで起動するプロセスの並列数
写真・画像 レコード 更新チャレンジ
Take 1. 前例踏襲
過去のアプローチに従ってみる • 複数プロセス更新での実⾏時間を計測したい • アプローチの課題を理解したい
結果 • 実⾏時間は約70分/1,000万件 • サービスへの影響はなかった • この時点で⼤きめのパフォーマンスチューニングはしていない 対象は⼤体500~600倍、実⾏し続ければ1ヶ⽉弱…?
⾒えてきた課題 更新対象の指定で処理時間のブレが⼤きい サービス成⻑に起因(後述) 更新処理をコントロールしづらい 実⾏状況を外部からモニタリングしづらい ⻑時間実⾏し続けることが難しい、けど⻑時間実⾏し続けたい • 環境の都合上リリースの度に中断 • スパイクが予測される状況で中断
課題を深掘り 更新対象の指定 • 処理対象のfrom/to にテーブルのidカラム(PK)の値を指定 • Idに Snow fl ake
ID を利⽤ • 64ビットの整数値、timestampに基づいて⽣成される時系列ソート済みのID • Twitter の TweetのIDで利⽤されていた • TimeTreeの場合、timestampにcreated_atを使っている • 1億件分更新したい場合、⼤体1億件分の期間の from, to をこのフォーマットに変換して指定 + ————— —————— ——— ——— — —— — —— — —— ——— — —— ——— — —— — —+ | timestamp (41ビット) | ゾーンID (10ビット) | シーケンス番号 (13ビット) | + ————— —————— ——— ——— — —— — —— — —— ——— — —— ——— — —— — —+ 例)timestampに “2020-01-01 00:00:00.000 UTC”、ゾーンIDに”10”、シーケンス番号に”1”を指定 10110111101011110011001101110100000000000 0000001010 0000000000001 => 13235854403174481921
更新期間の指定のムラ
改善アイデア 更新対象の指定で処理時間のブレが⼤きい from/toを⼀定の期間で分割、分割期間ごとに並列で処理する 更新処理をコントロールしづらい 更新処理をSidekiq Workerで処理する⾮同期ジョブにする • rails runner の実⾏環境の制限に依存しない
• 実⾏状況のモニタリングも容易
期間の分割⾒直し Ұఆͷظؒ5ͰJEΛׂ ىಈ#BUDIͷύϥϝʔλͱͯ͠ࢦఆՄೳ ظؒ5ΛฒྻͰॲཧ Take 1 改善案 ฒྻͰJEͷൣғΛׂ
Take 2.
Take 2 アイデア • 更新対象の from/to • 分割期間
Take 2 アイデアちょっと待って • 更新対象の from/to • 分割期間
⾮同期で更新 前提 • 通常のワークロードより優先度低 • データベースの負荷を抑えたい • 全件更新に時間がかかることは問題ない 制御したいこと •
workerあたりの実⾏時間 • Sidekiqクラスター全体での更新Job数 • 更新処理を全て停⽌するトリガー
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
②from/toの分割リストをRedisにpush ④更新対象のfrom/toを pop、なければ終了 ⑤更新 ③⾮同期Jobを登録 ⑥⾮同期Jobを登録
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
②from/toの分割リストをRedisにpush ③⾮同期Jobを登録
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
④更新対象のfrom/toをpop( ) ③⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
④更新対象のfrom/toをpop ⑤更新 ③⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑤更新 ⑥⾮同期Jobを登録 ③⾮同期Jobを登録 ④更新対象のfrom/toをpop ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑤更新 ⑥⾮同期Jobを登録 ③⾮同期Jobを登録 ④更新対象のfrom/toをpop ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑦更新対象のfrom/toをpop( ) ⑤更新 ③⾮同期Jobを登録 ⑥⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑦更新対象のfrom/toをpopできない ⑤更新 ③⾮同期Jobを登録 ⑥⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑧分割リストのデータを削除 ⑤更新 ③⾮同期Jobを登録 ⑥⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
全体像 ①rails runnerで起動処理実⾏ • 更新対象の from/to • 分割期間 • Jobの同時起動数
⑧分割リストのデータを削除 ⑤更新 ③⾮同期Jobを登録 ⑥⾮同期Jobを登録 ②from/toの分割リストをRedisにpush
コンセプト: 起動処理
コンセプト: 更新ジョブ
結果 実⾏時間 実⾏時間は約40分/1,000万件(変更前の175%⾼速化) 実⾏し続ければ約2~3週間強 Take.1 の課題 実⾏中もリリース可能、問題があれば全処理停⽌可能 カスタマイズせず、Sidekiqの管理画⾯やAPMのメトリクスで実⾏状況を確認できる Workerごとの実⾏時間もパラメータによりある程度調整可能
早くなった、楽できた、はっぴー🎃
やりきった 🎉
写真・画像 学び
学び ⼤量データの変更には時間がかかる • ⻑期戦になるので関係者の理解が必要 • ⼩さなデータで簡単なことでも、後に回せば回すほどもっと⾟くなる ⼩さく試して進める • PoCのフィードバックサイクルを素早く回す •
可能であれば⼩さいデータセットから始め、少しずつ⼤きくして問題を捉えていく 選択肢を広げるためのinputの⼤切さ • 改善のヒントは様々な場所に潜んでいるので広く、深く探す • 前例や背景、変更対象や関連のドメインの把握、制限やトレードオフの理解、少数の異常データの存在
他の全件更新に適⽤できるか? 🙅 or 🙆 • 🙆 適⽤できたケースもある • 😰 変更対象が今回と同じでもさらにデータが増え続けたら…
• 🙅 READよりもWRITE(更新)が多いケースはLockが多発して使えなかった • 🧐 変更内容や対象の特性次第で前例に捉われずに考え続ける
end
⼀緒に最⾼のカレンダーサービス作りませんか?