アーカイブ | 08:11

2種類のLINQ

9 12月

LINQとは何?みたいな話はすっとばしますよ♪

 

今日のテーマはIEnumerableとIQueryableの使い分けです。

IEnumerableとIQueryable

LINQ、つまりC#(やVB)のデータ処理の方法として、大まかにいうと以下のような2つの方式があります。

  • 内部加工方式: データ全件もらって、プログラム中で加工
  • 外部クエリ方式: クエリを投げて、外部のサーバー上で処理してもらって、結果だけ受け取る

C#的にいうと、LINQを、IEnumerableを使って書く(内部加工)か、IQueryableを使って書く(外部クエリ)かの差です。

ほとんどの場合、性能のネックになるのは通信部分です。なので、「全件返してもらう」なんてことはめったにしません。

というか、データを持っているサーバーが、いわゆるリレーショナル データベースのように、高度なクエリを受け付けてくれる場合は迷わず外部クエリ方式(IQueryable)を使います。

ハイブリッド

さて問題は、そんな高機能なサーバーばかりではないということです。

まあ、たとえばAzure Tableなんかだと、WhereTakeくらいしか使えなかったり。

となると、WhereTakeだけは外部クエリにして、残りのSelectSkipは内部加工で行うというようなやり方も考えないといけません。

例えば以下のコードを見てみましょう。いわゆる、ページング用のクエリです(アイテム情報は既にプログラム中にキャッシュを持っていて、IDだけデータベースから引ければOKなものとして)。

// あるユーザーの持っているアイテム一覧を取得する。例として、ID: 1 のユーザー。
var items = inventory
    .Where(x => x.UserId == 1)
    .Select(x => x.ItemId);
// 
一気には表示しないので、10個区切りで表示。例として、3ページ目。
var page = items.Skip(20).Take(10);

 

前述の通り、相手がリレーショナル データベースなら素直に全部外部クエリを投げます。

一方、WhereTakeしか受け付けてくれないAzure Tableの場合、以下のように、間にAsEnumerableをはさみます。それよりも前は外部クエリに、後ろは内部加工になります。

くれぐれも、AsEnumerableの挿入位置には注意してください。それ以降は内部加工になります。前の方に持ってきすぎると、通信量が増えて、性能を落とします。

ちなみに、TakeSkipが逆順になっているので、Takeで読み取る個数も変えないといけないのにも注意。つまり、後ろの方のページほど、読み取ってしまうデータが増えます(つまりは、幾分かマシになる程度で、やっぱり通信量的に問題になる可能性が残ります。この点については補足にて改めて)。

補足

説明を簡素化するために、いくつか端折ったところがあるので、最後に補足を。

PartitionKeyとRowKey

上記の例ではUserIdで条件選択していますが、Azure Tableの場合、PartitionKeyかRowKey以外での条件選択はあまり性能が良くないです。UserIdでの検索が頻繁なら、UserIdをこれらのキーとひもづけておきましょう。

非同期処理

データベースへのアクセスは、大半が通信の待ち時間になります。

上記の例のようなコードでは、同期(通信が終わるまでスレッドを止めてしまう)実行になり、あまり性能がでません。ちゃんと、BeginExecuteメソッドを使って非同期処理しましょう。

明日、改めて、データ アクセスの非同期化の話をしようと思っています。

継続トークン

Azure Tableに対してページングをしたい場合、継続トークンを使って読み込んで、ローカルにデータをキャッシュしておくような作りにするのが正しいかも。

継続トークンというのは、前回のクエリの続きからデータを読むための仕組みです。詳しくは、BeginExecuteSegmentedメソッドで検索していただけるとそれっぽい例が出てくると思います。

SQL Azure

まあ、めんどくさいから、履歴系(巨大データになりがち)を除いて全部SQL Azureに載せてしまえという説もあり。もちろん、要件しだいですが。

まとめ

ここ数年、大規模アクセスに備えるため、高機能なリレーショナル データベースから、ある意味退化ともいえる(低機能な代わりに、やり方次第では(分散させやすく)性能を上げやすい)Key-Valueストアのようなデータベース(Azure Tableもその1種です)の利用が増えています。

Key-Valueストアのようなデータベースでは、書けるクエリに制限があるため、外部クエリと内部加工のハイブリッドな処理が必要になります。

  • C#なら、AsEnumerableを付けるだけで一発切り替え
  • ただし、AsEnumerableを付ける位置にはくれぐれも気を付けましょう