この記事は何?
私がエンジニア2年目の時に「これ実装して」と頼まれた時の思い出です
何を実装したの?
下記の要件を満たす新機能を実装することになりました
- 最大5個のテキストカードをDnDで並べ替えができる(最大枚数は今後増える
- 並べ替えは複数人同時に操作ができる
- 更新頻度は高め(1日に数回は行う)
- 文字数は100文字まで(ここはそんなに重要ではない)
私は上記機能のDB設計とバックエンドの実装を担当することになりました。
私「こんなん余裕やん!」
DB設計 ver1
テキストカードの情報を保存するためのテーブルをまずは設計してみました
cardsテーブル
| カラム名 | データ型 |
|---|---|
| id | unsigned int |
| text | varchar(100) |
| order_index | int |
id, textを持ったレコードにorder_indexに0~4の値を持たせて、order byすれば順序も保存できるやないか!
と、思っていましたがアウトでした。
何がダメだったのか
order_indexを0~4の値にしてしまったことです。
この状態で末尾のCard5を先頭に持ってくると
Card5: order_index 4→0 にUpdate
Card1: order_index 0→1 にUpdate
Card2: order_index 1→2 にUpdate
Card3: order_index 2→3 にUpdate
Card4: order_index 3→4 にUpdate
と全てのカードをUpdateしなくてはなりません。
複数人が高頻度で並べ替えを行うので毎回毎回複数レコードの更新が走るのは設計上良くないです(下手するとデッドロックが起きるかも)
先人を調査
では順序(order_index)をどう持ったらいいのか?
DnDでアイテムを並び替えるシステムを思い浮かべたとき、カンバン形式のタスク管理システムが真っ先に思い浮かびました。
そこで、trelloさんのタスク管理ボードを調査した結果、順序はposというプロパティをfloatで持っていました。なるほど〜!!
DB設計 ver2
cardsテーブル
| カラム名 | データ型 |
|---|---|
| id | unsigned int |
| text | varchar(100) |
| pos | float |
posについては、0~4ではなく、1024〜5120で値を持たせるようにしました。
Card間のposに一定の数値間隔を空けることでUpdateコストを下げることができます。
- 末尾のカード(Card5)を3番目に移動
- 元々2番目(Card2)と3番目(Card3)のposの平均値を計算
- Card5のposを2.の平均値に設定
こうすることで更新するのはCard5のposのみになりました!🎉
ただし、平均値を取り続けるといつか隣のカードとposの値が同値になってしまうので、その時は1024などの数値で再採番してあげれば問題なしです!(これが面倒なのですが
終わりに
複数人同時&高頻度でなければver1の設計でも問題ないかと思いますが、DBやロジックを考える際は更新コストも考えないと。。。と学ぶ良い機会になりました💦
最後まで読んでいただきありがとうございました!
参考
こちらに他の手法含めて色々解説されていました!(感謝🙌


