RDB脳の自分がNoSQLを実務で使った所感等をつらつら書いてみる

この記事は [ミライトデザイン Advent Calendar 2021](https://qiita.com/advent-calendar/2021/miraito-inc) の 24日目です。

23日目は @yyykms123 さんの [Serverless NEGを使用して、GAEにIPv4の固定IPアドレスでIP制限をする](https://yukimasablog.com/gae-ip-restriction) でした。

GAEでの事例ですが最近はIPv6化も進んできているので、知っておくと役に立ちそうな記事です。

# 初めに

自分がNoSQLという言葉と概念を初めて知ったのは多分7～8年前ぐらいだと思います。
当時は「NoSQL！そういうのもあるのか」という感じで、なんとなく知っているぐらいの状態でした。
ここ最近は実務でNoSQLを使った案件に携わっているので、自分が感じた事などを雑多に書きます。

RDBとNoSQLでは自分が思っていた以上に違いが大きいので、これからNoSQLを触る方のなにかの参考になれば良いなと思います。

## 簡単なRDB経歴

もう20年以上前になりますが、自分が新人研修で初めてデータベースという物に触れたのはOracleのPro*Cでした。
研修課題として書籍管理アプリを作ることになって、テーブル結合や副問合せ、集計やら集合等の概念を理解するのに苦労したのも懐かしい思い出です。
その後も色々な案件を渡り歩いて、IBM DB2,SQL Server,MySQL,PostgreSQL等の有名所に出会いつつずっとRDBを使っていました。

## NoSQL経歴

数年前から携わる事になった案件で初めてNoSQLであるDynamoDBを使うことになりました。
この案件ではDB設計の段階から参加しました。

また、今年の夏ぐらいから掛け持ちで参加している案件ではMongoDBが使用されていました。
こちらはすでに設計と製造がある程度されている段階で途中からの参加でした。

どちらの案件もRDBの併用はしておらず、全てNoSQLだけでバックエンドのデータを管理しています。
ここ数年RDBを使う機会がまったく無いので、SQLの書き方を忘れてそうです。

## 実務で使った所感

### 全体的に

自戒の念を込めて言うと、両方の案件共にRDB的なテーブル構成となっていてNoSQL的に正しい使い方が出来ていないというのが実情です。
そのため、開発や運用で度々ツライ目に遭遇しています。

色々な要因があるのですが、いくつか箇条書きすると

- RDB的なテーブル結合が可能な前提で作られたデータ定義書から設計されている
- 実装側がスキーマレス(各レコードでカラムが異なる場合がある)なことを意識出来ずに項目がある前提の作りをしてしまう
- 運用・調査時にRDB的に自由なクエリ検索をしたくても出来ない
- パフォーマンス・チューニングの勘所がわからず大量データの場合の検索処理が遅い

みたいな事があります。

NoSQLでのベストプラクティスに従った設計や運用にしていれば防げる事ですが、知見が少なくRDB的な考え方で使って痛い目に合うパターンが多いです。
事前調査はしていてもその時は正しく理解出来てなくて、痛い目にあってから本当の意図に気が付くという事が多々ありました。

また、NoSQLの強みは「スケールアウトが簡単」「大量のリクエストを捌ける」「リアルタイム性が高い」等があると思います。
実務の案件で実際にこの点が必要だったかと問われると少し疑問が残るのが正直な所です。

### DynamoDB

まさにNoSQLといった感じでRDBとは別物です。

検索条件として最大で2項目(パーティションキー、ソートキー)しか指定するが出来ません。
[複合ソートキー](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-sort-keys.html) を使う等設計時点で考慮してないとツライです。

filterを使うことで取得結果を絞ることは出来ますが、フルスキャンした上で対象外の物を除外する挙動となるのでパフォーマンスは悪くなります。
また、任意項目(ソートキー以外)でのソートが出来ず、結合や集計関数等もサポートされていません。

公式の[ベストプラクティス](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-general-nosql-design.html#bp-general-nosql-design-concepts)には下記のように書かれています。

> - 対照的に、DynamoDB の場合は答えが必要な質問が分かるまで、スキーマの設計を開始すべきではありません。ビジネス上の問題とアプリケーションのユースケースを理解することが不可欠です。
> - DynamoDB アプリケーションではできるだけ少ないテーブルを維持する必要があります。

この部分は自分も最初に読みましたが、痛い目にあってから本当の意図に気が付きました。
RDB的な考え方で後から条件(対象項目)を変えて検索を行うということがとても難しいです。

### MongoDB

DynamoDBに比べるとRDBに近い使い方が出来るので、こちらの方が自分は扱いやすかったです。

検索でテーブル結合することが可能で、MongoShellではJavaScriptを使って処理を行うことが出来ます。
ちょっとニュアンスが違いますがストアドプロシージャ的な使い方が出来る感じです。

例として特定の条件に一致する対象を調べつつ更新したい場合、下記のようにJavaScriptでの処理を記載してMongoShellで実行出来ます。

```
var matchCnt = 0;
db.商品.find({種別: 'PC用品'}).forEach(
  function(e){
    var itemName = e.詳細情報.商品名;
    var sale = db.特売情報.findOne({targetName: itemName});
    if (sale == null) { return; }

    var match = db.クーポン.find({saleCode: sale.code}).forEach(
      function(me) {
        if (itemName == me.target.name) {
          print('match:' + e._id + ',' + itemName + ',' + me._id + ',' + me.クーボン番号);
          matchCnt++;
          me.disabled = true;
          db.クーポン.save(me);
        }
      }
    )
  }
)
print('matchCnt:' + matchCnt);
```

慣れが必要ですがJavaScrptで処理が書けるのでSQLよりも色々と出来て調査やメンテナンスには便利でした。

# 終わりに

使い所を間違わなければNoSQLは良いものだと思います。
ただ、RDBが提供している柔軟な検索・集計機能、トランザクションや整合性といった機能をNoSQLに期待するのは難しいです。

正直な所今の案件でのNoSQLの使い方は間違った世界線に進んでしまっていると感じています。
全てをNoSQLでやろうとして無理が出ている状態なので、役割分担を考えて必要な部分のみNoSQLにしてRDBと併用するのが良かったのでは無いかと思っています。
(誰か電子レンジでメールを過去の俺に飛ばしてくれ……)


明日は @hirodragon さんのスクラムに関する記事になります。
アドベントカレンダーの締めとなる記事なので、どんな内容になるのか楽しみにしています。