一文弄懂分布式场景中各种锁的原理及使用

本文详细探讨了分布式锁的不同实现方式,包括语言层面的乐观锁与互斥锁、数据库MySQL的行级锁、Zookeeper与Etcd的分布式锁、Redis的分布式锁及其优化、以及Redlock算法。针对每种锁,文章分析了其工作原理、优缺点以及可能遇到的问题,强调在实际应用中应根据场景选择合适的锁实现。
摘要由CSDN通过智能技术生成

1. 语言层面的锁

乐观锁:

原子操作中的比较并交换简称CAS(Compare And Swap),在sync/atomic包中,这类原子操作由名称以CompareAndSwap为前缀的若干个函数提供
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer,old, new unsafe.Pointer) (swapped bool)

使用AddInt32函数对int32值执行添加原子操作:
func main() {
	var n int32
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			atomic.AddInt32(&n, 1)
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println(atomic.LoadInt32(&n)) // output:1000
}

golang中原子操作CompareAndSwap:

 

CompareAndSwap函数会先判断参数addr指向的操作值与参数old的值是否相等,仅当此判断得到的结果是true之后,才会用参数new代表的新值替换掉原先的旧值,否则操作就会被忽略。atmoic原子操作总是假设被操作值未曾被改变(即与旧值相等),并一旦确认这个假设的真实性就立即进行值替换。在被操作值被频繁变更的情况下,CAS操作并不那么容易成功所以需要不断进行尝试,直到成功为止。

互斥锁:

golang中互斥锁的一个经典实现就是sync包下的sync.mutex,下面以并发访问slice为例:

slice是对数组一个连续片段的引用,当 slice 长度增加的时候,可能底层的数组会被换掉。当在换底层数组之前,切片同时被多个 goroutine 拿到,并执行 append 操作。那么很多 goroutine 的 append 结果会被覆盖,导致 n 个 gouroutine append 后,长度小于n,互斥锁解决并发访问slice的场景:
func main() {
	slc := make([]int, 0, 1000)
	var wg sync.WaitGroup
	var lock sync.Mutex

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func(a int) {
			defer wg.Done()
			// 加锁
			lock.Lock()
			defer lock.Unlock()
			slc = append(slc, a)
		}(i)
	}
	wg.Wait()
	fmt.Println(len(slc))
}

缺点:分布式部署环境下锁会失效

2. mysql数据库实现锁

方案一:使用数据库的唯一性来实现资源锁定,比如主键和唯一索引等;建立一个字段为唯一索引,加入一条数据即表示加锁了,删除这条数据就解锁了;具体,使用很简单,具体实现就不再阐述
数据库中,为了实现高并发的数据访问,对数据进行多版本处理,并通过事务的可见性来保证事务能看到自己应该看到的数据版本,
方案二:select for update解决并发数据查询更新的问题
SET AUTOCOMMIT=0; 
BEGIN WORK; 
SELECT category_id FROM blog_article WHERE id=3 FOR UPDATE;
UPDATE blog_article SET category_id = 3;
# 在commit前其它事物无法对此行数据进行修改
COMMIT WORK;
在另外一个窗口执行新的事物修改:
UPDATE blog_article SET category_id = 2 WHERE id = 3;

会发现事物无法立即执行,会等待for update那条事物commit,如果此时长时间未commit则会超时:

[SQL]UPDATE blog_article SET category_id = 2 WHERE id = 3;
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction

缺点:单机mysql负载能力有限,mysql锁性能低下,select for update加锁如果where条件后的字段非主键则"表锁",如果是主键则为"行锁"

3. zookeeper、etcd实现分布式锁

zookeeper实现分布式锁:

 

利用 ZooKeeper 支持临时顺序节点的特性,可以实现分布式锁;当客户端对某个方法加锁时,在 ZooKeeper
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值