目录
基本概念
ESDB用途 - 交易数据查询
ES基本概念
ESDB创新点
ESDB架构
3.1 应用层
3.2 控制层
3.3 执行层
ESDB负载均衡
哈希、双重哈希
动态二次哈希
ESDB数据复制优化
总结
交易数据库中,交易日志包括结构化数据(如交易ID、卖家ID、创建时间和交易状态)和全文数据(如拍卖标题、卖家和买家的昵称)以及各种商品的信息,允许用户为商品添加自定义属性(如尺寸、衣服的材质、食物的重量),这导致了数据模式的多样化(即不同的交易日志有不同的属性)。
例如:书店卖家通过拍卖标题中的关键词搜索图书交易的交易状态
一般使用 JSON 格式存储数据,属于文档存储。
分布式多租户数据库,来自多个租户的数据被分配到共享同一组计算和存储资源,但是不同租户使用量/使用频率不同
索引太大-使用多个分片处理
索引更改频繁-降低文件粒度,使用多个段存储
ES和关系数据库对比:
文档(Document)----------------行记录
字段(Field)-------------------Columns 列
ES服务层的每个节点即使协调者又是worker(上面有多个分片shard)
应用层由ESDB的写客户端和查询客户端组成,对外提供RestfullAPI
写客户端在ESDB的负载均衡器的配合下,ESDB的写客户端使用以下技术来加速写和缓解热点问题。
分析:
查询客户端。ESDB从Elasticsearch继承了RESTful API和查询语言ES-DSL。Xdriver4ES采用了一个智能翻译器,可以从SQL查询中生成具有成本效益的ES-DSL查询,优化点:CNF/DNF转换、谓词合并。
控制层由一个主节点Master、协调者节点Coordinator和一个收集工作负载均衡指标的监视器Monitor组成。
协调器节点Coordinator
主节点Master
又分为以下3个模块
只为最经常被查询的子属性建立索引,因为有的子属性的确超级高,有的的确超级低
Worker在其本地SSD中维护分片和副本,并执行读/写工作负载。每个worker维护自己的存储,与其他worker无关。
哈希即使用哈希算法对K计算得到一数值,之后Mod 总数N。
双重哈希: 引入一对独立的二级哈希函数,以便在第一个哈希函数引起碰撞时产生偏移。
p=(h1(k)+ih2(k)) mod N,其中i随着碰撞序列的增长而增加,N是哈希表的大小。
ES中的双哈希策略
使用两个独立的哈希函数,两个属性k1和k2以及一个全局静态变量s
p=(h1(k1) + h2(k2)mod s) mod N --> h1(k1) mod N + (h2(k2)mod s) mod N
k1是负载均衡的主要属性(如租户ID),k2是为每个交易唯一生成的密钥(如交易ID),1≤s≤N,s∈Z。
对于ES,即为 h1(k1) mod N + 偏移量,于是分片便从原来的单个分片变成了一个范围,对于给定的K1,K2通过该公式能唯一确定具体分片。只是K1的范围变大了。
当s=1时(任何整数求余1为0),双哈希退化为哈希 --> 单个分片。
当s=N时,双哈希将记录路由到所有分布式分片。它使所有租户的工作负载完全平衡,但需要查询执行从所有分布式分片中搜索和汇总查询结果,从而导致低查询吞吐量。一旦数据以分布式方式存储,如sort和topK,会更加耗时。
概念
固定的最大偏移量s被替换为L(K1)。1≤L(K1)≤N,L(K1)∈Z,L(K1)为租户K1实时工作负载和当前存储容量
原公式:p=(h1(k1) + h2(k2)mod s) mod N --> p=(h1(k1) + h2(k2)mod L(k1)) mod N
在实践中,二次哈希的最大偏移量s=L(K1)依赖于两个指标:
三种哈希方式对比
哈希法:只能根据分区密钥(如租户ID)的1级哈希来路由工作负载,因此当它需要平衡多租户的工作负载时,就会出现问题。每次都是固定的一个。
普通的双重哈希:能够根据两个键(即租户ID和记录ID)的2级哈希将租户的工作负载路由到一组固定的连续分片,但不能管理动态工作负载。每次都是固定的范围。
动态二级哈希:也将工作负载路由到连续的分片,当工作负载增加时,它能够扩展到连续的分片(例如,图4中L(K1)从8增加到16)。可以动态扩展分片范围。
读写一致性 Read-your-writes Consistency
二级哈希偏移的变化会给读写一致性带来风险,因为使用的分片范围变化导致写入的范围变化,对应查询的范围也需要变化。
ESDB的初始化和负载均衡运行时维护二级哈希规则列表R。每个二次哈希规则被维护为一个元组(t, s, k_list),其中t代表规则生效的时间,s是二次哈希的最大偏移量,k_list记录在二次哈希中采用s的租户ID。
用哈希规则进行负载均衡
在初始化阶段(第5-10行),负载均衡器从每个租户的当前存储中建立二级哈希规则。
在运行阶段(第11-21行),负载均衡器根据实时工作负载的波动,更新二级哈希规则列表R,主要是为热点数据的租户进行更新。
写操作(如INSERT、UPDATE、DELETE)(通过租户ID k1、记录ID k2和记录创建时间t_c(create)标识),到达协调器节点,协调器负责从R中选择一个匹配的二级哈希规则。该规则(t, s, k_list)必须满足以下三个条件:
读操作(SELECT)到达时,ESDB查询客户端使用k1的匹配二级哈希规则(t, s, k_list)来决定查询应该在哪个连续的分片上执行,即从分片h1(k1) mod N到分片(h1(k1) + s-1) mod N。
共识
在ESDB中,每个节点都是一个协调者,负责将工作负载路由到匹配的分片。因此,一旦任何节点上产生了新的规则,整个系统必须对二级哈希规则列表R达成共识,以确保严格的一致性。
因为R是一个append only的列表,每个规则都与有效时间有关,不需要决定规则的排序(因为它们是按有效时间排序的),因为可以简化为决定是承诺还是中止。在ESDB中,我们提出了一个2PC的变体协议,其灵感来自于Spanner的提交等待机制,以解决工作负载阻塞的问题。图5显示了这个协议的概况。
准备阶段。每当协调者节点建立了一个二级哈希规则,它就会将新的规则发送给ESDB集群的Master。Master决定规则生效的有效时间t(使用本地时间),人工配置的时间间隔T,t = timer.now() + T.Next ,主节点将有效时间作为建议广播给所有参与者节点(应该是所有协调者)。当一个参与者收到建议时,它检查其所有记录是否在有效时间之前被创建如果这个条件得到满足,参与者会阻止所有创建时间晚于有效时间的工作负载,并向主节点回复一个接受信息。否则,参与方会向主节点报告错误。如果主节点收到任何错误信息或检测到任何超时(参与者在T/2内没有回应),这个二级哈希规则将被终止。否则,提交阶段开始。
提交阶段。主节点向所有参与的协调者广播提交消息以及二级哈希规则。由于所有节点在上一阶段达成了共识,它们将接受提交消息并将该规则添加到它们的本地二级哈希规则列表中。一旦提交阶段完成,各节点将删除工作负载的执行块(即继续处理创建时间大于有效时间的工作负载)。
选择时间间隔。时间间隔T为系统提供一个缓冲期,以便就二级哈希规则达成共识。T应远远大于广播的往返延迟(如100ms)和整个集群的本地时钟偏差的最大值(在ESDB中不超过1s),以确保严格的一致性。同时,T应短于我们预期的负载均衡时间(如60s),以保证有效性。只要T大于集群达成共识的时间,ESDB的共识协议就能实现工作负载处理的无阻塞。
容错性。尽管ESDB的共识协议保证了R的严格一致性,但在提交阶段,它仍然会受到网络分区和节点故障的影响。ESDB采用了一种自动容错的解决方案。然而,它依赖于对网络分区和节点故障的检测,也就是说,它需要区分暂时没有响应的节点和故障节点。一个典型的解决方案是使用一个预先定义的超时;在ESDB中,我们手动验证一个提出的警报(一个节点变得无响应),以明确决定是否发生了节点故障或网络分区。
不要求副本执行相同的写入工作负载(即逻辑复制),而是直接复制段文件。当主分片刷新一个新的段文件时,它初始化一个复制过程,在此期间,副本计算段差(即位于两个分片上的段文件之间的差异),并从远程分片上请求缺少的段。
ESDB采用了物理复制,目的是减少复制过程中产生的CPU开销。具体来说,我们设计它是为了克服以往工作中看到的两个缺点。1)冗长的单体复制过程,很容易被新一轮的复制过程打断;2)复制大型合并段所造成的较长的可见性延迟(即一个段在主分片和复制段上变得可见的时间戳之间的间隔)。ESDB的物理复制框架(如图9所示)由三种复制机制组成。
当刷新间隔较短时,上述机制稳定了物理复制过程,并保证了最新片段的复制。
这篇论文对于不搞ES的大概比较有启发性的有以下几点: