Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
多重登録を防御するためのノウハウ
Search
Ryohei KOMATSUYAMA
May 15, 2024
Programming
0
130
多重登録を防御するためのノウハウ
2024/05/15【DMM×バイセル】若手エンジニアによる学生向けTech Study Summit
Ryohei KOMATSUYAMA
May 15, 2024
Tweet
Share
Other Decks in Programming
See All in Programming
What is TDD?
urakawa_jinsei
1
200
Flutterアプリを生成AIで生成する勘所
rizumita
0
250
RDBの世界をぬりかえていくモデルグラフDB〜truncus graphによるモデルファースト開発〜
jurabi
0
160
PHPを書く理由、PHPを書いていて良い理由 / Reasons to write PHP and why it is good to write PHP
seike460
PRO
5
350
自分だけの世界を創るクリエイティブコーディング / Creative Coding: Creating Your Own World
chobishiba
2
570
AWS CDKを用いたセキュアなCI/CDパイプラインの構築 / Build a secure CI/CD pipeline using AWS CDK
seike460
PRO
3
570
標準ライブラリの動向とイテレータのパフォーマンス
makki_d
3
190
◯◯エンジニアになった理由
gessy0129
PRO
0
620
"Swarming" をコンセプトに掲げるアジャイルチームのベストプラクティス
boykush
2
200
Progressive Web Apps for Rails developers
siaw23
2
530
M5Stack に色々な M5ユニットをつないで扱う為の新たなアプローチ
gob
0
200
ビット演算の話 / Let's play with bit operations
kaityo256
PRO
4
170
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
41
9.2k
What the flash - Photography Introduction
edds
67
11k
Statistics for Hackers
jakevdp
796
220k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
125
18k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
225
22k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9k
Designing Experiences People Love
moore
138
23k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
30
2.6k
Automating Front-end Workflow
addyosmani
1365
200k
The Invisible Side of Design
smashingmag
297
50k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
Teambox: Starting and Learning
jrom
131
8.7k
Transcript
多重登録を防御するためのノウハウ 2024/05/15 【DMM×バイセル】若手エンジニアによる学生向けTech Study Summit 小松山凌平
👓 小松山 凌平 👶 1998/09/09 生 (2021 年卒) 🏢 開発
1 部 Cosmos 基盤グループ 🧻 BE メインのエンジニア 🏀 NBA 観戦 🧌 Monster Hunter World 🙅 x.com/kmtym_dev zenn.dev/kmtym1998 2 自己紹介
多重登録をデータベースレベルで防御できると安心です API 実装の際の手札を増やしてもらいたいです (PostgreSQL を前提とした話をします) 3 今日のメッセージ
• 多重登録によるトラブル • 多重登録を防ぐためのアプローチ • まとめ 4 話すこと
• 多重登録によるトラブル • 多重登録を防ぐためのアプローチ • まとめ 5 話すこと
• とあるシステムのユーザ管理機能を例にする • ユーザが持つ情報 ◦ ユーザID ◦ 名前 ◦ メールアドレス
◦ ステータス (招待済 / 登録済 / 削除済 の 3 値をとる) • ユーザに関する制約 ◦ "招待済" "登録済" のユーザはメールアドレスが重複しない 6 説明のための例 (要件)
7 説明のための例 (テーブル定義) カラム名 カラムの型 制約 id serial PRIMARY KEY,
NOT NULL name text NOT NULL email text NOT NULL status enum NOT NULL • テーブル名: users • users.status の取り得る値は 'INVITED' / 'REGISTERED' / 'DELETED' の 3 値
8 • 招待者のダブルサブミットによって 招待済ユーザが 2 人できてしまった • 一応重複チェックはしていたつもり ◦ メールアドレスが一致
&& "削除済" でない ユーザがいるか検索 ◦ 検索して見つからなければ新規にレコード作成 • ダブルサブミットによってリクエストが 並列に処理され、ロジックをすり抜けた 多重登録によるトラブル エラーハンドリング等は省略しています
• 多重登録によるトラブル • 多重登録を防ぐためのアプローチ • まとめ 9 話すこと
10 多重登録を防ぐためのアプローチ • トランザクション分離レベルを変更する • テーブルごとロックをかける • 部分インデックスを使ってユニーク制約をつける
• トランザクション分離レベルを変更する • テーブルごとロックをかける • 部分インデックスを使ってユニーク制約をつける 11 多重登録を防ぐためのアプローチ
• 同時に複数のトランザクションが発生したときの挙動を決めるオプション • 標準 SQL で定められているものが以下の 4 つ (下に行くほど分離性が強い) ◦
リードアンコミッティド ◦ リードコミッティド (PostgreSQL のデフォルトはこれ) ◦ リピータブルリード (MySQL のデフォルトはこれ) ◦ シリアライザブル 12 トランザクション分離レベルとは・・・ PostgreSQLでの挙動
• 同時に複数のトランザクションが発生したときの挙動を決めるオプション • 標準 SQL で定められているものが以下の 4 つ (下に行くほど分離性が強い) ◦
リードアンコミッティド ◦ リードコミッティド (PostgreSQL のデフォルトはこれ) ◦ リピータブルリード (MySQL のデフォルトはこれ) ◦ シリアライザブル 13 トランザクション分離レベルとは・・・ PostgreSQLでの挙動
デフォルトの分離レベルがデータベース 単位で決まっている (変更可能) 14 トランザクション分離レベルの指定 トランザクション開始時に個別で設定も可能
シリアライザブル分離レベルは、最も厳しいトランザクションの分離性を提供します。このレベルでは トランザクションが同時にではなく、次から次へと、あたかも順に実行されているように逐次的な トランザクションの実行を全てのコミットされたトランザクションに対しエミュレートします。 PostgreSQL 15.4 文書 - 13.2 トランザクション分離 15
トランザクション分離レベル: シリアライザブル トランザクションの直列性を保証したいときに使う ※ ただし、相対的にパフォーマンスは悪くなる
16 TX開始 重複チェック ユーザ登録 コミット TX開始 重複チェック ユーザ登録 コミット 並列検出🔎
TX開始 重複チェック ユーザ登録 コミット TX開始 重複チェック ロールバック 成功 ✅ 成功 ✅ 重複検出🔎 直列なトランザクション 並列なトランザクション
17 多重登録を防ぐためのアプローチ • トランザクション分離レベルを変更する • テーブルごとロックをかける • 部分インデックスを使ってユニーク制約をつける
• 複数のトランザクションから同時に同じデータを 操作させないようにするための仕組み • トランザクション A がロックをかけているとき、 トランザクション B がロックをかけようとすると
A が解放されるまで B は待たされる • 重複チェックの前にロックをかける 18 テーブルロックとは・・・
19 別のトランザクションはロック解放を待つ TX開始 重複チェック ユーザの登録 コミット 成功 ✅ ロック 重複チェック
TX開始 ロック ロックの解放待ち 重複検出🔎 ※ 分離レベルはリードアンコミッティド・リードコミッティドを使う
20 • トランザクション分離レベルが高すぎると正しく重複チェックできない ◦ 並列して発生したトランザクションで作成されたレコードを読み取る必要がある ◦ リードアンコミッティド・リードコミッティドを使う • デッドロックに気を配る ◦
トランザクションが互いのロック解放を待つ状態となってしまい、いずれの処理も 止まってしまう状態 ◦ ロック時間の上限を設けておくなどして回避する • レコードロックでは重複登録は防げない 注意点
21 多重登録を防ぐためのアプローチ • トランザクション分離レベルを変更する • テーブルごとロックをかける • 部分インデックスを使ってユニーク制約をつける
22 部分インデックスとは・・・ 部分インデックスとは、テーブルの部分集合に構築されるインデックスです。 部分集合は、(部分イン デックスの述語と呼ばれる)条件式で定義されます。 部分インデックスには、その述語を満たすテーブ ル行のみに対するエントリが含まれます。 PostgreSQL 15.4 文書
- 11.8. 部分インデックス 普通のインデックス 部分インデックス ※ サポートがない RDBMS もある
23 部分インデックスのあるテーブルへの INSERT id status email 1 REGISTERED
[email protected]
2
REGISTERED
[email protected]
3 INVITED
[email protected]
4 INVITED
[email protected]
5 DELETED
[email protected]
id status email 1 REGISTERED
[email protected]
2 REGISTERED
[email protected]
3 INVITED
[email protected]
4 INVITED
[email protected]
5 DELETED
[email protected]
インデックスの範囲 インデックスの範囲 普通のインデックス 部分インデックス
インデックスの範囲 24 部分インデックスのあるテーブルへの INSERT id status email 1 REGISTERED
[email protected]
2 REGISTERED
[email protected]
3 INVITED
[email protected]
4 INVITED
[email protected]
5 DELETED
[email protected]
6 INVITED
[email protected]
id status email 1 REGISTERED
[email protected]
2 REGISTERED
[email protected]
3 INVITED
[email protected]
4 INVITED
[email protected]
5 DELETED
[email protected]
6 INVITED
[email protected]
インデックスの範囲 🙅 🙆 普通のインデックス 部分インデックス
25 部分インデックスのパフォーマンス (1)のグラフより、部分インデックスが定義されている条件であれば、通常のインデックスと部分イン デックスで実行速度に差がないことを確認できました。 (2)のグラフより、部分インデックスが定義されていない条件では、当然ですが通常のインデックスの 方が部分インデックスよりも高速に実行されます。 (※ B-Tree インデックスの場合) 株式会社インサイト
TECH BLOG - 部分インデックスの効果を検証(1)
26 話すこと • 多重登録によるトラブル • 多重登録を防ぐためのアプローチ • まとめ
• 忘れがちですが、多重登録はサーバ側で守れると安心です ◦ データベースレベルで防御できると堅牢になります • 重複チェックをするための方法を 3 つ紹介しました ◦ トランザクション分離レベルを上げる方法
◦ テーブルロックを使う方法 ◦ 部分インデックスを使う方法 • 同じような話を記事にしているので良ければご覧ください 27 まとめ
ありがとうございました