# 4.1 分布式锁

# 4.1.1 基础介绍

分布式锁是一种用于在分布式系统中协调对共享资源访问的机制。它确保在任意时刻只有一个进程或线程可以访问被保护的资源,从而避免并发冲突和数据不一致的问题。

# 4.1.2 何时使用

当多个进程或线程需要同时访问共享资源,并且对资源的访问必须是互斥的时,就需要使用分布式锁。例如,在分布式数据库中,多个进程可能需要同时对数据库进行写操作,而分布式锁可以确保每次只有一个进程能够成功写入,避免数据冲突。

# 4.1.3 如何使用

以下以redis分布式锁为例展示如何使用,其他分布式锁的使用方法类似。

  1. 安装依赖
$ go get github.com/dobyte/due/v2@v2.4.2
$ go get github.com/dobyte/due/lock/redis/v2@e5cd009
1
2
  1. 导入依赖
import (
	"github.com/dobyte/due/v2/lock"
	"github.com/dobyte/due/lock/redis/v2"
)
1
2
3
4
  1. 设置锁制造商
// 在全局设置锁制造商
lock.SetMaker(redis.NewMaker())
1
2
  1. 使用分布式锁
// 构造一个锁头
locker := lock.Make("lock_name")

// 申请锁
if err := locker.Acquire(context.Background()); err != nil {
    log.Errorf("Failed to acquire lock: %v", err)
    return
}

// 释放锁
defer locker.Release(context.Background())

// 锁申请成功,执行临界区代码
// ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 接口文档

https://pkg.go.dev/github.com/dobyte/due/v2/lock (opens new window)

  1. 全部实现

# 4.1.4 redis分布式锁示例

以下完整示例详见:lock-redis-example (opens new window)

  1. 创建项目
$ mkdir lock-redis-example
1
  1. 安装依赖
$ cd lock-redis-example
$ go mod init lock-redis-example
$ go get github.com/dobyte/due/v2@v2.4.2
$ go get github.com/dobyte/due/lock/redis/v2@e5cd009
1
2
3
4
  1. 启动配置

文件位置:lock-redis-example/etc/etc.toml (opens new window)

[lock.redis]
    # 客户端连接地址
    addrs = ["127.0.0.1:6379"]
    # 数据库号
    db = 0
    # 用户名
    username = ""
    # 密码
    password = ""
    # 私钥文件
    keyFile = ""
    # 证书文件
    certFile = ""
    # CA证书文件
    caFile = ""
    # 最大重试次数
    maxRetries = 3
    # key前缀
    prefix = "due:lock"
    # 锁过期时间(自动续约),支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为3s
    expiration = "3s"
    # 循环获取锁的频率间隔时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为20ms
    acquireInterval = "20ms"
    # 循环获取锁的最大重试次数,默认为0,<=0则为无限次
    acquireMaxRetries = 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  1. 编写示例

文件位置:lock-redis-example/main.go (opens new window)

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/dobyte/due/lock/redis/v2"
	"github.com/dobyte/due/v2/lock"
)

func main() {
	// 设置锁制造商
	lock.SetMaker(redis.NewMaker())

	var (
		wg      sync.WaitGroup
		ctx     = context.Background()
		total   = 100
		counter int
	)

	wg.Add(total)

	startTime := time.Now().UnixNano()

	for range total {
		go func() {
			defer wg.Done()

			locker := lock.Make("lock")

			if err := locker.Acquire(ctx); err == nil {
				defer locker.Release(ctx)

				counter++
			}
		}()
	}

	wg.Wait()

	fmt.Printf("total	: %d\n", total)
	fmt.Printf("latency	: %fs\n", float64(time.Now().UnixNano()-startTime)/float64(time.Second))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  1. 运行示例
$ cd lock-redis-example
$ go run main.go
total   : 100
latency : 0.317310s
1
2
3
4

# 4.1.5 memcache分布式锁示例

以下完整示例详见:lock-memcache-example (opens new window)

  1. 创建项目
$ mkdir lock-memcache-example
1
  1. 安装依赖
$ cd lock-memcache-example
$ go mod init lock-memcache-example
$ go get github.com/dobyte/due/v2@v2.4.2
$ go get github.com/dobyte/due/lock/memcache/v2@e5cd009
1
2
3
4
  1. 启动配置

文件位置:lock-memcache-example/etc/etc.toml (opens new window)

[lock.memcache]
    # 客户端连接地址
    addrs = ["127.0.0.1:11211"]
    # key前缀
    prefix = "due:lock"
    # 锁过期时间(自动续约),支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为3s
    expiration = "3s"
    # 循环获取锁的频率间隔时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为20ms
    acquireInterval = "20ms"
    # 循环获取锁的最大重试次数,默认为0,<=0则为无限次
    acquireMaxRetries = 0
1
2
3
4
5
6
7
8
9
10
11
  1. 编写示例

文件位置:lock-memcache-example/main.go (opens new window)

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/dobyte/due/lock/memcache/v2"
	"github.com/dobyte/due/v2/lock"
)

func main() {
	// 设置锁制造商
	lock.SetMaker(memcache.NewMaker())

	var (
		wg      sync.WaitGroup
		ctx     = context.Background()
		total   = 100
		counter int
	)

	wg.Add(total)

	startTime := time.Now().UnixNano()

	for range total {
		go func() {
			defer wg.Done()

			locker := lock.Make("lock")

			if err := locker.Acquire(ctx); err == nil {
				defer locker.Release(ctx)

				counter++
			}
		}()
	}

	wg.Wait()

	fmt.Printf("total	: %d\n", total)
	fmt.Printf("latency	: %fs\n", float64(time.Now().UnixNano()-startTime)/float64(time.Second))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  1. 运行示例
$ cd lock-memcache-example
$ go run main.go
total   : 100
latency : 0.008639s
1
2
3
4