Elasticsearch由浅入深(四)ES并发冲突、悲观锁与乐观锁、_version乐观锁并发

ES并发冲突

举个例子,好比是电商场景下,假设说,咱们有个程序,工做的流程是这样子的:缓存

  1. 读取商品信息(包含了商品库存)
  2. 用户下单购买
  3. 更新商品信息(主要是将库存减1)

咱们好比我们的程序就是多线程的,因此可能有多个线程并发的去执行上述的3步骤流程多线程

有一个牙膏,库存100件,如今,同时有两我的都过来读取了牙育的数据,而后下单购买了这管牙膏,此时两个线程并发的服务于两我的,同时在进行商品库存数据的修改并发

总有一个线程是先到的,假设就是线程A ,此时线程A就会先将牙育的库存设置为99件,而后线程B再次将牙育的库存设置为99件,此时结果就已经错了异步

正确的状况下,咱们指望的,应该是说,线程A将库存-1,设置为99件;而后线程B接着这个99件,将库存-1,变为98件,而后设置到ES中。最终ES中应该库存是98件才对啊.spa

普通的ES操做流程:线程

  1. 先get document数据,商品信息,显示到网页上,同时在内存中缓存该document的数据
  2. 当网页发生了购买以后,直接基于内存中的数据,进行计算和操做
  3. 将计算后的结果写回ES中

上面说的这个流程和过程,其实就是ES中的并发冲突问题,会致使数据不许确:版本控制

  1. 有些场景下,实际上是无所谓的,不care这个数据不正确的事情,好比说,咱们若是就只是简单的将数据写入ES ,不管数据是什么样的,均可以;还有些状况下,即便是算错了,也能够 
  2. 当并发操做ES的线程越多,或者并发请求越多;或者是读取一份数据,供用户查阅和操做的时间越长,由于这段时间里极可能数据在ES中已经被修改了,那么咱们拿到的就是旧数据,基于 1旧数据去操做,后面结果确定就错了

悲观锁与乐观锁

优缺点:code

  • 悲观锁
    优势:方便,直接加锁,对应用程序来讲,透明,不须要作额外的操做;
    缺点:并发能力很低,同一时间只能有一条线程操做数据,
  • 乐观锁
    优势:并发能力很高,不给数据加锁,大量线程并发操做;
    缺点:麻烦,每次更新的时候,都要先比对版本号,而后可能须要从新加载数据,再次修改,再写;这个过程,可能要重复好几回

悲观锁并发控制机制

悲观锁并发控制方案,就是在各类状况下,都上锁。上锁以后,就只有一个线程能够操做这一条数据了,固然,不一样的场景下,上的敏不一样,行级锁,表级锁,读锁,写锁。blog

乐观锁并发控制机制

乐观锁是不加锁的, ,每一个线程均可以任意操做内存

线程B去判断,当前数据的版本号,version=1,与es中的数据的版本号, version=2,是否相同?明显是不一样的。版本号不一样,说明数据已经被其余人修改过了。此时用户B不会用99去更新。而是从新去es中读取最新的数据版本,99件,再次减 1,变为98件,再执行上述流程,就能够写入

基于_version进行乐观锁并发控制

示例数据:

PUT /test_index/test_type/6
{
  "test_field": "test test"
}
{
  "_index": "test_index",
  "_type": "test_type",
  "_id": "6",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

第一次建立一个document的时候,它的_version内部版本号就是1;之后,每次对这个document执行修改或者删除操做,都会对这个_version版本号自动加1;哪怕是删除,也会对这条数据的版本号加1

 

{
  "found": true,
  "_index": "test_index",
  "_type": "test_type",
  "_id": "6",
  "_version": 4,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

咱们会发现,在删除一个document以后,能够从一个侧面证实,它不是当即物理删除掉的,由于它的一些版本号等信息仍是保留着的。先删除一条document,再从新建立这条document,其实会在delete version基础之上,再把version号加1

ES后台同步:知识点: es的后台,不少的这种相似于replica同步请求,都是多线程异步的,也就是说,多个修改请求之间,是乱序的,没有顺序的,可能后修改的先到,先修改的后到

假设es内部没有乐观锁并发控制机制:

  1. 后修改的先到了,此时version=2, field-test3
  2. 而后先修改的后到了,此时若是不基于version进行版本控制,直接将field-test2盖过去,此时数据就错了
  3. 由于按照顺序来讲,应该是数据从field=test1 ,先变为 frield-test2,再变为field-test3

es内部的多线程异步并发修改时,是基于本身的_version版本号进行,在后修改先到时,那么field-test3, version=2,先修改后到时,先修改的field=test2 , version-1 ,此时会比较一下version号,是否相等,若是不相等的话,那么就直接将 field-test2这条数据给扔掉,就不须要了,这样的话,结果就会保持为一个正确的状态, field-test3

相关文章
相关标签/搜索