非规范化和并发 | Elasticsearch: 权威指南 | Elastic
2024-12-21
当然,数据非规范化也有弊端。
第一个缺点是索引会更大因为每个博客文章文档的 _source
将会更大,并且这里有很多的索引字段。这通常不是一个大问题。数据写到磁盘将会被高度压缩,而且磁盘已经很廉价了。Elasticsearch 可以愉快地应付这些额外的数据。
更重要的问题是,如果用户改变了他的名字,他所有的博客文章也需要更新了。幸运的是,用户不经常更改名称。即使他们做了,
用户也不可能写超过几千篇博客文章,所以更新博客文章通过 scroll
和 bulk
APIs 大概耗费不到一秒。
然而,让我们考虑一个更复杂的场景,其中的变化很常见,影响深远,而且非常重要,并发。
在这个例子中,我们将在 Elasticsearch 模拟一个文件系统的目录树,非常类似 Linux 文件系统:根目录是 /
,每个目录可以包含文件和子目录。
我们希望能够搜索到一个特定目录下的文件,等效于:
grep "some text" /clinton/projects/elasticsearch/*
这就要求我们索引文件所在目录的路径:
PUT /fs/file/1 { "name": "README.txt", "path": "/clinton/projects/elasticsearch", "contents": "Starting a new Elasticsearch project is easy..." }
事实上,我们也应当索引 directory
文档,如此我们可以在目录内列出所有的文件和子目录,但为了简洁,我们将忽略这个需求。
我们也希望能够搜索到一个特定目录下的目录树包含的的任何文件,相当于此:
grep -r "some text" /clinton
为了支持这一点,我们需要对路径层次结构进行索引:
/clinton
/clinton/projects
/clinton/projects/elasticsearch
这种层次结构能够通过 path
字段使用 path_hierarchy
tokenizer 自动生成:
PUT /fs { "settings": { "analysis": { "analyzer": { "paths": { "tokenizer": "path_hierarchy" } } } } }
自定义的 |
file
类型的映射看起来如下所示:
PUT /fs/_mapping/file { "properties": { "name": { "type": "string", "index": "not_analyzed" }, "path": { "type": "string", "index": "not_analyzed", "fields": { "tree": { "type": "string", "analyzer": "paths" } } } } }
一旦索引建立并且文件已被编入索引,我们可以执行一个搜索,在 /clinton/projects/elasticsearch
目录中包含 elasticsearch
的文件,如下所示:
GET /fs/file/_search { "query": { "filtered": { "query": { "match": { "contents": "elasticsearch" } }, "filter": { "term": { "path": "/clinton/projects/elasticsearch" } } } } }
所有在 /clinton
下面的任何子目录存放的文件将在 path.tree
字段中包含 /clinton
词项。所以我们能够搜索 /clinton
的任何子目录中的所有文件,如下所示:
GET /fs/file/_search { "query": { "filtered": { "query": { "match": { "contents": "elasticsearch" } }, "filter": { "term": { "path.tree": "/clinton" } } } } }
到目前为止一切顺利。
重命名一个文件很容易--所需要的只是一个简单的 update
或 index
请求。
你甚至可以使用 optimistic concurrency control 确保你的变化不会与其他用户的变化发生冲突:
PUT /fs/file/1?version=2 { "name": "README.asciidoc", "path": "/clinton/projects/elasticsearch", "contents": "Starting a new Elasticsearch project is easy..." }
我们甚至可以重命名一个目录,但这意味着更新所有存在于该目录下路径层次结构中的所有文件。
这可能快速或缓慢,取决于有多少文件需要更新。我们所需要做的就是使用 scroll
来检索所有的文件,
以及 bulk
API 来更新它们。这个过程不是原子的,但是所有的文件将会迅速转移到他们的新存放位置。
官方地址:https://www.elastic.co/guide/cn/elasticsearch/guide/current/denormalization-concurrency.html