😵
個人的DynamoDBバッドプラクティス
はじめに
- Amazon DynamoDBを使う上でやってはいけないバッドプラクティス
- いちおう、その後の改善策も書きます(ベストとは言えないまでもグッドレベルだと信じたい)
- 自分が初めて使ったときにやらかしたことのまとめ
前提
- 基本的に dynamodb-local を利用していますが、AWS上でも同じだと思います
テーブル設計
こまかくテーブルを分割してしまう
- ベストプラクティスは1アプリ1テーブルとのこと
経緯
- とあるセンサデータを蓄積する必要があり、センサのデバイスIDと日付をテーブル名にして、毎日テーブルを生成していました
- 定期的にデータのscanを実施する必要があったため、テーブルサイズが小さい方がいいだろうという判断から
結果
- テスト時や稼働当初は問題なかったのですが、稼働34日目にXデーがやってきました
- 1日3テーブル作成を34日続けた結果、テーブル数が100を超えてしまい boto3 の list_tables関数 を使ったテーブルのチェック処理が正常に機能しなくなりました
-
The output from ListTables is paginated, with each page returning a maximum of 100 table names.
と記載のとおり一度に100テーブルしか返却されません - 上記を受けて100テーブル以上を考慮したテーブルの存在確認関数は以下のとおり(clientは取得済みの前提)
def check_table(self, table_name): # テーブル一覧取得 table_list = self.client.list_tables() # 存在する場合 if table_name in table_list.get('TableNames'): return True # list_tables()が一度に100テーブルまでしか取得できないのでループ while table_list.get('LastEvaluatedTableName') is not None: last_value = table_list.get('LastEvaluatedTableName') table_list = self.client.list_tables(ExclusiveStartTableName=last_value) if table_name in table_list.get('TableNames'): return True # テーブル存在せず return False
-
対応
- 1テーブルにまとめるようにしました
- キー設計については下記
キー設計
クエリ実行対象をパーティションキーにしてしまう
経緯
- 経緯は上記と同じです
- センサからくるデータにIDが付与されていたため、そちらをパーティションキーにしてscan するようにしていました
結果
- パーティションキーが異なるとハッシュ値が異なり、保存場所も違ってしまいます
- そのため、検索性能も出ていませんでした
対応
- パーティションキーに デバイスID+日付 を設定
- ソートキーにデータIDを設定
- これにより定期的なその日のデータのscanが劇的に改善しました
- パーティションキーすなわち検索対象のデータの保存場所がまとまっており、各データがソートされているので当然ですね
データ取得
scanをせずにデータを1個ずつ取得しforループしてチェックしてしまう
- これは絶対に真似してはいけない悪手です
経緯
- RDBと異なりdynamodbではscanなどのクエリが遅いと聞いていたため
結果
- python の forループの遅さと相まって激遅です
- scanでの抽出対象が多い場合は、たしかに遅いのですが自前のforループよりはマシです
- 自分の環境やアプリの構成ではscanよりも50~100倍くらいの時間がかかっていました
対応
-
FilterExpression
を利用してscanするようにしました
データ保存
大きなデータを保存してしまう
経緯
- 画像データの保持も必要だったのと、dynamodbでは1アイテム400KBまで 保存できるとのことだったので、100KBほどの画像も一緒に保存していました
結果
- (当たり前ですが)アクセス速度が遅くなります
- get_itemもいまいちですが特にscanの性能がかなり悪化します
対応
- 画像はs3に保存して、そのパスをdynamodbに保存しました
浮動小数点型を保存してしまう
経緯
- センサデータなので小数点以下もあったので、取得したものをそのまま保存していました
結果
- dynamodbでは浮動小数点型をそのまま保存できず、一旦decimal型にするためかアクセス性能が(ほんの少し)悪化します
対応
- 小数点以下の精度はそこまで必要でなかったので、10倍や100倍して整数型として保存しました
あとがき
- AWS上の場合はテーブル作成もかなり気を使うと思うのですが、dynamodb-localを使っていたこともあり気軽にアプリからテーブル作成をしてしまってイマイチな使い方をしていました
- 業務ではdynamodbよりもRDBをよく使うのですが、個人的にはdynamodbの方が好きなのでもっと正しく使っていきたいと思います(また何かやらかしたら追記します)
Discussion