State Machines in Distributed

global lock

Locks are used more or less when the system accesses a single resource, such as Java's Lock, etc. However, when multiple systems access resources, or when each instance in the cluster needs to access resources, a global lock needs to be established. Here are three A global lock method.

database

Leverage ACID

A lock can be created using ACID of a relational database

UPDATE LOCKTABLE SET INSTANCE=<instance_name> WHERE RESOURCE=XXXX AND INSTANCE IS NULL;

When the corresponding record is updated, it means that the lock has
been obtained. The following sql can be used to release the obtained lock.

UPDATE LOCKTABLE SET INSTANCE='' WHERE RESOURCE=XXXX AND INSTANCE=<instance_name>;

The above method looks good, but when the instance that acquires the lock crashes then the lock is always occupied

Use row locks

In order to solve the abnormal exit of the instance without releasing the lock, you can use the row lock of the database (ORACLE)

SELECT 1 FROM LOCKTABLE WHERE RESOURCE=XXXX FOR UPDATE;

In this way, the lock can be held before commit/rollback. If the caller disconnects, the database will automatically rollback. You can use NOWAIT+ loop query to prevent blocking

REDIS

The database can, of course, but it is not appropriate to hold a large number of locks for a long time to deal with a large number of resources. Let's see how Redis creates a global lock.

SETNX

SETNX is an abbreviation for set if not exist, that is, assign a value when the value does not exist

SETNX lock.resource 1
//hold the lock
DEL lock.resource

The above pseudo-code simply demonstrates how to acquire and release locks. Like the database method, this method also has the risk of instance downtime leading to deadlock.

SET

Unfortunately, there is no for update like in the database on Redis. An alternative is to use expire. That is, set a timeout time for the lock. If the time exceeds the automatic release of the lock, the timeout time here should be appropriate and not too long to allow other instances to wait, or too short to automatically release the instance before it ends.
Fortunately after Redis 2.6.12 the SET command can use expire and notexist

SET lock.resource <instance_name> NX EX timeout
//hold the lock
WATCH lock.resource
GET lock.resource
MULTI
if(getResult==<instance_name>)
    DEL lock.resource
EXEC

Use watch/multi to ensure race conditions

RedLock

To prevent a single Redis from being unavailable, multiple redis can be used, and if more than half of the nodes acquire the lock, it means that the lock is acquired, otherwise all the acquired locks are released.

Redison

如果不想自己造轮子,已经有现成的类库可以使用Redis创建全局锁了
Redison封装了锁的实现,提供可重入的锁的一系列实现,可以方便地使用
https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers

Zookeeper

redis的不足就是只能通过expire来控制锁持有者失联的情况。
zookeeper在这方面就有一定的优势,再加上zookeeper天生自带集群,在可靠性上优于redis
zookeeper可以创建ephemeral节点,当客户端断开连接节点自动删除,可以创建一个节点,最小值持有当前锁

create -e -s /LOCK/RESOURCE/REQUEST 1

之后判断如果当前节点最小就获得锁,如果没有就在前一个节点上加watch,在watch中再进行判断,这样就实现了等待获得锁的队列。

总结

全局锁在集群上的应用有不少,最常见的就如集群内CRON任务执行的管理等。这里主要介绍的还是悲观锁,在某些场景也可以使用乐观锁进行优化。

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326798602&siteId=291194637