Maison > Article > base de données > Comment utiliser Redis pour verrouiller les ressources
À mesure que cette technologie continue d'être mise à jour et itérée, le concept de distribution prend de plus en plus de poids dans les entreprises ! Lorsqu'on parle de verrous distribués, on parlera inévitablement de verrous distribués. À ce stade, il existe trois méthodes principales d'implémentation de verrous distribués, Zookeeper
, DB
, Redis. code>, nous utilisons Redis comme exemple dans cet article ! Zookeeper
、DB
、Redis
,我们本篇文章以Redis为例!
从我们的角度来看,这三个属性是有效使用分布式锁所需的最低保证。
安全特性:互斥。在任何给定时刻,只有一个客户端可以持有锁。
活力属性:无死锁。最终,即使锁定资源的客户端崩溃或分区,也始终可以获得锁。
活动性:容错能力。只要大多数Redis节点都处于运行状态,客户端就可以获取和释放锁。
我们使用Redis锁定资源的最简单方法是:
在实例中创建锁。
锁通常使用Redis过期功能在有限时间存在,因此最终将被释放,最终超过给定期限会被删除。
当客户端需要释放资源时,它将删除锁。
乍一看,似乎并没有什么问题。但是不妨我们深究一下,这种实现方案在redis单机环境下似乎并没有什么问题!但是如果节点宕了呢?好吧,那么让我们添加一个slave
节点!如果主服务器宕机了,就使用这个节点!但是我们不妨来看看她真的能保证可用吗?
在谈论这个的致命缺陷时,我们需要了解一个知识点,Redis复制是异步的。
客户端A获取主服务器中的锁。
在将锁复制传输到从机之前,主机崩溃。
slave
晋升为
master
。
客户端B获取锁,因为从机并没有该锁的对象,获取成功!
显然,这样是不对的,主节点因为没来得及同步数据就宕机了,所以从节点没有该数据,从而造成分布式锁的失效,那么作者antirez
的观点是如何解决这个呢?
作者认为,我们应该使用多个Redis
,这些节点是完全独立的,不需要使用复制或者任何协调数据的系统,多个redis系统获取锁的过程就变成了如下步骤:
以毫秒为单位获取当前的服务器时间
尝试使用相同的key和随机值来获取锁,对每一个机器获取锁时都应该有一个超时时间,比如锁的过期时间为10s那么获取单个节点锁的超时时间就应该为5到50毫秒左右,他这样做的目的是为了保证客户端与故障的机器连接,耗费多余的时间!超时间时间内未获取数据就放弃该节点,从而去下一个节点获取,直至将所有节点全部获取一遍!
获取完成后,获取当前时间减去步骤一获取的时间,当且仅当客户端半数以上获取成功且获取锁的时间小于锁额超时时间,则证明该锁生效!
获取锁之后,锁的超时时间等于
设置的有效时间-获取锁花费的时间
Fonctionnalités de sécurité : Exclusion mutuelle. A un moment donné, un seul client peut détenir le verrou.
Attribut Vitalité : pas d'impasse. En fin de compte, le verrou peut toujours être obtenu même si le client verrouillant la ressource plante ou se partitionne.
2. Défis posés par la mise en œuvre multi-nœuds de verrous distribués dans Redis
La façon la plus simple pour nous d'utiliser Redis pour verrouiller les ressources est : #🎜 🎜##🎜🎜# À première vue, il ne semble y avoir rien de mal. Mais regardons de plus près. Cette solution d’implémentation ne semble poser aucun problème dans un environnement autonome Redis ! Mais que se passe-t-il si le nœud tombe en panne ? D'accord, ajoutons donc un nœud
Lorsque le client doit libérer la ressource, il supprimera le verrou.
- Créez un verrou dans l'instance.
- Les verrous existent généralement pour une durée limitée grâce à la fonction d'expiration de Redis, ils finiront donc par être libérés et seront éventuellement supprimés après une période donnée.
esclave
! Si le serveur principal est en panne, utilisez ce nœud ! Mais jetons un coup d’œil et voyons si sa disponibilité est vraiment garantie ? #🎜🎜##🎜🎜#Lorsque nous parlons du défaut fatal de cela, nous devons comprendre un point de connaissance, La réplication Redis est asynchrone.
#🎜🎜#esclave
est promu au
maître
. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Le client B acquiert la serrure car l'esclave n'a pas d'objet pour la serrure, et l'acquisition est réussie ! #🎜🎜##🎜🎜##🎜🎜##🎜🎜#De toute évidence, c'est faux. Le nœud maître est tombé en panne avant d'avoir eu le temps de synchroniser les données, donc le nœud esclave n'a pas les données, provoquant le verrouillage distribué. échouer. Alors, comment l'auteur antirez
résout-il ce problème ? #🎜🎜##🎜🎜#3. Algorithme Redlock#🎜🎜##🎜🎜#L'auteur pense que nous devrions utiliser plusieurs Redis
. Ces nœuds sont complètement indépendants et n'ont pas besoin d'utiliser la réplication ou. tout autre Pour les systèmes qui coordonnent les données, le processus d'acquisition de verrous par plusieurs systèmes Redis se déroule comme suit : #🎜🎜#Le temps effectif défini - le temps nécessaire pour acquérir la serrure
#🎜🎜##🎜🎜##🎜🎜##🎜🎜#Si plus de la moitié des machines qui acquièrent la serrure ne sont pas satisfaites , ou le délai d'expiration du verrouillage a été calculé. S'il y a une opération anormale telle qu'un nombre négatif, le système tentera de déverrouiller toutes les instances. Même si certaines instances ne parviennent pas à acquérir le verrou, elles seront quand même tentées de se déverrouiller ! #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Libérez le verrou, libérez simplement le verrou dans toutes les instances, que le client pense ou non pouvoir verrouiller avec succès l'instance donnée. #🎜🎜##🎜🎜##🎜🎜##🎜🎜# 4. Mais Redlock peut-il vraiment résoudre le problème ? #🎜🎜##🎜🎜#Martin Kleppmann a publié un article tâche, Redlock ne peut garantir la sécurité de la serrure ! #🎜🎜##🎜🎜# Il estime qu'il n'y a que deux utilisations des verrous#🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Améliorez l'efficacité et utilisez des verrous pour vous assurer qu'une tâche ne se termine pas doivent être exécutés deux fois. Par exemple (calcul très coûteux) #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Pour garantir l'exactitude, un mécanisme de verrouillage est utilisé pour garantir que les tâches sont effectuées dans un ordre de processus normal afin d'éviter que deux nœuds ne traitent le mêmes données en même temps Effectuez des opérations entraînant des conflits de fichiers et des pertes de données. #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Pour la première raison, nous avons une certaine tolérance pour les verrous Même si deux nœuds fonctionnent en même temps, l'impact sur le système sera. petit, cela coûte simplement plus de calculs et n’a aucun impact supplémentaire. À l'heure actuelle, l'utilisation d'un seul point de Redis peut très bien résoudre le problème. Il n'est pas nécessaire d'utiliser RedLock pour maintenir autant d'instances Redis et augmenter le coût de maintenance du système. #🎜🎜#Mais pour le deuxième scénario, il est plus prudent, car il est susceptible d'impliquer certaines transactions monétaires si le verrou échoue et que deux nœuds le traitent en même temps. sera une corruption de fichiers, une perte de données, une incohérence permanente ou une perte monétaire !
Supposons un scénario dans lequel nous avons deux clients. Chaque client doit obtenir un verrou avant de pouvoir enregistrer des données dans la base de données. Quels problèmes se produiront si nous utilisons l'algorithme RedLock pour l'implémenter ? Dans RedLock, afin d'éviter les blocages, le verrou a un délai d'expiration, mais Martin
pense que ce n'est pas sûr ! L'organigramme ressemble à ceci ! Martin
认为这是不安全的!该流程图类似于这样!
客户端1获取到锁成功后,开始执行,执行到一半系统发生Full GC ,系统服务被挂起,过段时间锁超时了。
客户端2等待客户端1的锁超时后,成功的获取到锁,开始执行入库操作,完成后,客户端1完成了Full GC,又做了一次入库操作!这是不安全的!如何解决呢?
Martin
提出来一种类似乐观锁的实现机制,示例图如下:
客户端1长时间被挂起后,客户端2获取到锁,开始写库操作,同时携带令牌 34
,写库完成后,客户端1苏醒,开始进行入库操作,但是因为携带的令牌为33 小于最新令牌,该次提交就被拒绝!
即使系统出现问题导致挂起,该思路似乎完备,可确保数据仍能得到正确处理。但是仔细想一下:
如果仅当您的令牌大于所有过去的令牌时,数据存储区才能始终接受写入,则它是可线性化的存储区,相当与使用数据库来实现一个 分布式锁系统,那么RedLock的作用就变的微乎其微!甚至不在需要使用redis保证分布式锁!
回想一下Redlock算法
获取锁的几个步骤,你会发现锁的有效性是与当前的系统时钟强依赖,我们假设:
我们有,A B C D E 五个redis节点:
客户端1获取节点A,B,C的锁定。由于网络问题,无法访问D和E。
节点C上的时钟向前跳,导致锁过期。
客户端2获取节点C,D,E的锁定。由于网络问题,无法访问A和B。
现在,客户1和2都认为他们持有该锁。
如果C在将锁持久保存到磁盘之前崩溃并立即重新启动,则可能会发生类似的问题。
Martin认为系统时间的阶跃主要来自两个方面(以及作者给出的解决方案):
人为修改。
对于人为修改,能说啥呢?人要搞破坏没办法避免。
从NTP服务收到了一个跳跃时时钟更新。
需要运维人员处理NTP接受阶跃时钟更新的问题。需要将阶跃的时间更新到服务器的时候,应当采取小步快跑的方式。多次修改,每次更新时间尽量小。
我们回顾 1 观点,深究抽象出现这个缺陷的根本原因,就是为了解决由于系统宕机带来的锁失效而给锁强加了一个失效时间,异常情况下,程序(业务)执行的时间大于锁失效时间从而造成的一系列的问题,我们能否从这方面去考虑,从而用程序来解决这个样一个死局 呢?
我们可以保证业务程序执行时间绝对小于锁超时时间,这样就可以避免锁失效时间小于业务时间的问题
java语言中redisson
Une fois que le client 2 a attendu l'expiration du verrouillage du client 1, il obtient le verrou avec succès et commence à effectuer l'opération d'entreposage. Une fois terminé, le client 1 termine le GC complet et effectue une autre opération d'entreposage ! C'est dangereux ! Comment le résoudre ? Martin
a proposé un mécanisme d'implémentation similaire au verrouillage optimiste. L'image d'exemple est la suivante :
34
en même temps. Après avoir écrit la bibliothèque, le client 1 s'est réveillé et a commencé. pour entrer. Opération de bibliothèque, mais comme le jeton transporté est 33, ce qui est inférieur au dernier jeton, la soumission est rejetée ! 🎜🎜Même s'il y a un problème avec le système qui provoque un blocage, cette idée semble complète pour garantir que les données sont toujours traitées correctement. Mais réfléchissez-y : 🎜algorithme Redlock
pour obtenir le verrou, vous constaterez que l'efficacité du verrou est lié au système actuel L'horloge est fortement dépendante, nous supposons : 🎜🎜Nous avons, A B C D E cinq nœuds redis : 🎜redisson
implémente une méthode pour garantir que le délai d'expiration du verrou est absolument supérieur au mécanisme du temps d'exécution du programme métier. Officiellement appelé mécanisme de surveillance (Watchdog), son principe principal est qu'une fois que le programme a acquis avec succès le verrou, il créera un thread enfant pour renouveler continuellement le verrou jusqu'à ce que le verrou soit libéré ! Son diagramme schématique ressemble à peu près à ceci : 🎜🎜🎜
🎜🎜redisson utilise un thread démon pour renouveler le verrou. (Le rôle du thread démon : lorsque le 🎜thread principal🎜 est détruit, il sera détruit avec le 🎜thread principal🎜.) Pour empêcher le thread de continuer à se renouveler. une fois le programme arrêté, provoquant la mort de Lock ! 🎜De plus, Redisson implémente et optimise également l'algorithme RedLock, le verrouillage équitable, le verrouillage réentrant, la chaîne et d'autres opérations, rendant la mise en œuvre du verrouillage distribué Redis plus facile et plus efficace !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!