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件才对啊.

普通的ES操作流程:

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

上面说的这个流程和过程,其实就是ES中的并发冲突问题,会导致数据不准确:

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

悲观锁与乐观锁

优缺点:

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

悲观锁并发控制机制

悲观锁并发控制方案,就是在各种情况下,都上锁。上锁之后,就只有一个线程可以操作这一条数据了,当然,不同的场景下,上的敏不同,行级锁,表级锁,读锁,写锁。

乐观锁并发控制机制

乐观锁是不加锁的, ,每个线程都可以任意操作

线程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

原文地址:https://www.cnblogs.com/wyt007/p/11382028.html

时间: 2024-08-01 16:27:47

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

ElasticSearch 并发冲突+悲观锁与乐观锁+基于_version和external version进行乐观锁并发控制

1.图解剖析Elasticsearch并发冲突问题 2.图解剖析悲观锁与乐观锁两种并发控制方案 3.图解Elasticsearch内部如何基于_version进行乐观锁并发控制 (1)_version元数据 PUT /test_index/test_type/6{ "test_field": "test test"} { "_index": "test_index", "_type": "test

通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底层实现浅尝辄止,但是在需要时能够知道去查什么. 首先要打消一种想法,就是一个锁只能属于一种分类.其实并不是这样,比如一个锁可以同时是悲观锁.可重入锁.公平锁.可中断锁等等,就像一个人可以是男人.医生.健身爱好者.游戏玩家,这并不矛盾.OK,国际惯例,上干货. 〇.synchronized与Lock

写文章 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底层实现浅尝辄止,但是在需要时能够知道去查什么. 首先要打消一种想法,就是一个锁只能属于一种分类.其实并不是这样,比如一个锁可以同时是悲观锁.可重入锁.公平锁.可中断锁等等,就像一个人可以是男人.医生.健身爱好者.游戏玩家,这并不矛盾.OK,国际惯例,上干货. 〇.synchronized与Lock

Linq to Sql并发冲突及处理策略

0. 并发冲突的示例 单用户的系统现在应该比较罕见了,一般系统都会有很多用户在同时进行操作:在多用户系统中,涉及到的一个普遍问题:当多个用户“同时”更新(修改或者删除)同一条记录时,该如何更新呢?    下图展示了开放式并发冲突的一个示例: 假设数据库中有一条记录Record{Field1=5, Field2=6, Field3=7}(以下简写为{5, 6, 7}),A.B两个用户按照如下顺序操作这一条记录:(1). A读取该记录,取得的值为{5, 6, 7},读取完毕后,不对该记录加排他锁:(

Linq to Sql : 并发冲突及处理策略

原文:Linq to Sql : 并发冲突及处理策略 0. 并发冲突的示例 单用户的系统现在应该比较罕见了,一般系统都会有很多用户在同时进行操作:在多用户系统中,涉及到的一个普遍问题:当多个用户"同时"更新(修改或者删除)同一条记录时,该如何更新呢?    下图展示了开放式并发冲突的一个示例: 假设数据库中有一条记录Record{Field1=5, Field2=6, Field3=7}(以下简写为{5, 6, 7}),A.B两个用户按照如下顺序操作这一条记录:(1). A读取该记录,

zbb20180929 thread 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、对象锁和类锁

1.自旋锁自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行.若线程依然不能获得锁,才会被挂起.使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强.因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,

互联网线上项目开发最大坑点-并发冲突处理

大家可能都有这样的经验,自个儿在家里很多功能很容易实现,一下就做完了,但是在做线上产品的时候,就变得无比复杂,需要花费很多的时间. 自己写的程序在家跑,所有的业务都很正常,一旦发布到线上,就会出现很多bug,而且很多bug在测试的时候很难重现,这是在互联网开发的时候经常遇到的现象. 这些难以重现的bug,大部分是由于并发产生的,为了能让大家充分的了解并发的问题,并且建立并发环境下的程序设计思维,我们为大家准备了几个小案例 大家来看一下,这个网站典型的场景,遇到内容比较多的情况下,我们会使用分页

ElasticSearch(简称ES)

Windows下安装ElasticSearch ElasticSearch(简称ES)是一个基于Lucene的分布式全文搜索服务器,和SQL Server的全文索引(Fulltext Index)有点类似,但是ES天生具有分布式和实时的属性,本随笔演示在Windows环境中安装ElasticSearch,以及用于管理ElasticSearch的Head插件. ElasticSearch官网:http://www.elasticsearch.org 一,安装Java SE环境 1,从Java Se

27 Apr 18 GIL 多进程多线程使用场景 线程互斥锁与GIL对比 基于多线程实现并发的套接字通信 进程池与线程池 同步、异步、阻塞、非阻塞

27 Apr 18 一.全局解释器锁 (GIL) 运行test.py的流程: a.将python解释器的代码从硬盘读入内存 b.将test.py的代码从硬盘读入内存  (一个进程内装有两份代码) c.将test.py中的代码像字符串一样读入python解释器中解析执行 1 .GIL:全局解释器锁 (CPython解释器的特性) In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple na