Rumah >pangkalan data >Redis >Apakah yang perlu kita perhatikan semasa melaksanakan kunci teragih dalam Redis? [Ringkasan perkara yang memerlukan perhatian]

Apakah yang perlu kita perhatikan semasa melaksanakan kunci teragih dalam Redis? [Ringkasan perkara yang memerlukan perhatian]

青灯夜游
青灯夜游ke hadapan
2022-03-04 16:21:583795semak imbas

Apakah yang perlu anda perhatikan semasa melaksanakan kunci teragih dalam Redis? Artikel berikut akan meringkaskan dan berkongsi dengan anda beberapa perkara yang perlu diberi perhatian apabila menggunakan Redis sebagai kunci yang diedarkan saya harap ia akan membantu anda!

Apakah yang perlu kita perhatikan semasa melaksanakan kunci teragih dalam Redis? [Ringkasan perkara yang memerlukan perhatian]

Redis melaksanakan kunci teragih

Baru-baru ini saya melihat satu artikel sambil melihat kunci yang diedarkan Artikel yang bagus, diproses khas untuk pemahaman saya sendiri:

Tiga elemen teras pelaksanaan kunci yang diedarkan Redis:

1 Mengunci

Cara paling mudah ialah menggunakan perintah setnx. Kuncinya ialah pengecam unik kunci, yang dinamakan mengikut perniagaan Nilai ialah ID utas urutan semasa. [Cadangan berkaitan: Tutorial video Redis]

Contohnya, jika anda ingin mengunci aktiviti jualan kilat produk, anda boleh menamakan kunci "lock_sale_ID". Dan apakah nilai yang ditetapkan? Kita boleh menetapkannya buat sementara waktu kepada 1. Kod pseudo untuk mengunci adalah seperti berikut:

setnx(key, 1)Apabila thread melaksanakan setnx dan mengembalikan 1, ini bermakna kunci itu tidak wujud dan benang berjaya memperolehnya Kunci, apabila utas lain melaksanakan setnx dan mengembalikan 0, ini bermakna kunci itu sudah wujud dan utas gagal meraih kunci.

2. Membuka kunci

Jika sesuatu dikunci, ia mesti dibuka. Apabila benang yang memperoleh kunci menyelesaikan tugasnya, ia perlu melepaskan kunci supaya benang lain boleh masuk. Cara paling mudah untuk melepaskan kunci adalah dengan melaksanakan arahan del Kod pseudo adalah seperti berikut:

del (kunci) Selepas. melepaskan kunci, benang lain Anda boleh terus melaksanakan perintah setnx untuk mendapatkan kunci.

3. Tamat masa kunci

Apakah maksud tamat masa kunci? Jika benang yang memperoleh kunci mati semasa melaksanakan tugas dan tidak mempunyai masa untuk melepaskan kunci secara eksplisit, sumber akan dikunci selama-lamanya dan utas lain tidak akan dapat masuk lagi.

Oleh itu, kunci setnx mesti menetapkan tempoh tamat masa untuk memastikan walaupun ia tidak dilepaskan secara eksplisit, kunci akan dilepaskan secara automatik selepas tempoh masa tertentu. setnx tidak menyokong parameter tamat masa, jadi arahan tambahan diperlukan kod pseudo adalah seperti berikut:

tamat tempoh (kunci, 30) Diambil bersama, langkah ketiga. pelaksanaan kunci teragih kami Versi pertama pseudokod adalah seperti berikut:

if(setnx(key,1) == 1){
    expire(key,30)
    try {
        do something ......
    }catch()  {  }  finally {
       del(key)
    }

}

Oleh kerana terdapat tiga masalah maut dalam pseudokod di atas:

1 ketidakatoman setnx dan tamat tempoh

Bayangkan senario yang melampau apabila benang melaksanakan setnx, ia berjaya memperoleh kunci:

setnx baru sahaja berjaya dilaksanakan dan telah. belum sempat melaksanakan perintah tamat tempoh Nod 1 Duang Tiba-tiba menutup telefon.

if(setnx(key,1) == 1){  //此处挂掉了.....
    expire(key,30)
    try {
        do something ......
    }catch()
  {
  }
  finally {
       del(key)
    }
 
}
Dengan cara ini, kunci tidak mempunyai masa tamat yang ditetapkan dan menjadi "abadi", dan benang lain tidak lagi boleh mendapatkan kunci itu.

Bagaimana untuk menyelesaikannya? Arahan setnx itu sendiri tidak menyokong lulus dalam tamat masa

Redis 2.6.12 atau lebih tinggi menambah parameter pilihan pada arahan yang ditetapkan adalah seperti berikut: set (kunci, 1, 30, NX). supaya Boleh menggantikan arahan setnx .

2. Menggunakan del selepas tamat masa mengakibatkan secara tidak sengaja memadamkan kunci urutan lain

Satu lagi senario melampau, jika benang berjaya mendapatkan kunci dan tamat masa ditetapkan kepada 30 saat.

Jika atas sebab tertentu benang A dijalankan dengan sangat perlahan dan belum selesai dilaksanakan selepas 30 saat, kunci akan dilepaskan secara automatik apabila tamat tempoh dan benang B akan memperoleh kunci.

Selepas itu, utas A menyelesaikan tugasan dan utas A kemudian melaksanakan arahan del untuk melepaskan kunci. Tetapi pada masa ini, utas B belum selesai melaksanakan

Thread A sebenarnya telah memadamkan kunci yang ditambahkan oleh utas B .

Bagaimana untuk mengelakkan situasi ini? Anda boleh membuat pertimbangan sebelum del melepaskan kunci untuk mengesahkan sama ada kunci semasa ialah kunci yang ditambahkan oleh anda sendiri.

Bagi pelaksanaan khusus, anda boleh menggunakan ID utas semasa sebagai nilai semasa mengunci dan mengesahkan sama ada nilai yang sepadan dengan kunci itu ialah ID urutan anda sendiri sebelum memadamkannya.

加锁:
String threadId = Thread.currentThread().getId()
set(key,threadId ,30,NX)
doSomething.....
 
解锁:
if(threadId .equals(redisClient.get(key))){
    del(key)
}
Walau bagaimanapun, berbuat demikian membayangkan masalah baharu,

jika penghakiman dan pelepasan kunci adalah dua operasi bebas, bukan atom.

Kami semua adalah pengaturcara yang mengejar kesempurnaan, jadi bahagian ini perlu dilaksanakan menggunakan skrip Lua:

String luaScript = 'if redis .call('get', KEYS[1]) == ARGV[1] kemudian kembalikan redis.call('del', KEYS[1]) else return 0 end' ;

redisClient.eval(luaScript , Collections.singletonList(key) , Collections.singletonList(threadId));

Dengan cara ini, proses pengesahan dan pemadaman adalah operasi atom.

3. Kemungkinan konkurensi

还是刚才第二点所描述的场景,虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然是不完美的。

怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”

当过去了29秒,线程A还没执行完,这时候守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,每20秒执行一次。

当线程A执行完任务,会显式关掉守护线程。

另一种情况,如果节点1 忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。

 memcache实现分布式锁

首页top 10, 由数据库加载到memcache缓存n分钟
微博中名人的content cache, 一旦不存在会大量请求不能命中并加载数据库
需要执行多个IO操作生成的数据存在cache中, 比如查询db多次
问题
在大并发的场合,当cache失效时,大量并发同时取不到cache,会同一瞬间去访问db并回设cache,可能会给系统带来潜在的超负荷风险。我们曾经在线上系统出现过类似故障。

解决方法

if (memcache.get(key) == null) {
// 3 min timeout to avoid mutex holder crash
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
value = db.get(key);
memcache.set(key, value);
memcache.delete(key_mutex);
} else {
 
sleep(50);
retry();
}
}

在load db之前先add一个mutex key, mutex key add成功之后再去做加载db, 如果add失败则sleep之后重试读取原cache数据。为了防止死锁,mutex key也需要设置过期时间。伪代码如下

Zookeeper实现分布式缓存

Zookeeper的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做Znode

Znode分为四种类型:

  • 1.持久节点 (PERSISTENT)

默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在 。

  • 2.持久节点顺序节点(PERSISTENT_SEQUENTIAL)

所谓顺序节点,就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号:

  • 3.临时节点(EPHEMERAL)

和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除:

  • 4.临时顺序节点(EPHEMERAL_SEQUENTIAL)

顾名思义,临时顺序节点结合和临时节点和顺序节点的特点:在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后,临时节点会被删除。

Zookeeper分布式锁恰恰应用了临时顺序节点。具体如何实现呢?让我们来看一看详细步骤:

  • 获取锁

首先,在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1

之后,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。

这时候,如果再有一个客户端 Client2 前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock2

Client2查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock2是不是顺序最靠前的一个,结果发现节点Lock2并不是最小的。

Jadi, Client2 mendaftarkan Lock1 dengan nod Watcher yang hanya berkedudukan lebih tinggi daripadanya, untuk memantau sama ada nod Lock1 wujud. Ini bermakna Client2 gagal mengambil kunci dan memasuki keadaan menunggu.

Pada masa ini, jika pelanggan lain Client3 datang untuk memperoleh kunci, muat turun dan buat nod jujukan sementara ParentLock dalam Lock3.

Client3Cari semua nod jujukan sementara di bawah ParentLock dan isikannya dan nilai sama ada nod Lock3 yang anda cipta adalah yang mempunyai susunan tertinggi. Hasilnya Juga didapati bahawa nod Lock3 bukanlah yang terkecil.

Jadi, Client3 mendaftarkan Lock2 dengan nod Watcher yang hanya berkedudukan lebih tinggi daripadanya, untuk memantau sama ada nod Lock2 wujud. Ini bermakna Client3 juga gagal mengambil kunci dan memasuki keadaan menunggu.

Dengan cara ini, Client1 mendapat kunci, Client2 memantau Lock1 dan Client3 memantau Lock2. Ini hanya membentuk baris gilir menunggu, sama seperti ReentrantLock yang AQS(AbstractQueuedSynchronizer) bergantung pada Java.

  • Lepaskan kunci

Terdapat dua situasi untuk melepaskan kunci:

1. Apabila tugasan selesai, klien memaparkan keluaran

Apabila tugasan selesai, Client1 akan memaparkan arahan untuk memanggil nod padam Lock1.

2 Semasa pelaksanaan tugas, pelanggan ranap

Mendapat kunciClient1 Semasa pelaksanaan tugas, jika Duang ranap, maka Sambungan ke pelayan Zookeeper akan diputuskan. Mengikut ciri nod sementara, nod yang berkaitan Lock1 akan dipadamkan secara automatik.

Memandangkan Client2 sentiasa memantau status kewujudan Lock1, apabila nod Lock1 dipadamkan, Client2 akan menerima pemberitahuan serta-merta. Pada masa ini, Client2 akan menanyakan semua nod di bawah ParentLock sekali lagi untuk mengesahkan sama ada nod Lock2 yang dibuat dengan sendirinya ialah nod terkecil pada masa ini. Jika ia adalah yang terkecil, maka Client2 secara semula jadi memperoleh kunci.

Begitu juga, jika Client2 turut memadamkan nod Lock2 disebabkan penyiapan tugas atau ranap nod, maka Cient3 akan menerima pemberitahuan.

Akhirnya, Client3 berjaya mendapatkan kunci.

Perbandingan kunci pengagihan Zookeeper dan Redis

Jadual di bawah Meringkaskan kelebihan dan kekurangan kunci yang diedarkan Zookeeper dan Redis:

Untuk lebih banyak pengetahuan berkaitan pengaturcaraan, sila lawati: Pengenalan kepada Pengaturcaraan ! !

Atas ialah kandungan terperinci Apakah yang perlu kita perhatikan semasa melaksanakan kunci teragih dalam Redis? [Ringkasan perkara yang memerlukan perhatian]. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:cnblogs.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam