ユーザーログインシステム
ユーザーのログイン情報を記録するシステムですが、業務を簡素化するとテーブルは1つだけ残ります。
リレーショナル データベースの設計
mysql>select*fromlogin;
--------- ------------ - --- ------------- ---------------------
|ユーザーID|名前|ログイン回数 | last_login_time|
--------- ---------------- ---------------- - -- ------------------
|1|ケントンプソン|5|2011-01-0100:00:00|
| 2| デニスリッチー|1|2011-02-0100:00:00|
|3|ジョーアームストロング|2|2011-03-0100:00:00|
----- -------------------------------------------------- ------------------ --
user_id テーブルの主キー、name はユーザー名、login_times はユーザーのログイン数を表します。各ユーザーがログインすると、login_times が自動的に増加し、last_login_time が現在時刻に更新されます。
REDIS の設計
リレーショナル データを KV データベースに変換します。私の方法は次のとおりです。
キー テーブル名: 主キー値: カラム名
値列の値
通常、区切り文字としてコロンが使用されますが、これは不文律です。たとえば、php-adminforredis システムでは、デフォルトでキーがコロンで区切られているため、user:1user:2 とその他のキーが 1 つのグループに分割されます。したがって、上記のリレーショナル データは kv データに変換され、次のように記録されます。
Setlogin:1:login_times5
Setlogin:2:login_times1
Setlogin:3:login_times2
Setlogin:1:last_login_time2011-1-1
Setlogin:2:last_login_time2011-2-1
Setlogin:3:last_login_time2011-3-1
setlogin :1 :name"kenthompson"
setlogin:2:name"dennisritchie"
setlogin:3:name"JoeArmstrong"
主キーがわかっている場合は、次のことができます。 get メソッドと set メソッドを使用して、ユーザー名、ログイン時刻、および最終ログイン時刻を取得または変更します。
通常、ユーザーは自分の ID を知ることができず、ユーザー名しか知らないため、名前から ID へのマッピング関係が必要になります。ここでの設計は上記とは異なります。
set"login:kenthompson:id"1
set"login:dennisritchie:id"2
set"login:JoeArmstrong:id"3
このように、ユーザーがログインするたびに、ビジネス ロジックは次のようになります (Python バージョン)。r は Redis オブジェクト、name は既知のユーザー名です。
#ユーザーのidを取得します
# uid=r.get("login:%s:id"%name) #ユーザーのログイン数を自動的に増やす次は言い換え可能な文です: ret = r.incr("login:%s:login_times" % uid) #ユーザーの最終ログイン時刻を更新します ret=r.set("login:%s:last_login_time " %uid,datetime.datetime.now()) 要件が ID を知り、ユーザーの最終ログイン時刻とログイン回数を更新または取得することだけである場合、リレーショナル データベースと違いはありません。そしてkvデータベース。 1 つは btreepk 経由で、もう 1 つは hash 経由で、どちらも非常にうまく機能します。 最近ログインした N 人のユーザーを検索するには、次の要件があるとします。開発者が見てみると、比較的シンプルで、たった 1 つの SQL ステートメントで実行できます。 「login」テーブルからすべての列を選択し、「last_login_time」列で降順に並べ替え、結果セットのサイズを N に制限してください。DBA が要件を理解した後、次のことを検討してください。将来のテーブルが比較的大きい場合は、last_login_time にインデックスを作成します。インデックスの右端から開始して N 個のレコードにアクセスし、N 個のテーブルの戻り操作を実行することにより、実行計画は大きな効果をもたらします。 一般的な Redis データベース キー値の設計は何ですか? 2 日後、別のリクエストが届き、誰が最も多くログインしているかを知る必要がありました。同じリレーショナル型にどう対処するか? DEV は、それは簡単だと言いました。 select*fromloginorderbylogin_timesdesclimitN DBA は調べて、login_time にインデックスを作成する必要があります。何か問題があると思いますか? テーブル内のすべてのフィールドには主引用符が付いています。 問題の原因は、リレーショナル データベースのデータ ストレージが十分に柔軟ではなく、行に配置されたヒープ テーブルを使用してのみデータを保存できることです。データ構造を統一すると、特定の列に素早くアクセスするにはインデックスを使用してSQLのアクセスパスを変更する必要があり、アクセスパスの増加により統計情報を利用する必要があるため、多くの問題が発生します。 インデックス、統計計画、実行計画はありません。これが kv データベースです。 最新の N 個のデータを取得する必要がある場合、Redis ではリンク リストの後入れ先出し機能が非常に適しています。上記のログイン コードの後にコードを追加して、ログイン リンク リストを維持し、最新の N 人のログイン ユーザーが常にそのリストに保存されるようにその長さを制御します。 #現在のログイン者をリンクリストに追加します ret=r.lpush("login:last_login_times",uid) #Nのみのリンクリストを保持しますbits
ret=redis.ltrim("login:last_login_times",0,N-1) このようにして、最新のログイン者の ID を取得する必要があります。次のコードです。使用できます last_login_list=r .lrange("login:last_login_times",0,N-1) さらに、最も多くログインした人を見つけるには、sortedset を使用します。ユーザーとログイン時間を組み合わせてソートセットに均一に保存します。
zaddlogin:login_times51
zaddlogin:login_times12
zaddlogin:login_times23
このように、ユーザーがログインすると、追加のソートセットが維持され、コードは次のようになります。
#このユーザーのログイン回数が 1 回増加します
ret=r.zincrby("login:login_times",1,uid)
それではどうやってログイン回数が最も多いユーザーを取得するには、逆順に並べ替えます。N 番目にランク付けされたユーザーを取得します。
ret=r.zrevrange("login:login_times",0,N-1)
DEV は 2 行のコードを追加する必要があることがわかりますが、DBA はインデックスなどを考慮する必要がありません。
タグ システム
タグはインターネット アプリケーションで特に一般的ですが、従来のリレーショナル データベースで設計した場合は、少し目立たないものになるでしょう。この点における Redis の利点を確認するために、書籍を検索する例を考えてみましょう。
リレーショナル データベースの設計
書籍の詳細とタグの 2 つのテーブル (各書籍のタグを示します) 1 つの書籍には複数のタグがあります。
mysql>select*frombook;
------ ------------------------ - ----------
|id|name|author|
------ --- - --------------------------------------
| 1|TheRubyProgrammingLanguage|MarkPilgrim|
|1|Rubyonrail|DavidFlanagan|
|1|ProgrammingErlang|JoeArmstrong|
------ ------ ------------------------------------------
mysql> select *fromtag;
--------- ---------
|tagname|book_id|
----- - --- --------
|ruby|1|
|ruby|2|
|web|2|
|erlang|3|
--------- ---------
そのようなニーズがある場合、検索は両方とも Rubyおよび web.Books、リレーショナル データベースが使用されている場合、それらはどのように処理されますか?
selectb.name,b.authorfromtagt1,tagt2,bookb
wheret1.tagname='web'andt2. tagname='ruby'andt1. book_id=t2.book_idandb.id=t1.book_id
タグ テーブルが 2 回関連付けられ、次にブックに関連付けられます。この SQL は非常に複雑です。要件が Ruby の場合はどうなるでしょうか
リレーショナル データは、実際には、これらの集合演算にはあまり適していません。
REDIS の設計
まず、上記と同様に書籍データを保存する必要があります。
セットブック:1:名前「TheRubyProgrammingLanguage」
セットブック:2:名前「Rubyonrail」
セットブック:3:名前「ProgrammingErlang」
セットブック: 1:author"MarkPilgrim"
Setbook:2:author"DavidFlanagan"
Setbook:3:author"JoeArmstrong"
タグ テーブル データの保存にはセットを使用します。交差点や結合を見つけるのが得意です。
# saddtag:ruby1 saddtag:ruby2 saddtag:web2 saddtag:erlang3 次に、ruby に属する本ですが、Web にも属しますか? inter_list=redis.sinter("tag.web","tag:ruby") つまり、 Ruby に属するが Web に属さない本 ? inter_list=redis.sdiff("tag.ruby","tag:web") に属する書籍のコレクションto Ruby and web? inter_list=redis .sunion("tag.ruby","tag:web") とてもシンプルです。 上記の 2 つの例から、シナリオによってはリレーショナル データベースが適していないことがわかります。ニーズを満たすシステムを設計できるかもしれませんが、それは常に奇妙で奇妙に感じられます。何かが機械的に行われています。 特にシステムへのログインの例では、業務上インデックスが頻繁に作成されます。複雑なシステムでは、ddl (インデックスの作成) によって実行計画が変更される場合があります。複雑なビジネスを伴う古いシステムの SQL はあらゆる種類の奇妙なものであり、他の SQL が異なる実行プランを使用する原因となるため、この問題を予測するのは困難です。 DBA にこのシステム内のすべての SQL を理解するよう要求するのは非常に困難です。この問題は Oracle では特に深刻であり、おそらくすべての DBA がこの問題に遭遇したことがあります。現在はオンライン DDL メソッドがありますが、MySQL のようなシステムにとって DDL はまだあまり便利ではありません。大きなテーブルの場合、DBA は朝早く起きて営業時間の短い時間帯に作業を行います。私もこれを頻繁に行ってきました。 Redis を使用してこの需要を処理すると非常に便利で、DBA は容量を見積もるだけで済みます。以上がRedis データベースの一般的なキーと値の設計は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。