C C++最全分布式锁笔记_数据库分布式锁(1),从草根到百万年薪程序员的十年风雨之路

img
img

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

需要这份系统化的资料的朋友,可以添加戳这里获取

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

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"strings"
	"sync"
	"time"
)

var SqlDB *sql.DB

var wg sync.WaitGroup

func clientFirst() {
	defer wg.Done()
	for {
		fmt.Println("client first  add lock...")
		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 first add lock failed", err.Error())
			continue //
		}
		fmt.Println("client first 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("first delete lock failed ...", err.Error())
		} else {
			fmt.Println("client first  delete lock...")
		}

		time.Sleep(time.Millisecond * 15)
	}

}

func clientSecond() {
	defer wg.Done()
	for {
		fmt.Println("client second add lock...")
		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:" + 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从业务中睡眠中醒来,在释放锁的时候,将该锁匙放掉了,这样导致了问题的产生。如下图:

img
img

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

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

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

-1715705251715)]

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

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

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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值