😵

個人的DynamoDBバッドプラクティス

2021/10/24に公開

はじめに

  • 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