并发控制的必要性

假设两个请求同时更新某个文档,如果缺乏有效的并发控制,会导致更改的数据丢失。

悲观并发控制

假定有同事写共享变量的请求,会对资源加锁,例如数据库行锁。

乐观并发控制

  • 假定冲突是不会发生的,不会阻止正在尝试的操作
  • 如果数据在读写中被更改,读写将失败
  • 应用程序决定如何解决重复,例如重试机制,使用新的数据,或者将错误的数据报告给用户
  • ES采用的是乐观并发控制

ES的乐观并发控制

ES中的文档是不可变更的,如果更新一个文档,会将旧文档标记删除,同时添加一个全新的文档,并且文档的version会+1

内部版本控制

使用if_seq_no + if_primary_term做并发控制。

数据准备

写入文档,会返回_seq_noprimary_term

  1. PUT /user/_doc/222
  2. {
  3. "name":"neelon"
  4. }
  5. //响应
  6. {
  7. "_index": "user",
  8. "_type": "_doc",
  9. "_id": "222",
  10. "_version": 1,
  11. "result": "created",
  12. "_shards": {
  13. "total": 2,
  14. "successful": 1,
  15. "failed": 0
  16. },
  17. "_seq_no": 29,
  18. "_primary_term": 1
  19. }

更新操作

  • 根据文档当前的_seq_no和_primary_term去更新文档,更新成功文档的_seq_no和_primary_term会增加;
  • 如果此时有别的请求用增加前的_seq_no和_primary_term尝试更新文档则会失败
  • 通过这种机制,保证并发写的正确性

也就是说,每次更新前需要通过查询,预先获取到文档的_seq_no和_primary_term的值。

  1. PUT /user/_doc/1?if_seq_no=29&if_primary_term=1

外部版本控制

比如应用的数据存储再MySQL中,ES值是作为一个数据同步用以支持搜索。

使用version + version type控制并发写

  • MySQL中的记录也有version字段
  • 写入成功,并且文档的_version变成30001
  • 如果再次更新,version还是30000,更新失败。带有version更大,则更新成功。