Rumah > Artikel > pangkalan data > Artikel yang menerangkan secara terperinci cara menggunakan Redis untuk melaksanakan kunci teragih
Pembelajaran yang disyorkan: Tutorial video Redis
Bila Apabila kami menulis kod berbilang utas, utas yang berbeza mungkin bersaing untuk mendapatkan sumber Untuk mengelakkan ralat yang disebabkan oleh persaingan sumber, kami akan mengunci sumber Hanya utas yang telah memperoleh kunci boleh terus dilaksanakan.
Kunci dalam proses pada asasnya ialah pembolehubah dalam ingatan Apabila benang menjalankan operasi untuk memohon kunci, jika ia berjaya menetapkan nilai pembolehubah yang mewakili kunci kepada 1, ini bermakna. kunci telah diperoleh.
Apa yang kita bincangkan di atas ialah kunci antara benang yang berbeza dalam proses pelayan Kunci ini diletakkan dalam ingatan, dan untuk aplikasi yang diedarkan Katakan, aplikasi yang berbeza (proses atau benang) digunakan pada pelayan yang berbeza, jadi kunci tidak boleh diwakili oleh pembolehubah dalam ingatan.
Memandangkan kunci boleh diwakili oleh ruang memori yang dikongsi pada pelayan, untuk aplikasi yang diedarkan, sistem storan boleh dikongsi untuk menyimpan kunci yang dikongsi Ini adalah kunci yang diedarkan , Redis
dilaksanakan dengan sangat pantas dan sangat sesuai sebagai sistem storan kongsi untuk melaksanakan kunci teragih.
Untuk kunci, sebenarnya hanya terdapat dua operasi, mengunci dan melepaskan kunci Mari lihat di bawah lihat bagaimana untuk mencapainya melalui Redis
?
Redis
setnx
akan menentukan sama ada nilai kunci wujud Jika wujud, tiada operasi akan dilakukan dan 0 akan dilakukan dikembalikan. Jika ia wujud, buat dan tetapkan nilai, dan kembalikan 1, supaya kita boleh melaksanakan setnx
untuk menetapkan nilai untuk kunci kunci wakil Jika tetapan berjaya, ini bermakna kunci itu diperolehi. kunci tidak boleh diperolehi.
# 使用key为lock来表示一个锁 setnx lock 1
Selepas menjalankan operasi, apabila anda ingin melepaskan kunci, hanya padamkan nilai kunci Redis
dalam lock
supaya proses lain boleh menetapkan semula dan mendapatkan kunci melalui perintah setnx
.
# 释放锁 del lock
Melalui dua arahan di atas, kami telah melaksanakan kunci teragih mudah, tetapi terdapat masalah di sini: jika proses dikunci melalui perintah setnx
, ralat berlaku semasa operasi tertentu. Jika tiada cara untuk melepaskan kunci dalam masa, maka proses lain tidak akan dapat mendapatkan kunci, dan sistem tidak akan dapat meneruskan pelaksanaan Cara untuk menyelesaikan masalah ini adalah dengan menetapkan tempoh sah untuk kunci , dan selepas tempoh sah ini, kunci akan dilepaskan secara automatik.
Sangat mudah untuk menetapkan tempoh sah untuk kunci Hanya gunakan perintah Redis
expire
, seperti:
# 加锁 setnx lock 1 # 给锁设置10s有效期 expire lock 10
Walau bagaimanapun, masalah lain timbul sekarang jika kita menetapkan kunci dan proses digantung sebelum melaksanakan perintah expire
, maka expire
tidak akan berjaya dilaksanakan dan kunci akan. tidak dilepaskan, jadi kita mesti memastikan bahawa kedua-dua arahan di atas dilaksanakan bersama-sama.
mempunyai dua kaedah, satu ialah menggunakan skrip yang ditulis dalam bahasa LUA
, dan satu lagi ialah menggunakan perintah Redis
set
Perintah set
diikuti oleh nx
parameter, dan kesan pelaksanaan adalah Ia konsisten dengan setnx
, dan perintah set
boleh digunakan dengan parameter ex
untuk menetapkan masa tamat tempoh, jadi kita boleh menggunakan perintah set
untuk bergabung setnx
dan expire
bersama-sama, supaya pelaksanaan boleh dijamin Ia atom.
# 判断是否键值是否存在,ex后面跟着的是键值的有效期,10s set lock 1 nx ex 10
Setelah menyelesaikan masalah penguncian yang berkesan, kini mari kita lihat masalah lain.
Seperti yang ditunjukkan dalam gambar di atas, kini terdapat proses A
, B
dan C
pada tiga pelayan berbeza yang perlu mendapatkan kunci apabila melakukan operasi tertentu Kunci mesti dilepaskan selepas pelaksanaan.
现在的情况是进程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视频教程
Atas ialah kandungan terperinci Artikel yang menerangkan secara terperinci cara menggunakan Redis untuk melaksanakan kunci teragih. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!