并发冲突是一个比较常见的问题,无论是elasticSearch还是类似于mysql的数据库都存在并发冲突的问题。
并发冲突出现的场景:例如当前售卖苹果,由于是圣诞节,苹果按个来买。当前有100个苹果。当用户1过来购买苹果1个时,用户2同时过来购买1个苹果,用户1对应的线程从es中取出数据为100,用户2对应的线程从es中取出的数据也为100。当用户1的线程执行完成之后苹果的个数减1,变成99.此时用户2的线程也执行完成,之后苹果的个数减1,仍然为99.但是实际的情况是已经售出2个苹果,剩余的苹果数量应该为98。当并发越多的时候,出现的错误就会更明显。这就是并发冲突的问题。
解决并发冲突的2中方式
1.悲观锁
悲观锁,总是认为我在改数据的时候也有其他人在改数据,于是直接给数据加上锁。其优点就是方便。缺点也是显而易见的就是同一时间内只有一条线程操作数据。
2.乐观锁
每一次从es中拿数据的时候顺带拿到一个version,进行数据修改的时候进行version的对比,如果一致,进行修改操作,如果不一致,从新获取数据重新重复操作。
其优点就是不需要给数据加锁,并发能力高。缺点就是每一次的修改操作都需要进行version的对比,然后才能进行数据的修改,但可能出现多次对比不正确,对此进行数据的重新加载,操作,对比version,重复性的多次操作
使用es模拟乐观锁_version测试
准备数据
创建一个document,可以看到此时docuemnt的version=1,当我们对version进行增删改操作之后version会进行自动加1的操作
PUT /product/fruit/6
{
"price":200,
"productName":"Grape"
}
返回结果
{
"_index": "product",
"_type": "fruit",
"_id": "6",
"_version": 1, #版本1
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
模拟两个客户端:对数据进行全量替换
external version进行乐观锁并发控制
es提供了一个feature.可以基于自己维护的一个版本号进行并发控制。
例如:使用es内部的versino 进行并发控制 ?version=1
例如:使用自定义version进行并发控制 ?version=1&version_type=external
区别:
内部_version并发控制,需要你提供的 _version和es中的 _version一致,才可以进行修改,否则报错。
自定义version并发控制,只有当提供的 _version比es中的 _version大的时候才能进行修改,否则报错。
由于使用自定影version进行并发控制,但是为了保证es内部的version控制,需要指定一个比当前version大的version
模拟数据:
PUT /product/fruit/7
{
"price":200,
"productName":"Grape"
}
客户端1:进行版本更新,此时版本号为2
PUT /product/fruit/7?version=2&version_type=external
{
"price":100,
"productName":"Grape"
}
客户端2:进行版本更新,此时版本号3
PUT /product/fruit/7?version=3&version_type=external
{
"price":100,
"productName":"Grape"
}