共享锁和排它锁
共享锁: 这份数据是共享的,然后多个线程过来,都可以获取同一个数据的共享锁,然后对这个数据执行读操作.
排它锁: 是排他的操作,只能一个线程获取排他锁,然后执行增删改操作
读写锁分离
如果是要读取数据的话,那么任意多少个线程都可以进来然后读取数据,每个线程都可以上一个共享锁,但是这个时候如果有线程过来修改数据,会尝试上排他锁排它锁会跟共享锁互斥,也就是说,如果有人已经上了共享锁了,那么排它锁就不能上,就得等.
就是说如果有人在读数据,就不允许别人来修改数据,反之也是一样的.
如果有人在修改数据,就是加了排他锁,这时候其他线程过来也要修改数据,也会尝试加排它锁,此时就会失败,锁冲突,就必须等待重试,同时只能有一个线程修改数据,如果有线程过来读取数据,就是尝试加共享锁,此时也会失败,因为共享锁和排它锁是互斥的
如果有线程再修改数据,就不许别的线程再来修改,也不许别的线程读取数据
案例
多个线程上共享锁
先试一下共享锁,多个线程同时读取数据. 上共享锁也需要一个groovy脚本,和之前一样,脚本内容是
1 | if (ctx._source.lock_type == 'exclusive') { assert false }; ctx._source.lock_count++ |
就是如果锁已经存在了,判断这个锁的类型是排它锁的话,直接报错,是共享锁的话,就把锁的数量加1
假设,先有一个线程过来上了一个共享锁
1 | POST /fs/lock/1/_update |
返回值:
1 | { |
添加的数据,一个是锁的类型,一个是锁的数量,已经成功上了一个共享锁了
这时候又有一个线程过来上共享锁
1 | POST /fs/lock/1/_update |
返回值:
1 | { |
执行还是成功的,然后查询一下这个锁
1 | GET /fs/lock/1 |
返回值:
1 | { |
lock_count是2,就是加了两个共享锁. 就是有线程上了共享锁以后,其他线程还要再上,直接上就可以了,只是lock_count + 1
共享锁没有释放的情况下 上排他锁
上排他锁的请求
1 | PUT /fs/lock/1/_create |
这里用的是_create请求,就是强制创建,要求lock必须不能存在,但是之前已经有了共享锁了,/fs/lock/1是存在的 所以这里会创建失败,报错
1 | { |
释放共享锁
这里也是需要一个释放共享锁的groovy脚本
1 | if(--ctx._source.lock_count == 0){ctx.op = 'delete'} |
就是执行脚本的时候,先把lock_count-1 然后判断是不是0,是的话直接删除掉.
1 | POST /fs/lock/1/_update |
之前上了两个锁,执行两次,lock_count就是0了,然后就被删除了,所有的共享锁就都被释放了
再上排它锁
1 | PUT /fs/lock/1/_create |
返回值:
1 | { |
创建成功了,这时候如果再有其他线程过来申请排它锁
1 | PUT /fs/lock/1/_create |
返回值:
1 | { |
还是报错的,上锁失败,如果再上一个共享锁呢
1 | POST /fs/lock/1/_update |
返回值:
1 | { |
也是报错,加不了的
释放排他锁
直接把这个删除掉就好了
1 | DELETE /fs/lock/1 |