Mysql乐观锁和悲观锁

# 向mysql请求一把锁(使用for update)
select * from inventory where goods = 421 for update
# 这里是具体的执行逻辑
update invenory set stocks = 19
# 释放锁

注意:在使用for update时,默认每个sql语句都是i自动提交的

select @@autocommit

查看是否自动提交默认为1,为自动提交

set autocommit = 0

此时就开启锁了,其他线程执行相同的sql语句将会被锁住

set autocommit = 0;
select * from inventory where goods = 421 for update;
commit;


# 注意:此时set autocommit = 0 只在当前窗口有效,并没有改变musql的自动提交熟悉,在别的窗口下查看@@autocommit 依然是1

mysql的锁如果查询的字段建立了索引,那么这个锁的粒度是一行数据,如果没有建立索引,那么这个锁会升级成表锁。

# 如果在索引上进行查询,查询结果没有任何满足条件的话,不会进行锁表。但是如果在非索引上进行查询,没有查询到满足条件的数据,依然会进行锁表。

gorm实现悲观锁

# 首先要开启事务

	tx := db.Begin()
	good := Goods{}
   # 还是在查询语句开启悲观锁
	_ = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", 1).First(&good).Error
	good.Count -= 1
	//_ = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Updates(&good)
	_ = tx.Updates(&good)
	tx.Commit()

乐观锁:

数据库乐观锁,并没有对数据库加任何的锁,它是在代码层面上,通过预期值和版本号,对其进行更新操作。

redis分布式锁

redsync源码解读

package main

import (
	goredislib "github.com/go-redis/redis/v8"
	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v8"
)

func main() {
	// Create a pool with go-redis (or redigo) which is the pool redisync will
	// use while communicating with Redis. This can also be any pool that
	// implements the `redis.Pool` interface.
	client := goredislib.NewClient(&goredislib.Options{
		Addr:     "139.198.37.13:6379",
		Password: "root1234",
	})
	pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)

	// Create an instance of redisync to be used to obtain a mutual exclusion
	// lock.
	rs := redsync.New(pool)

	// Obtain a new mutex by using the same name for all instances wanting the
	// same lock.
	mutexname := "my-global-mutex"
	mutex := rs.NewMutex(mutexname)

	// Obtain a lock for our given mutex. After this is successful, no one else
	// can obtain the same lock (the same mutex name) until we unlock it.
	if err := mutex.Lock(); err != nil {
		panic(err)
	}

	// Do your work that requires the lock.

	// Release the lock so other processes or threads can obtain a lock.
	if ok, err := mutex.Unlock(); !ok || err != nil {
		panic("unlock failed")
	}
}

setnx作用:

将获取值和设置值变成原子性的操作

问题?

如果服务器宕机了,锁没有释放怎么办(死锁)?

1. 设置过期时间?
2. 如果设置了过期时间,但是业务逻辑没有执行完怎么办?
	一. 在过期的时候,刷新一下
	二. 需要自己去启动一个协程完成延时任务(redsync没有自动帮我们做,要根据业务去完成)
	同时延时的接口会带来负面影响,如果其中一个服务执行的慢,卡住了,需要10s才能执行完,但是你卡住你就会一直去申请延长锁,导致别人一直获取不到,这很要命。
	
# 分布式锁需要解决的问题:
	1. 互斥性
	2. 死锁
	3. 安全性
		锁只能被持有该锁的用户删除,不能被其他用户删除
		> 当时设置的value值只能被当时设置value的goroutine知道,
		> 在删除时会取出当前值和自己保存下来的值对比一下
		
# 即使是这样实现了分布式锁,还是会有问题的 redLock

Q.E.D.


勤俭节约,艰苦奋斗。