この記事では、Redis トランザクションを徹底的に理解し、Redis トランザクションの 2 つのモード (トランザクション モードと Lua スクリプト) を比較します。
正確には、Redis トランザクションにはトランザクション モードとLua スクリプトの 2 つのモードが含まれています。
最初に結論について話しましょう:
Redis のトランザクション モードには次の特徴があります:
しかし、Lua スクリプトには、より実用的なシナリオがあります。これはトランザクションの別の形式であり、ある程度の原子性がありますが、スクリプトがエラーを報告すると、トランザクションはロールバックされません。 Lua スクリプトは分離を確保し、前のステップの結果に応じて後続のステップを完全にサポートできます。 [関連する推奨事項:Redis ビデオ チュートリアル]
Lua スクリプト モードは、分散ロック、遅延キュー、赤い封筒の取得、その他のシナリオなど、ほぼどこでも使用できます。
Redis トランザクションには次のコマンドが含まれます:
シリアル番号 | コマンドと説明 |
---|---|
1 | MULTI トランザクション ブロックの開始をマークします。 |
2 | EXEC は、トランザクション ブロック内のすべてのコマンドを実行します。 |
3 | DISCARD はトランザクションをキャンセルし、トランザクション ブロック内のすべてのコマンドの実行を放棄します。 |
4 | WATCH key [key ...] トランザクションが実行される前に、この (または複数の) キーが他のコマンドによって使用されているかどうかを監視します。変更された場合、トランザクションは中断されます。 |
5 | UNWATCH WATCH コマンドによるすべてのキーの監視を解除します。 |
トランザクションは 3 つの段階で構成されます:
次に、トランザクションの例を示します。
1 redis> MULTI 2 OK 3 redis> SET msg "hello world" 4 QUEUED 5 redis> GET msg 6 QUEUED 7 redis> EXEC 8 1) OK 9 1) hello world
ご質問がありますか?トランザクションの開始時に Redis キーを変更できますか?
トランザクションが EXEC コマンドを実行する前でも、Redis キーを変更できます。
トランザクションを開始する前に、watch コマンドを使用して Redis キーを監視できます。トランザクションを実行する前にキーの値を変更すると、トランザクションの実行は失敗し、nilが返されます。
上記の例を通じて、watch コマンドはオプティミスティック ロックと同様の効果を達成できます。
2.1 アトミック性
アトミック性とは、トランザクションすべての操作を指します。は完了または未完了のいずれかであり、中間段階で終了することはありません。トランザクションの実行中にエラーが発生した場合は、トランザクションがまったく実行されなかったかのように、トランザクションが開始される前の状態にロールバックされます。 最初の例: EXEC コマンドを実行する前に、クライアントから送信された運用コマンドが構文エラーまたは存在しないコマンドなど、間違っています。1 redis> MULTI 2 OK 3 redis> SET msg "other msg" 4 QUEUED 5 redis> wrongcommand ### 故意写错误的命令 6 (error) ERR unknown command 'wrongcommand' 7 redis> EXEC 8 (error) EXECABORT Transaction discarded because of previous errors. 9 redis> GET msg 10 "hello world"
1 redis> MULTI 2 OK 3 redis> SET msg "other msg" 4 QUEUED 5 redis> SET mystring "I am a string" 6 QUEUED 7 redis> HMSET mystring name "test" 8 QUEUED 9 redis> SET msg "after" 10 QUEUED 11 redis> EXEC 12 1) OK 13 2) OK 14 3) (error) WRONGTYPE Operation against a key holding the wrong kind of value 15 4) OK 16 redis> GET msg 17 "after"
Redis トランザクションは、特定の条件下でのみ特定のアトミック性を持ちます。
2.2 分離
データベースの分離とは、データベースが複数の同時トランザクションで同時にデータの読み取り、書き込み、変更を行えることを意味します。機能と分離により、複数のトランザクションが同時に実行される場合のクロス実行によるデータの不整合を防ぐことができます。 トランザクション分離は、次のようなさまざまなレベルに分割されます。同時シナリオで、トランザクションが相互の干渉を回避できるかどうか。
トランザクションの実行は、EXEC コマンドが実行される前とEXEC コマンドが実行された後の 2 つの段階に分けて、別々に説明できます。
WATCH メカニズムを使用して、オプティミスティック ロックの効果を実現できます。
2.3 永続性
データベースの永続性とは、トランザクションの完了後、データの変更は永続的であることを意味します。システムが失敗しても失われることはありません。 Redis データが永続化されるかどうかは、Redis の永続化構成モードによって異なります。redis トランザクションの耐久性は保証できません。
2.4 一貫性
一貫性の概念は常に混乱を招きます。私が検索した情報では、2 つの異なる定義がありました。まず Wikipedia で一貫性の定義を見てみましょう:
一貫性とは、トランザクションがデータベースを 1 つの有効な状態からのみ取得できることを保証します。データベースに書き込まれるすべてのデータは、制約、カスケード、トリガー、およびそれらの組み合わせを含むすべての定義されたルールに従って有効である必要があります。これにより、不正なトランザクションによるデータベースの破損が防止されますが、トランザクションが正常に実行されることは保証されません。参照整合性により、主キーと外部キーの関係が保証されます。
このテキストでは、一貫性の核心は「constraint」、「に書き込まれるすべてのデータ」です。データベースは、定義されたすべてのルールに従って有効である必要があります」。
制約を理解するにはどうすればよいですか?以下は、Zhihu の質問「データベースの内部一貫性と外部一貫性を理解する方法」からの引用です。Ant Financial の OceanBase R&D 専門家である Han Fusheng が回答しました。
「制約」ユーザーは、データがこの制約またはその制約に従う必要があることをデータベースに伝えます。データが変更されると、データベースはデータが制約を満たしているかどうかを確認します。制約が満たされなくなった場合、変更操作は実行されません。
リレーショナル データベースで最も一般的な 2 つのタイプの制約は、「一意制約」と「整合性制約」です。テーブルに定義された主キーと一意キーにより、指定されたデータ項目が決して繰り返されないことが保証されます。定義された参照整合性により、異なるテーブル内の同じ属性の一貫性も保証されます。
「ACID の一貫性」は非常に使いやすいため、ほとんどのユーザーの血液に溶け込んでいます。ユーザーはテーブルを設計するときに必要な制約を意識的に追加します。データベースもこの制約を厳密に適用します。
したがって、トランザクションの一貫性は事前定義された制約に関連しており、その制約によって一貫性が確保されることが保証されます。
この文を詳しく見てみましょう:これにより、不正なトランザクションによるデータベースの破損は防止されますが、トランザクションが正しいことは保証されません。
これを書いてもまだ少し混乱している人もいるかもしれませんが、古典的なtransferのケースを考えてみましょう。
トランザクションを開始します。Zhang San と Li Si の口座の初期残高は両方とも 1,000 元で、残高フィールドには制限はありません。張三は李斯に1200元を送金した。 Zhang San の残高は -200 に更新され、Li Si の残高は 2200 に更新されます。 実際のシナリオではユーザー残高を 0 未満にすることはできないため、アプリケーション レベルから見ると、このトランザクションは明らかに違法ですが、データベースの制約に完全に従っているため、データベース レベルから見ると、このトランザクションはそれでも一貫性は保証されています。 Redis のトランザクションの一貫性とは、Redis トランザクションが実行中にデータベースの制約に従い、違法または無効なエラー データが含まれていないことを意味します。 3 つの例外シナリオについてそれぞれ説明します。一貫性の核心は制約であるというセマンティクスに基づいて、Redis トランザクションは一貫性を保証できます。
原子性、分離性、耐久性はデータベースのプロパティですが、(ACID の意味での) 一貫性はデータベースのプロパティです。アプリケーションのプロパティ。アプリケーションは一貫性を達成するためにデータベースのアトミック性と分離プロパティに依存する場合がありますが、それはデータベースだけで決まるわけではありません。したがって、文字 C は実際には ACID に属しません。原子性、分離性、耐久性はデータベースの特性ですが、一貫性 (ACID の意味で) はアプリケーションの特性です。アプリケーションは一貫性を達成するためにデータベースの原子性と分離特性に依存する場合がありますが、これはデータベースだけに依存するわけではありません。したがって、文字 C は ACID に属しません。 多くの場合、私たちが苦労している一貫性は、実際には
現実世界との整合性を指します. 現実世界における一貫性は、物事の究極の目標です。
現実世界で一貫性を実現するには、次の点を満たす必要があります:2.5 トランザクションの特性
通常、Redis はインメモリ データベースと呼ばれており、従来のリレーショナル データベースとは異なります。より高いパフォーマンスとより速い書き込み速度を提供するために、設計および実装レベルでいくつかのバランスが取られており、トランザクションの ACID を完全にはサポートできません。
Redis トランザクションには次の特性があります:
エンジニアリングの観点から、トランザクション操作の各ステップが前のステップから返された結果に依存する必要があると仮定すると、監視を通じてオプティミスティック ロックを実装する必要があります。
3.1 はじめに
Lua は標準 C で書かれています。コードはシンプルで美しく、ほぼすべてのオペレーティング システムとプラットフォームでコンパイルして実行できます。 Lua スクリプトは C/C コードで簡単に呼び出すことができ、C/C 関数を順番に呼び出すこともできるため、Lua はアプリケーションで広く使用されています。
Lua スクリプトはゲーム分野で大きな話題を呼び、有名な「Westward Journey II」や「World of Warcraft」ではどちらも Lua スクリプトが広く使用されています。 Lua スクリプトは、OpenrestyやKongなど、Java バックエンド エンジニアが接触した API ゲートウェイで見ることができます。
Redis バージョン 2.6.0 以降、Redis の組み込み Lua インタープリターは Redis で Lua スクリプトを実行できるようになりました。
Lua スクリプトを使用する利点:
3.2 EVAL 命令
命令格式:
1 EVAL script numkeys key [key ...] arg [arg ...]
说明:
script
是第一个参数,为 Lua 5.1脚本;numkeys
指定后续参数有几个 key;key [key ...]
,是要操作的键,可以指定多个,在 Lua 脚本中通过KEYS[1]
,KEYS[2]
获取;arg [arg ...]
,参数,在 Lua 脚本中通过ARGV[1]
,ARGV[2]
获取。简单实例:
1 redis> eval "return ARGV[1]" 0 100 2 "100" 3 redis> eval "return {ARGV[1],ARGV[2]}" 0 100 101 4 1) "100" 5 2) "101" 6 redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second 7 1) "key1" 8 2) "key2" 9 3) "first" 10 4) "second"
下面演示下 Lua 如何调用 Redis 命令 ,通过redis.call()
来执行了 Redis 命令 。
1 redis> set mystring 'hello world' 2 OK 3 redis> get mystring 4 "hello world" 5 redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring 6 "hello world" 7 redis> EVAL "return redis.call('GET','mystring')" 0 8 "hello world"
3.3 EVALSHA 命令
使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。
思路是先将 Lua 脚本先缓存起来 , 返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。
EVALSHA 命令基本语法如下:
1 redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...]
实例如下:
1 redis> SCRIPT LOAD "return 'hello world'" 2 "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 3 redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 4 "hello world"
从定义上来说,Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。
因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。
不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。
Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。
Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。
不过在编写 Lua 脚本时,要注意如下两点:
更多编程相关知识,请访问:编程视频!!
以上がRedis のトランザクションについて話しましょう: トランザクション モード、Lua スクリプトの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。
コマンドと説明 | |
---|---|
2 | |
3 | |
4 | |
5 | |
6 | |