推奨学習: Redis ビデオ チュートリアル
マルチスレッド コードを作成すると、異なるスレッドがリソースをめぐって競合することがあります。リソースの競合によるエラーを避けるために、リソースをロックします。ロックを取得したスレッドのみが実行を続行できます。
プロセス内のロックは、本質的にはメモリ内の変数です。スレッドがロックを適用する操作を実行するときに、ロックを表す変数の値を 1 に正常に設定できた場合、それは次のことを意味します。ロックが取得されました その他 スレッドはロックを取得しようとするとブロックされます ロックを所有するスレッドが操作を完了すると、ロックの値が 0 に設定され、ロックが解放されます。
上で話しているのは、サーバーのプロセス内の異なるスレッド間のロックです。このロックはメモリに配置され、分散アプリケーション、たとえば異なるアプリケーションの場合は、 (プロセスまたはスレッド) は異なるサーバーにデプロイされるため、ロックはメモリ内の変数で表すことができません。
ロックはサーバー上の共有メモリ空間で表すことができるため、分散アプリケーションの場合、共有ストレージ システムを使用して共有ロックを保存できます。これが分散ロックです。インメモリ データベースとして, Redis
は非常に高速に実行され、分散ロックを実装するための共有ストレージ システムとして非常に適しています。
ロックの場合、実際にはロックとロックの解放の 2 つの操作しかありません。 Redis
を通じてそれを実現する方法を見てみましょう?
#Redis
コマンドは、キー値が存在するかどうかを判断します。存在する場合は、存在しない場合は値を作成して割り当て、1 を返すため、setnx
を実行して代表的なロック キーの値を設定できます。設定が成功した場合はロックが取得されたことを意味し、失敗した場合はロックを取得できません。
# 使用key为lock来表示一个锁 setnx lock 1
操作実行後、ロックを解除したい場合は、Redis
のキー値を直接変更します。 lock
削除するだけで、他のプロセスが setnx
コマンドを通じてリセットしてロックを取得できるようになります。
# 释放锁 del lock
上記の 2 つのコマンドを通じて、単純な分散ロックを実装しましたが、ここで問題があります。プロセスが setnx
コマンドを通じてロックされている場合、特定の If the操作が失敗し、時間内にロックを解放する方法がない場合、他のプロセスがロックを取得できなくなり、システムは実行を続行できなくなります。この問題を解決する方法は、有効期間を設定することです。この有効期間が経過すると、ロックは自動的に解除されます。
ロックの有効期間を設定するのは非常に簡単で、expire
コマンドを使用するだけです。 Redis
の例:
# 加锁 setnx lock 1 # 给锁设置10s有效期 expire lock 10
ただし、別の問題が発生します。ロックを設定した後、expire
コマンドを実行する前にプロセスがハングした場合、expire
実行が成功しなかった場合は、ロックが解放されていないため、上記の 2 つのコマンドが同時に実行されていることを確認する必要があります。
LUA
言語で記述したスクリプトを使用する方法と、Redis
の set
コマンドを使用する方法の 2 つがあります。 ## set コマンドの後に
nx パラメータを指定すると、実行効果は
setnx と一致し、
set コマンドの後に次のパラメータを指定できます。
ex パラメータ。有効期限を設定します。これにより、
set コマンドを使用して
setnx と
expire をマージできるようになります。実行のアトミック性を保証できます。
# 判断是否键值是否存在,ex后面跟着的是键值的有效期,10s set lock 1 nx ex 10
A、
B、
C## という 3 つの異なるサーバー上にプロセスが存在します。 # 操作を実行するには、ロックを取得し、実行後にロックを解放する必要があります。 <p>现在的情况是<code>进程A
执行第2步时卡顿了(上面绿色区域所示),且时间超出了锁有效期,所以进程A
设置的锁自动释放了,这时候进程B
获得了锁,并开始执行操作,但由于进程A
只是卡顿了而已,所以会继续执行的时候,在第3步的时候会手动释放锁,但是这个时候,锁由线程B
所拥有,也就是说进程A删除的不是自己的锁,而进程B的锁,这时候进程B
还没执行完,但锁被释放后,进程C
可以加锁,也就是说由于进程A卡顿释放错了锁,导致进程B和进程C可以同时获得锁。
怎么避免这种情况呢?如何区分其他进程的锁,避免删除其他进程的锁呢?答案就是每个进程在加锁的时候,给锁设置一个唯一值,并在释放锁的时候,判断是不是自己设置的锁。
给锁设置唯一值的时候,一样是使用set
命令,唯一的不同是将键值1改为一个随机生成的唯一值,比如uuid。
# rand_uid表示唯一id set lock rand_id nx ex 10
当锁里的值由进程设置后,释放锁的时候,就需要判断锁是不是自己的,步骤如下:
Redis
的get
命令获得锁的值del
命令释放锁。此时我们看到,释放锁需要执行三个操作,如果三个操作依次执行的话,是没有办法保证原子性的,比如进程A
在执行到第2步后,准备开始执行del
命令时,而锁由时有效期到了,被自动释放了,并被其他服务器上的进程B
获得锁,但这时候线程A
执行del
还是把线程B
的锁给删掉了。
解决这个问题的办法就是保证上述三个操作执行的原子性,即在执行释放锁的三个操作中,其他进程不可以获得锁,想要做到这一点,需要使用到LUA脚本。
Redis
支持LUA
脚本,LUA
脚里的代码执行的时候,其他客户端的请求不会被执行,这样可以保证原子性操作,所以我们可以使用下面脚本进行锁的释放:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
将上述脚本保存为脚本后,可以调用Redis
客户端命令redis-cli
来执行,如下:
# lock为key,rand_id表示key里保存的值 redis-cli --eval unlock.lua lock , rand_id
推荐学习:Redis视频教程
以上がRedis を使用して分散ロックを実装する方法を詳しく説明した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。