2024年分布式锁笔记_数据库分布式锁(2),2024年最新金九银十正确打开方式

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

	sqlstr := &strings.Builder{}
	sqlstr.WriteString("insert into resource_lock(resource_lock_key,resource_lock_val)value(?,?)")
	sqlargs := make([]interface{}, 0)
	sqlargs = append(sqlargs, "lock", "method_lock")
	_, err := SqlDB.Exec(sqlstr.String(), sqlargs...)
	if err != nil {
		fmt.Println("client second add lock failed", err.Error())
		time.Sleep(time.Millisecond * 3)
		continue //
	}
	fmt.Println("client second process things")

	// 解锁
	sqlstr.Reset()
	sqlstr.WriteString("delete from resource_lock where resource_lock_key = 'lock'")
	_, err = SqlDB.Exec(sqlstr.String())
	if err != nil {
		fmt.Println("client second failed to delete lock", err.Error())
	} else {
		fmt.Println("client second  delete lock...")
	}

	time.Sleep(time.Millisecond * 5)
}

}

func mointer() {
defer wg.Done()

curtime := ""
tmp := "2006-01-02 15:04:05"
for {
	sqlstr := "select update_time from resource_lock where resource_lock_key = 'lock'"
	var time_str string
	rows := SqlDB.QueryRow(sqlstr)
	err := rows.Scan(&time_str)

	if err != nil || err == sql.ErrNoRows {
		time.Sleep(time.Second * 1)
		continue
	}
	if len(curtime) == 0 {
		curtime = time_str
	}

	res, _ := time.ParseInLocation(tmp, time_str, time.Local)
	cur_res, _ := time.ParseInLocation(tmp, curtime, time.Local)
	tm := res.Unix() - cur_res.Unix()
	if tm >= 10 {
		strstr := "delete from resource_lock where resource_lock = 'lock'"
		fmt.Println("mointer delete unexpired lock")
		SqlDB.Exec(strstr)
		curtime = "" // 置空
	}

	time.Sleep(time.Millisecond * 5)

}

}
func main() {
var err error
wg.Add(4)

SqlDB, err = sql.Open("mysql", "root:passwd@tcp(localhost:port)/sql_db?charset=utf8")
if err != nil {
	fmt.Println("connect failed", err.Error())
}
fmt.Println("success to connect mysql db !")

go clientFirst()
go clientSecond()

go mointer()

go func() {
	defer wg.Done()
	wg.Wait()
}()

// 主进程阻塞
select {}

}



b.基于缓存模型的分布式锁


在缓存中redis当中,使用的是setnx命令进行实现的分布式锁,都是原子操作处理的,实现思想跟数据库一样,需要加锁的时候,在缓存中添加一条数据,释放锁的时候,删除掉该数据即可。如下所示:


加锁:如下代码利用uuid作为value值。



func redis_lock(lock_name string, acquire_time int, time_out time.Duration) (string, error) {

// """获取一个分布式锁"""
identifier := uuid.NewV1().String()
end := time.Now().Second() + acquire_time
lock := "string:lock:" + lock_name
for {

    if t := time.Now().Second(); t <= end {
        if client.SetNX(lock, identifier, time_out).Val() {
            return identifier, nil
        } else {
            // 获取生命周期时间,并设置过期时间
            client.Expire(lock, client.TTL(lock).Val())
        }
    } else {
        break
    }
    time.Sleep(time.Second)
}

return "", nil

}

如上代码,获取锁时,尝试获取锁,并且给予时间内尝试获取锁,超时就表示获取失败。并在失败时,检查之前的,并设置该锁的有效时间长,避免该锁一直存在,导致其他客户端无法获取锁。


 解锁:



func redis_unlock(lock_name string, identify string) {
lock := “string🔒” + lock_name
for {
val, err := client.Get(lock).Result()
fmt.Println(“unlock key-var”, lock, “-”, val)
if err == nil {
if val == identify {
client.Del(lock)
return
}
} else {
break
}
}
}
如上代码中,表示的是解锁。


但是如上也是有缺陷的:


a.A获取了锁,但是A的业务逻辑进入睡眠了,但是锁的有效期到了,redis中自动清理了该锁,B需要来加锁,也成功了获取了锁,但是,此刻A从业务中睡眠中醒来,在释放锁的时候,将该锁匙放掉了,这样导致了问题的产生。如下图:


![](https://img-blog.csdnimg.cn/a8e2cbe91be343cc960860f9497cb3fb.png)



所以使用时,避免业务休眠时间大于锁的有效期。从而避免出现如上图问题。


b.redis中使用get和del也要进行原子操作,使用lua脚本实现原子操作处理,如上代码修改如下:



const (
script = if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
)

func redis_unlock(lock_name string, identify string) {
lock := “string🔒” + lock_name
var count int = 0
for {
// 利用lua脚本来保证redis的get和del操作的原子性
if val, err := client.Eval(script, []string{lock}, identify).Result(); err != nil {
if !strings.Contains(err.Error(), “Noscript”) {
fmt.Println(“no script !!!”)
}

        // 尝试五次,放弃,所周期时间到了,也会释放该锁
        if count < 5 {
            break
        }
        count++

    } else {
        fmt.Println("unlock key-var", lock, "-", val)
        client.ScriptLoad(script)
        break
    }

}

}


如上代码,保证了原子操作,get&del,用于释放锁。


c.redis中实现了如下方式实现分布式锁:


RedLock算法


这个算法基于N个完全独立的Redis节点(通常情况下N可以设置成5),客户端可以分成以下5个步骤来完成获取锁的操作


![img](https://img-blog.csdnimg.cn/img_convert/258ae9b35b2fe7422a89c054c2f6f94a.png)
![img](https://img-blog.csdnimg.cn/img_convert/64e5060f810e17de038d47d2ae3e69dd.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值