点击上方蓝字每天学习数据库
| 本文作者:游成松,腾讯云数据库后台开发,负责腾讯云数据库CTSDB产品的设计、研发、运维等工作。曾负责腾讯云数据库SQLServer、PostgreSQL、TDSQL、Tbase、CynosDB产品的研发工作。
一个理想的查询过程中,新增加或者修改的数据应该能立即被查询到。腾讯时序数据库CTSDB给人的第一印象好像就是如此工作的,而事实上并非如此。那它实际情况是怎么样的呢?
在进行说明之前先大概介绍一下CTSDB处理请求的流程。
在CTSDB和磁盘之间有一层FileSystem Cache的系统缓存,以使得能够更快地处理搜索请求。插入请求到来时document会先被放入到indexing buffer,然后被重写为一个segment直接写入到filesystem cache,这个操作是非常轻量级的,相对耗时较少,之后经过一定的间隔或外部触发后才会被flush到磁盘上,这个操作非常耗时。但只要sengment文件被写入cache后就可以被打开和查询,在短时间内就可以搜到,而不用执行一个flush也就是fsync操作。其请求处理流程如下图:
下面通过一个案例来验证进行观察分析。
新增加一条数据到新创建的索引中。
curl -u root:xxxxxx -XPOST 127.0.0.1:9200/test/test/1 -d '{ "title":"test"}'
为了进行验证,我们修改这条数据,并尝试立即查询它,连续执行下面两行命令。
curl -u root:xxxxxx -XPOST 127.0.0.1:9200/test/test/1 -d '{ "title":"test2"}'curl -u root:xxxxxx -XGET 127.0.0.1:9200/test/test/_search -d '{ "docvalue_fields":[ "title" ]}'
前面两行命令返回结果如下:
{ "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_version":2, "result":"updated", "_shards":{ "total":2, "successful":2, "failed":0 }, "created":false}{ "took":0, "timed_out":false, "_shards":{ "total":3, "successful":3, "skipped":0, "failed":0 }, "hits":{ "total":1, "max_score":1, "hits":[ { "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_score":1, "_source":{ "title":"test", "timestamp":1556299203460 }, "fields":{ "title":[ "test" ] } } ] }}
我们可以看到第1条命令修改数据成功了,返回结果version字段为2,一切正常。但是第2条命令返回的title字段应该返回test2的,但是还是返回修改之前的数据test。
由于CTSDB底层是基于ElasticSearch的,而ElasticSearch的索引是基于Apache Lucene索引的,那我们先来看看Lucene的内部机制,Lucene是如何让新索引的文档在搜索时可用?
索引新文档时会被写入索引段。不时会有新增的索引段被添加到可被搜索的索引段集合中,Lucene通过创建后续的(基于索引只写一次的特性)segments_N文件来实现此功能。这个过程被称为提交,Lucene会以安全、原子的操作来提交保证数据一致性。
我们回来之前的案例中,尽管第1个命令修改了文档,但它还没有执行提交操作。然而执行了提交操作也不能保证能被搜索到,因为Lucene使用一个叫Searcher的抽象类来执行索引的读取。该类需要被刷新,如果索引更新提交了,但Searcher实例没有重新打开(刷新),那么Searcher察觉不到有新索引段的加入。对于Searcher的刷新间隔时间可以通过refresh_interval来进行设置。
同时在ElasticSearch中提供了refresh端点进行强制Searcher刷新。我们可以在上面两行命令中间加入强制刷新API再来验证。
curl -u root:xxxxxx -XPOST 127.0.0.1:9200/test/test/1 -d '{ "title":"test3"}'curl -u root:xxxxxx -XGET 127.0.0.1:9200/test@1555344000000_30/_refreshcurl -u root:xxxxxx -XGET 127.0.0.1:9200/test/test/_search -d '{ "docvalue_fields":[ "title" ]}'
上面三行命令返回结果如下:
{ "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_version":3, "result":"updated", "_shards":{ "total":2, "successful":2, "failed":0 }, "created":false}{ "_shards":{ "total":6, "successful":6, "failed":0 }}{ "took":0, "timed_out":false, "_shards":{ "total":3, "successful":3, "skipped":0, "failed":0 }, "hits":{ "total":1, "max_score":1, "hits":[ { "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_score":1, "_source":{ "title":"test3", "timestamp":"1556301337152" }, "fields":{ "title":[ "test3" ] } } ] }}
可以看到第3条命令返回的title字段为test3,返回了修改之后的数据test3。
在ElasticSearch的索引实现中Apache Lucene能保证索引的一致性,但这不能保证当向索引中写数据失败时不损失数据(例如,磁盘空间不足,设备异常)。另外一个问题是频繁提交(触发一个索引段的创建操作,同时也可能触发索引段的合并)会导致性能问题。ElasticSearch使用事务日志来解决这些问题,事务日志保存所有未提交的事务。当有错误发生时,事务日志会被检查,必要时再次执行某些操作,以确保没有丢失任何更改。事务日志中的信息与存储介质之间的同步被称为事务日志刷新。同样事务日志的刷新间隔可以通过index.translog.flush相关参数进行配置,也可以通过_flush端点进行强制事务日志刷新。
事务日志刷新flush用于保证数据写入磁盘并清空事务日志。Searcher刷新refresh用于搜索到最新的文档。
事务日志给ElasticSearch带来了一个特性:实时读取。实时读取从索引中读取数据时,会先检查事物日志中是否有可用的新版本(未提交版本),如果有就会返回事务日志中的最新版本的文档。
为了演示实时读取,连续执行下面两条语句,第2条语句查询时指定索引文档id来查询,会从事务日志中读取最新的数据。
curl -u root:xxxxxx -XPOST 127.0.0.1:9200/test/test/1 -d '{ "title":"test4"}'curl -u root:xxxxxx -XGET 127.0.0.1:9200/test@1555344000000_30/test/1
上面2行数据返回结果如下:
{ "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_version":4, "result":"updated", "_shards":{ "total":2, "successful":2, "failed":0 }, "created":false}{ "_index":"test@1555344000000_30", "_type":"test", "_id":"1", "_version":4, "found":true, "_source":{ "title":"test4", "timestamp":"1556305107154" }}
可以看到第2条命令返回的title字段为test4,返回了修改之后的数据test4。
这时我们并没有使用refresh刷新技巧就查询到了最新的文档。
修改数据、search数据:不一定会查询到最新的数据。
修改数据、refresh强制刷新、search数据:会查询到最新的数据。
修改数据、指定文档id查询数据:会查询到最新的数据。
往期推荐
年中薅羊毛,可省18040元
云数据库MySQL年中疯狂折扣中,买MySQL高可用版送6个月数据迁移服务,1核1G内存100G SSD盘低至96.8元/月。免费数据管理DMC,双节点架构,自动容灾,最高可省18040元!点击左下角“阅读原文”立即参与~
↓↓点“阅读原文”享年中福利
好文和朋友一起看!
var first_sceen__time = (+new Date());if ("" == 1 && document.getElementById('js_content')) { document.getElementById('js_content').addEventListener("selectstart",function(e){ e.preventDefault(); }); } (function(){ if (navigator.userAgent.indexOf("WindowsWechat") != -1){ var link = document.createElement('link'); var head = document.getElementsByTagName('head')[0]; link.rel = 'stylesheet'; link.type = 'text/css'; link.href = "//res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg_new/winwx45ba31.css"; head.appendChild(link); } })();
游成松
长按二维码向我转账
受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。
阅读
分享 在看
已同步到看一看
确定
已同步到看一看写下你的想法
最多200字,当前共字 发送
已发送
确定
写下你的想法...
取消
确定
最多200字,当前共字
发送中
微信扫一扫 关注该公众号
微信扫一扫 使用小程序
即将打开""小程序