Blog ブログ

へっぽこプログラマの奮闘記 ~第4回:EntityFrameworkで大量データを取りたいんだけど…~

どうもこんにちは。プログラマの田中智大です。
3月にもなり、朝起きるときに布団から出たくないレベルの寒さは徐々になくなってきたところから、春が近づいているのを感じます。

今回はSQLDatabase から大量のレコードを EntityFramwork を使って取得した時に、どんな事が起きるかについて説明したいと思います。

わかりやすくするため、実際に値を置いて説明していきましょう。
DBには 100万のレコードがあり、何回か全データを使う事があるため、何度もDBから全件取得をしないように、ToList()を使ってそれらを全件取得します。
1つのレコードはだいたい 300バイトほどのデータがある場合、メモリは 300MB ほどしか使わないから大丈夫だと思われるでしょうが、
いざ EntityFramework でデータを取得したところ、メモリが 1GB近くも使われている事が分かりました。

◆どうしてこうなったの?
大量にメモリが使われてしまった理由は「EntityFramework が DBからデータをとってくるときの挙動」にありました。
以前、EntityFramework がどうやってDBを更新するかについて書きました。

 へっぽこプログラマの奮闘記 ~第1回:データベースが更新されません~

EntityFramework はサーバーのデータを持ってくる際に、Added(追加)、Unchanged(変更なし)、Modified(変更あり)等の状態をDbContextに保持していましたね。実はこの状態と、さらにクエリの結果データを変更前と変更後を判定するために2セットもキャッシュします。これらのキャッシュデータがメモリをあんなに圧迫していたんです。
これらの情報はデータを更新して SaveChanges() が呼ばれた時にサーバーの値を変えるために必要な情報なのですが、今回はデータを持ってきてどんなデータがどれだけあるかだけが知りたいので、このキャッシュは不要ですね。

◆どうすればいいの?
ここで、EntityFramework の AsNoTracking() という関数を使いましょう。
AsNoTracking()はドキュメントによると「DBから取得したエンティティが DbContext または ObjectContextにキャッシュされないクエリを返す」とあります。
このキャッシュとは、上で説明したキャッシュの事を指します。なので、AsNoTracking() を使うと、そのキャッシュを保持しなくなるため大幅にメモリを削減することができるんです。
(なお、Tracking という英単語には「追跡する」という意味があります。データの変更を追跡しないということでAsNoTrackingという名前なんです)

以下がサンプルのコードです。DbSetのプロパティに対してAsNoTracking()を挟む!たったこれだけで不要なキャッシュを行わないようにできちゃいます。

◆おわりに
今回 AsNoTracking() を使った事により、メモリの使用量を大幅に削減することができました。なお、今回は100万件のデータでお話しましたが、これがもし1000万件以上のデータを扱うような話になってくるとこの関数の使用は必須レベルです。
EntityFramework でを読み取るだけの目的で大量のデータを使いたい時は、AsNoTracking() を使う事を忘れないでくださいね。


採用情報

クラウドクリエイティブスタジオではエンジニアの方を絶賛募集中です。
一緒に面白いゲームを作っていきましょう!

採用情報