document 的部分更新
对于solr和elasticsearch, 一般来说我们可能会先获取document, 再更新某个field, 然后reindex这个document。因为document是不可变的, 不能够修改, 只能被替换。
但其实很久之前我觉得, 这个操作对于客户端来说, 如果不考虑version的话, 为何不在服务器完成, 提供一个部分更新的api, 让用户根据场景合理使用api, 比之前的做法可以节省一个请求。
所以说, elasticsearch还是很人性化的, 它提供了这个api, 不过在其内部来说, api实现还是遵循了retrieve-change-reindex
的步骤, 操作在shard中完成。
更新的安全问题
那么如果有多个客户端同时更新, 那么并发安全的问题es如何处理呢?
前面提到了, es内部是retrieve-change-reindex
的步骤, 在retrieve
阶段, es会获取当前的_version
, 并在后面的reindex
阶段使用。
如果其他进程修改了document, _version
将会和update request中的不同, 从而更新将会失败。
这个类似乐观锁的策略, 如果需要重试的话, 可以使用retry_on_conflict
设置重试次数,比如:
.../_update?retry_on_conflict=5
不过, 这个重试使用的场景在计数器上用的较多, 因为这个更新的顺序是不那么重要的。
REST api
言归正传, 接下来,介绍以下api的使用吧。
假设有个blog
的document, 其中有个field叫做tags
, 如果要部分更新这个tags
:
最简单的方式是使用doc
参数, 和已有document合并, 已有的field会被覆盖, 新的field将会新增到document, REST api 如下:
POST /website/blog/1/_update |
如果成功, 会收到这样的response:
{ |
再获取一次这个文档, 检查一下是否真的只更新了tags
这个field:
{ |
java api
如果使用 java api 的话:
UpdateRequest updateRequest = new UpdateRequest("website", "blog", "1") |
spring data elasticsearch api
如果使用spring-data-elasticsearch的ElasticsearchTemplate
:
|
使用这个api时可能会有这样的疑惑, UpdateQuery中有指定indexName和objectType, 但是@Document
的Class中也有声明, 那么使用哪个呢?
我们可以看看elasticsearchTemplate.update(updateQuery)
的源码:
@Override |
由上可知, query.getIndexName()
和query.getIndexName()
会被优先使用, 如果未指定, 会从document的Class注解中获取。
如果为了提高性能, 能写上也好, 不过最好统一管理这两个名称, 方便以后修改变更.
参考
https://www.elastic.co/guide/en/elasticsearch/guide/current/partial-updates.html
https://www.elastic.co/guide/en/elasticsearch/client/java-api/1.7/java-update-api-merge-docs.html