40

私のチームは、ServiceStack.net Redis Client を介して Redis と連携することを決定しました。これは、私たちが取り組んでいる新しい大量の Web サイトの基盤となるリポジトリです。この質問のドキュメントを探す場所がよくわかりません(一般的なRedisドキュメントまたは特定のServiceStack.Netドキュメント、またはその両方のいずれか)-RedisをServiceStack.Net経由で実装する方法に関するドキュメントの決定的なソースが実際にありますRedis の概念と ServiceStack.Net の概念の両方について知っておく必要があることはすべてですか? それとも、全体像を把握するために、両方の側面からのドキュメントを別々に統合する必要がありますか?

モデルのオブジェクト グラフに関連するオブジェクトを正確に格納する方法に取り組んでいます。これが私が扱いたい簡単なシナリオです:

システムには と の 2 つのオブジェクトがありUserますFeed。RDBMS 用語では、これら 2 つのオブジェクトは 1 対多の関係にあります。つまり、 にUserはオブジェクトのコレクションがFeedあり、フィードは 1 つの にのみ属することができますUser。フィードは常にユーザーを介して Redis からアクセスされますが、フィード インスタンスを介してユーザーにアクセスしたい場合もあります。

質問は、関連するオブジェクトをプロパティとして保存する必要があるのか​​、それとも関連するオブジェクトのId値を保存する必要があるのか​​ということです。説明する:

アプローチA

public class User
{
    public User()
    {
        Feeds = new List<Feed>();
    }

    public int Id { get; set; }

    public List<Feed> Feeds { get; set; }

    // Other properties
}

public class Feed
{
    public long Id { get; set; }

    public User User { get; set; }
}

アプローチ B :

public class User
{
    public User()
    {
        FeedIds = new List<long>();
    }

    public long Id { get; set; }

    public List<long> FeedIds { get; set; } 

    public List<Feed> GetFeeds()
    {
        return repository.GetFeeds( FeedIds );
    }
}

public class Feed
{
    public long Id { get; set; }

    public long UserId { get; set; }

    public User GetUser()
    {
        return repository.GetUser( UserId );
    }
}

上記のアプローチのどれが最も効果的ですか? さまざまな例で両方のアプローチが使用されているのを見てきましたが、見た例のいくつかはベストプラクティスではないかもしれないという印象を受けました。

関連するいくつかの簡単な質問:

  • オブジェクトに変更を加えた場合、Redis に自動的に反映されますか? それとも保存が必要ですか? 私は後者を想定していますが、完全に明確にする必要があります。
  • アプローチ A を使用する (できる) 場合、ユーザー オブジェクト X への更新は、参照されているオブジェクト グラフ全体に反映されますか?それとも、グラフ全体で変更を保存する必要がありますか?
  • インターフェイスを介してオブジェクトを保存することに問題はありますか (つまりIList<Feed>List<Feed>?

これらの質問が少し基本的なものである場合は申し訳ありません.2週間前まで、ServiceStackはもちろんのこと、Redisについても聞いたことがありませんでした(私のチームにも誰もいませんでした)。

4

1 に答える 1

68

世の中に出回っている他の多くのドキュメントを再ハッシュするのではなく、Redis + ServiceStack の Redis クライアントに関する背景情報をいくつか挙げます。

魔法はありません - Redis は真っ白なキャンバスです

最初に、Redis をデータ ストアとして使用すると空白のキャンバスが提供されるだけであり、関連するエンティティ自体の概念がないことを指摘したいと思います。つまり、分散 comp-sci データ構造へのアクセスを提供するだけです。リレーションシップを保存する方法は、Redis のプリミティブ データ構造操作を使用して、最終的にはクライアント ドライバー (ServiceStack C# Redis クライアント) またはアプリ開発者次第です。主要なデータ構造はすべて Redis で実装されているため、基本的に、データをどのように構造化して保存するかは完全に自由です。

コードでリレーションシップをどのように構築するかを考えてください

したがって、Redis にデータを格納する方法を考える最善の方法は、RDBMS テーブルにデータを格納する方法を完全に無視して、コードに格納する方法について考える、つまり、組み込みの C# コレクション クラスをメモリに使用することです。 Redis は、サーバー側のデータ構造を使用して動作をミラーリングします。

関連するエンティティの概念はありませんが、Redis の組み込みのSetおよびSortedSetデータ構造は、インデックスを格納するための理想的な方法を提供します。たとえば、Redis のSetコレクションは、要素の最大 1 回の出現のみを保存します。これは、アイテム/キー/IDを安全に追加できることを意味し、アイテムがすでに存在するかどうかを気にする必要はありません.1回または100回呼び出した場合、最終結果は同じになります。つまり、冪等であり、最終的には1つの要素のみがセット。そのため、オブジェクト グラフ (集約ルート) を保存する場合の一般的なユース ケースは、モデルを保存するたびに子エンティティ ID (別名外部キー) をセットに保存することです。

データの視覚化

エンティティがどのように Redis に格納されているかを視覚化するために、ServiceStack の C# Redis クライアントとうまく連携するRedis Admin UIをインストールすることをお勧めします。同じグローバル キースペースに存在する)。

エンティティを表示および編集するには、[編集]リンクをクリックして、選択したエンティティの内部 JSON 表現を表示および変更します。モデルがどのように格納されているかがわかれば、モデルの設計方法についてより適切な決定を下せるようになることを願っています。

POCO / エンティティの保存方法

C# Redis クライアントは、単一の主キーを持つ任意の POCO で動作します。これは、デフォルトで想定されていますId(ただし、この規則は ModelConfig でオーバーライドできます)。基本的に、POCO はシリアル化された JSON として Redis に保存され、そのインスタンスの一意のキーを形成するためにtypeof(Poco).Nameと の両方が使用されます。Id例えば:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'

C# クライアントの POCO は、ServiceStack の高速な Json Serializerを使用して従来どおりシリアル化されます。この場合、パブリック ゲッターを持つプロパティのみがシリアル化されます (およびパブリック セッターは逆シリアル化されます)。

デフォルトは属性で上書きできます[DataMember]が、POCO を醜くするのでお勧めしません。

エンティティがブロブ化される

したがって、Redis の POCO は単なる blob であることを知っているので、POCO の集約されていないルート データのみをパブリック プロパティとして保持する必要があります (意図的に冗長データを保存する場合を除きます)。メソッドを使用して関連するデータをフェッチするのが適切な規則です (シリアライズされないため) だけでなく、データを読み取るためにどのメソッドがリモート呼び出しを行うかをアプリに通知します。

したがって、FeedをUserと共に保存する必要があるかどうかについての問題は、それが集約されていないルート データであるかどうか、つまり、ユーザーのコンテキスト外でユーザー フィードにアクセスするかどうかです。いいえの場合は、List<Feed> FeedsプロパティをUser型のままにします。

カスタム インデックスの維持

ただし、すべてのフィードを独立してアクセスできるようにしたい場合、つまり、redisFeeds.GetById(1)それをユーザーの外部に保存し、2 つのエンティティをリンクするインデックスを維持したい場合があります。

お気付きのように、エンティティ間の関係を格納する方法は多数あり、その方法は主に好みの問題です。親>子の関係にある子エンティティの場合、常にParentIdを子エンティティとともに格納する必要があります。親の場合は、ChildIdsのコレクションをモデルとともに保存し、すべての子エンティティに対して単一のフェッチを実行してモデルを再水和することを選択できます。

もう 1 つの方法は、親インスタンスごとに独自のSetで親 dto の外側にインデックスを維持することです。これのいくつかの良い例は、Redis StackOverflow デモのC# ソース コードで、との関係が次の場所に格納されています。Users > QuestionsUsers > Answers

idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]

C# RedisClient には、そのTParent.StoreRelatedEntities()を介したデフォルトの親/子規則のサポートTParent.GetRelatedEntities<TChild>()TParent.DeleteRelatedEntities()、次のようなインデックスがバックグラウンドで維持される API が含まれています。

ref:Question/Answer:{QuestionId} => [{answerIds},..]

事実上、これらは可能なオプションのほんの一部に過ぎず、同じ目的を達成するためのさまざまな方法があり、独自の方法を自由に設定することもできます。

NoSQL のスキーマレスでルーズな型付けの自由を受け入れる必要があり、RDBMS を使用するときに慣れ親しんでいるかもしれない厳密で事前定義された構造に従おうとすることを心配する必要はありません。

結論として、Redis にデータを保存する本当の正しい方法はありません。たとえば、C# Redis クライアントは、POCO に関する高レベル API を提供するためにいくつかの仮定を行い、Redis のバイナリ セーフな文字列値で POCO を blob します。クライアントは、代わりにエンティティ プロパティを Redis ハッシュ (辞書) に格納することを好みます。どちらも機能します。

于 2012-01-19T01:24:19.007 に答える