Elasticsearch-85-基于全局锁实现悲观锁并发控制

并发控制的几种方案

结合之前的文件系统的案例, 假设有多个线程过来,要并发的给/workspace/projects/helloworld下的README.txt修改文件名,这个时候就要进行并发的控制,避免多线程的并发安全问题.

基于version的乐观锁,先获取要修改的信息的版本号,然后和自己的版本号对比,如果版本号不一样了,就意味着有别的线程已经修改过这个数据了,必须重新获取新的版本号再次尝试修改

然后是悲观锁,就是上来就先尝试给这个数据加锁,如果要是加锁成功了,这个时候这个数据只有当前这个线程可以操作,其他线程过来也尝试加锁,发现已经被上一个线程加锁了,就一直重新上锁,直到获得锁以后才可以对数据进行操作

全局锁是粒度最粗的

全局锁的实现

就是直接锁掉整个要操作的index

首先,第一步,上锁

1
2
PUT /fs/lock/global/_create
{}

  • fs: 要上锁的index
  • lock:就是指定一个对这个index上全局锁的一个type
  • global: 就是这个全局锁对应的这个doc的id
  • _create: 强制必须是创建,如果这个doc已经存在,就创建失败,报错

其实就是添加了一个空的document,语法是和添加document一样的 /index/type/id

创建完成后,如果另一个线程同时尝试上锁,也就是再执行一次这个请求

1
2
PUT /fs/lock/global/_create
{}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][global]: version conflict, document already exists (current version [1])",
"index_uuid": "RWijfql1Qlqa_OPoor0Ubw",
"shard": "2",
"index": "fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][global]: version conflict, document already exists (current version [1])",
"index_uuid": "RWijfql1Qlqa_OPoor0Ubw",
"shard": "2",
"index": "fs"
},
"status": 409
}

可以看到,锁已经存在的情况下,其他线程再尝试加锁就会报错,没获得锁的线程就一直重复尝试上锁,直到成功

此时,第一个成功上锁的线程就可以对document进行各种操作,比如更新文件名

1
2
3
4
5
6
POST /fs/file/1/_update
{
"doc": {
"name": "README1.txt"
}
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
{
"_index": "fs",
"_type": "file",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}

修改完成,最后一步,释放锁,也就是删除/fs/lock/global这个document

1
DELETE /fs/lock/global

第二个线程之前上锁失败,但是这时候第一个线程已经释放了锁,所以第二个线程上锁成功,然后他也可以对这个index里面的数据做各种操作,最后一步delete掉,释放锁就好了

全局锁的优缺点

优点: 操作简单,使用容易,成本低
缺点: 直接把整个index锁上了,这时候别的线程对index中所有的doc的操作都会被block住,导致整个系统的并发能力很低

适用场景

上锁释放锁的操作不是很频繁,每次上锁之后,执行的操作耗时不会太长,可以使用这种方式, 方便