ES-CRUD提高

文档 ID 生成

手动生成

  • 当我们从外界导入数据到 ES 时,我们并不希望改变原本数据的主键,就需要手动设置 ID
  • 手动设置的方式在上面已经学习
  • 方式如下:
PUT /index/type/id

自动生成

  • 语法如下:
POST /index/type

  • 自动生成的 ID,长度是 20 个字符的 GUID,分布式全局唯一

查询指定字段

  • 在我们操作 MySQL 时,想必大部分的公司都是禁止使用 select * 的,而要求用到什么列就查询什么列
  • 而我们上面使用 ES 的方式,就相当于在写 select * ,这是不推荐的,我们在实际使用的时候,可以只查询指定的字段
  • 语法如下:
GET /index/type/id?_source_includes=字段名,多个字段逗号隔开
article/_doc/3?_source_includes=title,red

强制创建

在上面的学习中,我们发现,替换文档和创建文档的 API 是一样的,这就意味着,我们在创建文档的过程中,可能这个文档其实已经存在了,但是我们不知道,通过创建的 API 直接把该文档进行了替换,这不是我们想要的结果,因此需要使用强制创建

  • 强制创建限定这个 API 只能是创建文档,如果指定 ID 的文档已经存在了,就抛出异常
  • 语法如下:
PUT /index/type/id/_create
article/_doc/4/_create

Lazy Delete

  • ES 在替换文档、修改文档的时候,本质上并不是直接替换,而是先将旧文档标记为 deleted,然后再创建新的文档
  • 此时旧的文档实际上还是存在于索引库里的,并不会立即删除,集群会找个合适的时机,一并删除所有标记deleted 的文档
  • 同样,删除的 API 也只是把文档标记了 deleted,并不是立即删除
  • 如果删除一条数据立马删除的话,所有分片和副本都要立马删除,对 es 集群压力太大

手动设置版本号

  • 我们的数据库中可能已经维护了版本号,现在需要同步这些数据,同步的时候需要手动设置版本号
  • 可以通过使用 external version 控制,设置的时候版本号必须大于当前文档的版本号
  • 语法如下:
PUT /index/type/id?version=要修改的版本号&version_type=external
article/_doc/4/?version=2&version_type=external

  • 两个客户端同时修改,只会有一个会修改成功

批量操作

批量查询

  • 当我们要根据 id 查询的时候,如果一条一条查,性能损耗和网络开销大,可以使用批量查询
  • 语法如下:
GET /_mget
  • _mget 是全查询,操作起来并不是那么方便,并且容易报出 request body or source parameter is required 异常,因此使用率较少

批量增删改

  • _bulk 操作将文档的增删改查一系列操作,通过一次请求全部做完,减少网络传输次数
POST /_bulk
  • 注意,_bulk 操作的形式是多个 JSON,每个 JSON 写完必须换行,而在 JSON 内则不可以换行
  • 多个 JSON 之间操作互不影响,即使前面的报错了,其他行也可以正常执行

{"delete": {"_index": "article", "_id": 6}}
{"create": {"_index": "article", "_id": 7}}
{"title": "我是批量操作中创建的数据,ID是7 "}
{"update": {"_index": "article", "_id": 5}}
{"doc": {"content": "我在批量操作中进行了修改,但是PHP依然是最好的语言"}}
  • delete:删除一个文档,只要 1 个 JSON 串就可以了
  • create:相当于强制创建,就是:PUT /index/type/id/_create
  • index:普通的 put 操作,可以是创建文档,也可以是全量替换文档
  • update:执行的是局部更新 partial update 操作
  • 使用 JSON 格式发送数据

ES的并发问题

  • 通常情况下,ES 在多线程操作的时候,会产生并发问题
  • 举个例子,我跟你在淘宝在同一时间下单买了同一本书,两个线程同时去 es 扣这本书的库存,库存有 100 本书,正常情况扣完库存后应该变成 98 本,但如果两个线程同时扣减就会有并发冲突,就会变成这样如下例子
  • 可以看到库存的值变成了 97 本,与我们期望中的 94 本不符
  • 这一现象也叫 超卖,对数据库的库存扣减的时候也会出现这种并发冲突的情况
  • 为了控制并发问题,我们通常采用 机制
  • 分为 悲观锁乐观锁 两种机制

悲观锁

悲观锁的思路是在线程1读到库存是 100 的时候就把 es 的这条库存给锁上,阻止线程2去读库存,线程1扣完库存并把新的库存量 97 写入 es 后,才允许线程2去读取库存,这时线程2读取出来的库存是 99 而不是100,扣减完变成 98 再写入es

这种思路实际是把并发的线程转成串行执行,非常方便,直接加锁就行,对程序来说不需要做额外的操作,但是并发能力低,同一时间只能一条线程去扣减库存

乐观锁

乐观锁的思路是给 es 的库存附加一个版本号,并发冲突的情况下,线程1读取库存库存 100 (版本号是1)线程2 读取库存 100(版本号1)线程1扣减库存后变成 99,线程2扣减后变成 99,线程1写入库存 99 到 es 前对比库存版本号(线程1读取的库存版本号为1,当前 es 的库存版本号为1)发现一致,于是写入库存 99 到 es 并更新库存版本号为 2,线程2写入库存 99 到 es 前对比库存版本号(线程2读取的库存版本号为1,当前 es 的库存版本号为2)发现不一致,线程2写入失败,线程2重新读取库存 99(版本号2)线程2扣减后变成 98,线程2写入库存98 到 es 前对比库存版本号(都是2)发现一致,于是写入库存 98 到 es 并更新库存版本号为3

这种方式只是在把库存写入 es 那一刻检查一下版本号判断是否可以写入就行了,不需要把库存锁上,因此并发能力很高,但这种方式编码的时候比较麻烦,每次更新库存都要去比对版本号和更新版本号,版本号对不上的时候还需要重新读取库存并扣库存

ES的乐观锁

ES 对文档的增删改都是基于版本号,也就是 _version 字段,新版本基于 _seq_no 字段

  • 多次新增同一个文档,发现版本号递增
  • 删除文档再新增,发现版本号依然递增,验证了延迟删除策略

乐观锁演示

  • 查询数据当前的版本号
GET /article/_doc/7
article/_doc/4

  • 客户端1更新,带版本号
PUT /article/_doc/4?if_seq_no=7&if_primary_term=1

  • 客户端2更新,带版本号,报错
PUT /article/_doc/4?if_seq_no=7&if_primary_term=1

  • 客户端2重新查询版本号
article/_doc/4

  • 客户端2更新,带版本号
article/_doc/4?if_seq_no=10&if_primary_term=1

更新时的重试机制

  • 更新时的重试机制(retry_on_conflict)
  • 有时候我们在更新时,并不希望失败了直接报错,而是想让程序有一定的重试机制,重试一定次数后如果还是失败,就报错,这时候可以使用 retry_on_conflict 指定重试次数
  • 语法如下:
POST /index/type/4/_update?retry_on_conflict=次数
article/_doc/4/_update?retry_on_conflict=2
  • 还可以和 _version 一起使用
  • 使用之前你可以先查看一下版本号
POST /index/type/4/_update?retry_on_conflict=3&version=4&version_type=external
posted @   BNTang  阅读(172)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示