面对一些海量数据集或非常高的查询压力,复制技术还不够,还需要将数据拆分成为分区,也称为分片
分区,在不同系统有着不同的称呼。
MongoDB
、Elasticsearch
、SolrCloud
的shard
,
HBase
的region
,Bigtable
的tablet
,Cassandra
和Riak
的node
Couchbase
的vBucket
分区通常是这样定义的,即每一条数据(或者每条记录,每行或每个文档)只属于某个特定分区。
每个分区可以视为一个完成的小型数据库。(即:每个分区基本保持独立运行)
采用数据分区的主要目的是:提高可扩展性
如果节点平均分担负载,那么理论上10个节点应该能够处理10倍的数据量和10倍于单个节点的读写吞吐量(忽略复制)
每个分区基本保持独立运行。
如果分区不均匀,则会出现某些分区节点比其他分区承担更多的数据量或查询负载,称之为倾斜
避免热点,最简单的方法是将记录随机分配给所有节点上。
缺点:当试图读取特定的数据时,没有办法知道数据保存在哪个节点上,所有不得不并行查询所有节点。
按照一段连续的关键字或者关键字区间范围(以最小值和最大值)来分区。
分区边界可以由管理员手动确定,或者由数据库自动选择
缺点:某些访问模式会导致热点。
例如:分区对应一个时间范围,每天一个分区(即以时间戳为关键字)
这会导致该分区在写入时负载过高,而其他分区始终处于空闲状态
解决方案:
根据业务划分,需要使用时间戳以外的其他内容作为关键字的第一项。
一个好的哈希函数可以处理数据倾斜并使其均匀分布。
例如,
Cassandra
和MongoDB
使用MD5
,Voldemort
使用Fowler-Noll-Vo
函数
基于哈希的分区方法可以减轻热点,但无法做到完全避免。
一个极端情况是,所有的读/写操作都是针对同一个关键字,则最终所有请求都将被路由到同一个分区。
一些名人用户有数百万的粉丝,当其发布一些热点事件时,出现大量的对相同关键字的写操作(其中关键字可能是名人的用户ID,或者人们正在评论的事件ID)
如果某个关键字被确认为热点,一个简单的方案:在关键字的开头或结尾处添加一个随机数。
只需一个两位数的十进制随机数就可以将关键字的写操作分布到 100个 不同的关键字上,从而分配到不同的分区。
读复杂度:之后的任何读取都需要些额外的工作,必须从所有100个关键字中读取数据然后进行合并。
缺点:无法良好的区间查询
Tips:相关技术需要看下版本。
在
MongoDB
中,如果开启了基于哈希的分片模式,则区间查询会发送到所有的分区上。
而Riak
、Couchbase
、Voldemort
就不支持关键字上的区间查询。
即:键的一部分来标识分区,而另一部分来记录排序后的顺序
Cassandra
则在两种分区策略之间做了一个折中。
Cassandra
中的表可以声明为由多个列组成的复合主键。
复合主键只有第一部分可用于哈希分区,而其他列则用作组合索引来对Cassandra SSTable
中的数据进行排序。
因此,它不支持在第一列上进行分区查询,但如果为第一列指定好了固定值,可以对其他列执行高效的区间查询
组合索引为一对多的关系提供了一个优雅的数据模型。
键-值模型相对简单,即都是通过关键字来访问记录,自然可以根据关键字来确定分区,并将读写请求路由到负责该关键字的分区上。
二级索引通常不能唯一标识一条记录,而是用来加速特定值的查找。
例如:查找所有颜色为红色的汽车。
可以理解为,二级索引相当于关系型数据非主键索引
参见 MySQL Innodb。
二级索引也是
Solr
和Elasticsearch
等全文索引服务器存在之根本
有两种主要的方法来支持对二级索引进行分区:基于文档的分区 和 基于词条的分区
场景:
一个数据库用ID进行分区,例如: ID 0~499归分区0, 500~999划为分区1
键(ID,color,make)对应(主键,颜色,厂商)
现在分别针对 color 和 make 建立索引
在这种索引方法中,每个分区完全独立,各自维护自己的二级索引,且只负责自己分区内的文档而不关心其他分区中的数据。
因此文档分区索引也被称为 本地索引,而不是 全局索引
这种查询分区数据的方法有时也称为 分散/聚集,查询代价较高。
即使采用了并行查询,也容易导致读延迟显著放大
对所有的数据构建 全局索引,而不是每个分区维护自己的本地索引。
为避免成为瓶颈,不能将全局索引存储在一个节点上,否则就破坏了设计分区均衡的目标。
全局索引也必须进行分区,且可以与数据关键字采用不同的分区策略
这种索引方案称为 词条分区,它以待查找的关键字本身作为索引。
即,索引的value 包含所有的关键字
同样,索引分区可以是 直接分区 和 哈希分区:
写入速度较慢且非常负责。
主要因为单个文档的更新时,里面可能会涉及多个二级索引,而二级索引的分区又可能完全不同甚至在不同的节点上。
对于词条分区来讲,这需要一个跨多个相关分区的分布式事务支持,写入速度会受到极大的影响。
实践中,对全局二级索引的更新往往都是异步的。
同时,当底层设施出现故障时,也有可能需要等待很长的时间。