elasticsearch学习笔记(二)

来源:互联网 发布:linux测试网速的命令 编辑:程序博客网 时间:2024/05/17 23:23

悲观并发控制(PCC)

这一点在关系数据库中被广泛使用。假设这种情况很容易发生,我们就可以阻止对这一资源的访问。典型的例子就是当我们在读取一个数据前先锁定这一行,然后确保只有读取到数据的这个线程可以修改这一行数据。
乐观并发控制(OCC)

Elasticsearch所使用的。假设这种情况并不会经常发生,也不会去阻止某一数据的访问。然而,如果基础数据在我们读取和写入的间隔中发生了变化,更新就会失败。这时候就由程序来决定如何处理这个冲突。例如,它可以重新读取新数据来进行更新,又或者它可以将这一情况直接反馈给用户。
乐观并发控制

Elasticsearch是分布式的。当文档被创建、更新或者删除时,新版本的文档就会被复制到集群中的其他节点上。Elasticsearch即是同步的又是异步的,也就是说复制的请求被平行发送出去,然后可能会混乱地到达目的地。这就需要一种方法能够保证新的数据不会被旧数据所覆盖。
我们在上文提到每当有索引、put和删除的操作时,无论文档有没有变化,它的_version都会增加。Elasticsearch使用_version来确保所有的改变操作都被正确排序。如果一个旧的版本出现在新版本之后,它就会被忽略掉。
我们可以利用_version的优点来确保我们程序修改的数据冲突不会造成数据丢失。我们可以按照我们的想法来指定_version的数字。如果数字错误,请求就是失败。
我们来创建一个新的博文:
PUT /website/blog/1/_create
{
“title”: “My first blog entry”,
“text”: “Just trying this out…”
}
反馈告诉我们这是一个新建的文档,它的_version是1。假设我们要编辑它,把这个数据加载到网页表单中,修改完毕然后保存新版本。
首先我们先要得到文档:
GET /website/blog/1
返回结果显示_version为1:
{
“_index” : “website”,
“_type” : “blog”,
“_id” : “1”,
“_version” : 1,
“found” : true,
“_source” : {
“title”: “My first blog entry”,
“text”: “Just trying this out…”
}
}
现在,我们试着重新索引文档以保存变化,我们这样指定了version的数字:
PUT /website/blog/1?version=1 <1>
{
“title”: “My first blog entry”,
“text”: “Starting to get the hang of this…”
}
我们只希望当索引中文档的_version是1时,更新才生效。
请求成功相应,返回内容告诉我们_version已经变成了2:
{
“_index”: “website”,
“_type”: “blog”,
“_id”: “1”,
“_version”: 2
“created”: false
}
然而,当我们再执行同样的索引请求,并依旧指定version=1时,Elasticsearch就会返回一个409 Conflict的响应码,返回内容如下:
{
“error” : “VersionConflictEngineException[[website][2] [blog][1]:
version conflict, current [2], provided [1]]”,
“status” : 409
}
这里面指出了文档当前的_version数字是2,而我们要求的数字是1。
我们需要做什么取决于我们程序的需求。比如我们可以告知用户已经有其它人修改了这个文档,你应该再保存之前看一下变化。而对于上文提到的库存量问题,我们可能需要重新读取一下最新的文档,然后显示新的数据。
所有的有关于更新或者删除文档的API都支持version这个参数,有了它你就通过修改你的程序来使用乐观并发控制。
使用外部系统的版本

还有一种常见的情况就是我们还是使用其他的数据库来存储数据,而Elasticsearch只是帮我们检索数据。这也就意味着主数据库只要发生的变更,就需要将其拷贝到Elasticsearch中。如果多个进程同时发生,就会产生上文提到的那些并发问题。
如果你的数据库已经存在了版本号码,或者也可以代表版本的时间戳。这是你就可以在Elasticsearch的查询字符串后面添加version_type=external来使用这些号码。版本号码必须要是大于零小于9.2e+18(Java中long的最大正值)的整数。
Elasticsearch在处理外部版本号时会与对内部版本号的处理有些不同。它不再是检查_version是否与请求中指定的数值相同,而是检查当前的_version是否比指定的数值小。如果请求成功,那么外部的版本号就会被存储到文档中的_version中。
外部版本号不仅可以在索引和删除请求时使用,还可以在创建时使用。
例如,创建一篇使用外部版本号为5的博文,我们可以这样操作:
PUT /website/blog/2?version=5&version_type=external
{
“title”: “My first external blog entry”,
“text”: “Starting to get the hang of this…”
}
在返回结果中,我们可以发现_version是5:
{
“_index”: “website”,
“_type”: “blog”,
“_id”: “2”,
“_version”: 5,
“created”: true
}
现在我们更新这个文档,并指定version为10:
PUT /website/blog/2?version=10&version_type=external
{
“title”: “My first external blog entry”,
“text”: “This is a piece of cake…”
}
请求被成功执行并且version也变成了10:
{
“_index”: “website”,
“_type”: “blog”,
“_id”: “2”,
“_version”: 10,
“created”: false
}
如果你再次执行这个命令,你会得到之前的错误提示信息,因为你所指定的版本号并没有大于当前Elasticsearch中的版本号。

获取多个文档

尽管Elasticsearch已经很快了,但是它依旧可以更快。你可以将多个请求合并到一个请求中以节省网络开销。如果你需要从Elasticsearch中获取多个文档,你可以使用multi-get 或者 mget API来取代一篇又一篇文档的获取。
mgetAPI需要一个docs数组,每一个元素包含你想要的文档的_index, _type以及_id。你也可以指定_source参数来设定你所需要的字段:
GET /_mget
{
“docs” : [
{
“_index” : “website”,
“_type” : “blog”,
“_id” : 2
},
{
“_index” : “website”,
“_type” : “pageviews”,
“_id” : 1,
“_source”: “views”
}
]
}

如果你所需要的文档都在同一个_index或者同一个_type中,你就可以在URL中指定一个默认的/_index或是/_index/_type。
你也可以在单独的请求中重写这个参数:
GET /website/blog/_mget
{
“docs” : [
{ “_id” : 2 },
{ “_type” : “pageviews”, “_id” : 1 }
]
}
事实上,如果所有的文档拥有相同的_index 以及 _type,直接在请求中添加ids的数组即可:
GET /website/blog/_mget
{
“ids” : [ “2”, “1” ]
}

文档没有被找到。
当第二篇文档没有被找到的时候也不会影响到其它文档的获取结果。每一个文档都会被独立展示。
注意:上方请求的HTTP状态码依旧是200,尽管有个文档没有找到。事实上,即使所有的文档都没有被找到,响应码也依旧是200。这是因为mget这个请求本身已经成功完成。要确定独立的文档是否被成功找到,你需要检查found标识。

批量更高效

mget能同时允许帮助我们获取多个文档相同,bulk API可以帮助我们同时完成执行多个请求,比如:create,index, update以及delete。当你在处理类似于log等海量数据的时候,你就可以一下处理成百上千的请求,这个操作将会极大提高效率。
bulk的请求主体的格式稍微有些不同:
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
这种格式就类似于一个用”\n”字符来连接的单行json一样。下面是两点注意事项:
每一行都结尾处都必须有换行字符”\n”,最后一行也要有。这些标记可以有效地分隔每行。
这些行里不能包含非转义字符,以免干扰数据的分析 — — 这也意味着JSON不能是pretty-printed样式。
action/metadata 行指定了将要在哪个文档中执行什么操作。
其中action必须是index, create, update或者delete。metadata 需要指明需要被操作文档的_index, _type以及_id,例如删除命令就可以这样填写:
{ “delete”: { “_index”: “website”, “_type”: “blog”, “_id”: “123” }}
你进行index以及create操作时,request body 行必须要包含文档的_source数据——也就是文档的所有内容。
同样,在执行update API: doc, upsert,script的时候,也需要包含相关数据。而在删除的时候就不需要request body行。
{ “create”: { “_index”: “website”, “_type”: “blog”, “_id”: “123” }}
{ “title”: “My first blog post” }
如果没有指定_id,那么系统就会自动生成一个ID:
{ “index”: { “_index”: “website”, “_type”: “blog” }}
{ “title”: “My second blog post” }
完成以上所有请求的bulk如下:
POST /_bulk
{ “delete”: { “_index”: “website”, “_type”: “blog”, “_id”: “123” }} <1>
{ “create”: { “_index”: “website”, “_type”: “blog”, “_id”: “123” }}
{ “title”: “My first blog post” }
{ “index”: { “_index”: “website”, “_type”: “blog” }}
{ “title”: “My second blog post” }
{ “update”: { “_index”: “website”, “_type”: “blog”, “_id”: “123”, “_retry_on_conflict” : 3} }
{ “doc” : {“title” : “My updated blog post”} }
注意delete操作是如何处理request body的,你可以在它之后直接执行新的操作。
请记住最后有换行符
Elasticsearch会返回含有items的列表、它的顺序和我们请求的顺序是相同的:

所有的请求都被成功执行。
每一个子请求都会被单独执行,所以一旦有一个子请求失败了,并不会影响到其他请求的成功执行。如果一旦出现失败的请求,error就会变为true,详细的错误信息也会出现在返回内容的下方

最大有多大?

整个数据将会被处理它的节点载入内存中,所以如果请求量很大的话,留给其他请求的内存空间将会很少。bulk应该有一个最佳的限度。超过这个限制后,性能不但不会提升反而可能会造成宕机。
最佳的容量并不是一个确定的数值,它取决于你的硬件,你的文档大小以及复杂性,你的索引以及搜索的负载。幸运的是,这个平衡点 很容易确定:
试着去批量索引越来越多的文档。当性能开始下降的时候,就说明你的数据量太大了。一般比较好初始数量级是1000到5000个文档,或者你的文档很大,你就可以试着减小队列。 有的时候看看批量请求的物理大小是很有帮助的。1000个1KB的文档和1000个1MB的文档的差距将会是天差地别的。比较好的初始批量容量是5-15MB。

0 0
原创粉丝点击