Redisシリーズカタログ
redisシリーズ - データ型geospatial: 隣に老王はいるか?
redisシリーズ - データ型ビットマップ:今日はサインインしましたか?
mysql を学ぶとき、mysql にはモノがあり、モノには 4 つの ACID 特性、原子性、一貫性、分離、および永続性があるとよく言われます。
redisには何かありますか?どのように見えるのでしょうか?実際にテストした結果です。
もの
redisにはモノがあります。しかし、redisにおけるthingは弱いものです。thingsは分離レベルを持ちませんし、thing内の複数のコマンドはアトミックではありません。このような理由から、redisのthingも実際の生産現場ではほとんど使われません。
redisのthingの本質は、コマンドのコレクションです。thing内のコマンドはすべてシリアライズされてキューに格納され、thingの実行中に、コマンドは落ちてきた順に実行されます。
redisは単一コマンドのアトミック性を保証しますが、複数コマンドのアトミック性は保証しません。
通常の業務遂行
redisを使うには3つのステップがあります:
オンにする
チームに参加させる
実行
普通に展示されているもの
.1:6379>
.1:6379> flushall
OK
.1:6379> multi #オンにする
OK
.1:6379> set name wuxl #キューに入るコマンド
QUEUED
.1:6379> set age 30 #キューに入るコマンド
QUEUED
.1:6379> get name #キューに入るコマンド
QUEUED
.1:6379> set addr Tokyo #キューに入るコマンド
QUEUED
.1:6379> exec #実行する
1) OK
2) OK
3) "wuxl"
4) OK
.1:6379>
上記のことがコミットされると、4つのコマンドが順次実行され、実行が完了すると終了します。
物品のキャンセル
また、オンになっているものをキャンセルすることもできます:
.1:6379>
.1:6379> flushall
OK
.1:6379> multi
OK
.1:6379> set name wuxl
QUEUED
.1:6379> set age 30
QUEUED
.1:6379> discard #キャンセルする
OK
.1:6379> get age #コマンドの中にあるものは実行されない。
(nil)
.1:6379>
エラーの報告
コンパイルエラー
コンパイルエラーは、キュー内のコマンドのキュー自体に問題があるため、エラーが発生したときにキューにコマンドの結果、;コンパイルエラーがある、execの実行は、失敗をプロンプトが表示されます、すべてのコマンドを実行することはできません。
.1:6379> flushall
OK
.1:6379> multi
OK
.1:6379> set name wuxl
QUEUED
.1:6379> get # 間違ったコマンド、キューの入力エラー
(error) ERR wrong number of arguments for 'get' command
.1:6379> set age 30
QUEUED
.1:6379> exec # コミットエラーが発生し、全てのコマンドが実行できない
(error) EXECABORT Transaction discarded because of previous errors.
.1:6379> get name # 結果ではないクエリー
(nil)
.1:6379> get age
(nil)
.1:6379>
実行時エラー
ランタイム・エラーとは、スタック上にあるコマンド自体にエラーはないが、実行キューから外れたときにエラーを報告することです。例えば、Stringに対する次のような自己インクリメント操作です。
.1:6379> flushall
OK
.1:6379> set name wuxl # 名前を初期化する。string
OK
.1:6379> multi
OK
.1:6379> set age 30
QUEUED
.1:6379> incr name # キューに入るコマンドは、名前に対してセルフインクリメントを行う。コマンド自体に問題はないが、実行時にエラーが発生する。
QUEUED
.1:6379> set addr Tokyo
QUEUED
.1:6379> exec # タスクを投入し、各コマンドを順番に実行する
1) OK
2) (error) ERR value is not an integer or out of range # つ目のコマンドはエラーを報告する。
3) OK
.1:6379> get name
"wuxl"
.1:6379> get age # 他のコマンドは全て正常に実行されている
".0.0.1:6379> get addr
"Tokyo"
.1:6379>
ここでわかるように、ランタイムはエラーを報告しますが、事態はロールバックされず、エラーは後続のコマンドの実行には影響しません。つまり、キュー内のコマンドにはアトミック性がありません。
オプティミスト・ロック
楽観的ロックと悲観的ロック
ペシミスティック・ロック
同時実行の問題が発生する可能性が高いと考える方が悲観的です。このような場合こそ、実際のロッキングを処理する必要があります。ロックはパフォーマンスを低下させます。
オプティミスト・ロック
これは、同時実行の問題が発生する可能性が低く、より楽観的であると考えられています。この時点では、ロックを追加する必要はなく、変更操作を実行するだけで、元のデータが変更されたかどうかを比較し、変更がなければ変更し、変更があれば変更しません。これは通常、バージョンフィールドを使用してmyslで処理されます。
redisはデータを変更する際に、他のスレッドによってデータが変更されたかどうかを監視するwatchコマンドを提供しており、データが変更された場合は変更に失敗し、変更されていない場合は変更に成功します。実際、watchコマンドはredisにおける楽観的ロックの実装とみなすことができます。
転送シミュレーション
以下では、2つの口座間で送金を行うビジネスをシミュレートしています。
シングルスレッドシミュレーション
通常の移籍プロセス:
.1:6379> flushall #データベースを空にする
OK
.1:6379> set acc1 1000 #支払い口座に$1000がある
OK
.1:6379> set acc2 0 #受取口座には$0がある
OK
.1:6379> multi #オンにする
OK
.1:6379> decrby acc1 100 #支払口座の引き落とし100
QUEUED
.1:6379> incrby acc2 100 #マネー口座のレシートを受け取る100
QUEUED
.1:6379> exec #実行する
1) (integer) 900
2) (integer) .0.1:6379> get acc1 #支払い口座には"
.1:6379> get acc2 #受け取ったアカウントは"
.1:6379>
上記のシングルスレッドでの送金シミュレーションの後、支払い口座には支払い後900があり、受け取り口座には100があります。
コンカレントシミュレーション
この処理において、誰かがexecの前にacc1に$1000をチャージしようとした場合、今回ロックが使用されなかった場合、同時実行の問題が発生しますが、exec終了後の結果はどうなるでしょうか?結果はacc1が1900、acc1が100となり、この結果も正しいです。なぜでしょうか?なぜなら、redisは物事を分離しないので、2つの物事が互いに影響し合うからです。
execを実行する必要がある場合は、acc1が変更されていないことを比較し、それが変更されている場合は、転送が失敗しました。このような場合、redisウォッチを使って楽観的にロックすることができます。以下は、2つのクライアントが同時にredisデータを変更し、楽観的ロックとしてwatchを使用するシミュレーションです。
acc1 は支払口座、acc2 は受取口座です。
.1:6379> flushall
OK
.1:6379> set acc1 1000 #口座への支払い
OK
.1:6379> set acc2 0 #お金を受け取る
OK
.1:6379>
ステップ2:クライアント1を使用して、acc1の変更をリッスンするためにwatchをオンにし、また、物事をオンにし、物事を最初に実行せずにキューにコマンドを実行します。
.1:6379>
.1:6379> watch acc1 # watchを使って、acc1の口座が実行中に変更されないか監視する。
OK
.1:6379> multi # オンにする
OK
.1:6379> decrby acc1 100 #支払いのシミュレーション
QUEUED
.1:6379> incrby acc2 100 #集金をシミュレートする
QUEUED
.1:6379>
ステップ3:クライアント2を使用して、acc1口座の金額を変更します。
.1:6379>
.1:6379> incrby acc1 1000 # acc1の口座への別の入金をシミュレートする。1000
(integer) .0.1:6379> get acc1 # これはacc1の口座の変更である。"
.1:6379>
ここで、クライアント2が正常に実行されたことがわかります!もしmysqlだったら、この時点でクライアント2はブロックされ、ここで成功する前にクライアント1の実行が終わるのを待たなければなりません。redisは分離されておらず、互いに影響を及ぼし合っている、というのはこういう意味です。
ステップ4:クライアント・ワンを使って実行します。
.1:6379> exec # ことを実行すると、実行時に、変更するかどうかacc1と比較される、変更すると、失敗の実装;acc1が変更されなかった場合は、成功の実装の
(nil) #実行失敗
.1:6379> get acc1
".0.0.1:6379> get acc2
"0"
.1:6379>
ここでは、クライアント2がacc2の口座の金額を変更したため、クライアント1がexecを実行する前に、watchがacc1の金額が変更を送信したことを監視し、クライアント1の転送処理が失敗していることがわかります。ここでは実際にwatchを使って楽観的ロックを実装しています。
完了!
知識の普及、価値の共有、小さなパートナーの注目とサポートのおかげで、私は諸葛小猿、インターネット民衆の闘争の不確実性です!




