# 4.1 分布式锁
# 4.1.1 基础介绍
分布式锁是一种用于在分布式系统中协调对共享资源访问的机制。它确保在任意时刻只有一个进程或线程可以访问被保护的资源,从而避免并发冲突和数据不一致的问题。
# 4.1.2 何时使用
当多个进程或线程需要同时访问共享资源,并且对资源的访问必须是互斥的时,就需要使用分布式锁。例如,在分布式数据库中,多个进程可能需要同时对数据库进行写操作,而分布式锁可以确保每次只有一个进程能够成功写入,避免数据冲突。
# 4.1.3 如何使用
以下以redis分布式锁为例展示如何使用,其他分布式锁的使用方法类似。
- 安装依赖
$ go get github.com/dobyte/due/v2@v2.4.2
$ go get github.com/dobyte/due/lock/redis/v2@e5cd009
1
2
2
- 导入依赖
import (
"github.com/dobyte/due/v2/lock"
"github.com/dobyte/due/lock/redis/v2"
)
1
2
3
4
2
3
4
- 设置锁制造商
// 在全局设置锁制造商
lock.SetMaker(redis.NewMaker())
1
2
2
- 使用分布式锁
// 构造一个锁头
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 接口文档
https://pkg.go.dev/github.com/dobyte/due/v2/lock (opens new window)
- 全部实现
# 4.1.4 redis分布式锁示例
以下完整示例详见:lock-redis-example (opens new window)
- 创建项目
$ mkdir lock-redis-example
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
2
3
4
- 启动配置
文件位置: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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 编写示例
文件位置: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
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
- 运行示例
$ cd lock-redis-example
$ go run main.go
total : 100
latency : 0.317310s
1
2
3
4
2
3
4
# 4.1.5 memcache分布式锁示例
以下完整示例详见:lock-memcache-example (opens new window)
- 创建项目
$ mkdir lock-memcache-example
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
2
3
4
- 启动配置
文件位置: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
2
3
4
5
6
7
8
9
10
11
- 编写示例
文件位置: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
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
- 运行示例
$ cd lock-memcache-example
$ go run main.go
total : 100
latency : 0.008639s
1
2
3
4
2
3
4