分布式存储发展至今,SQL,no SQL new SQL系统层出不群,仔细观察研究这些系统就会发现它们无论采用了怎样的架构,无论是SQL,noSQL还是new SQL,无论是单模数据库还是多摸数据库,无论是KV存储,文档存储,文件存储还是图存储,它们都需要回答和解决至少以下几个问题:
1. 数据如何分片
2. 系统容灾备份
3. 系统扩容
我们首先阐述数据分片的问题。数据为什么要分片,核心原因是单机存储容量有限,不能存储和处理系统所有的数据,因此需要对数据进行分割,分别存储在不同的机器上。我在思考这个问题时候发现这里其实有很强的逻辑推理,之所以需要强推理是因为我们面临的问题的约束条件是客观存在的,在很长的时间轴线上不能改变,比如单机的容量和算力相比于我们要处理的数据是不足的。因此我们必须把数据分割开来分别存储,这样我们就需要解决两个问题,一个是如何划分数据的问题,一个是多台机器之间数据处理的协同聚合问题。
先说如何划分数据,经典的划分策略有hash shard;range shard。它们分别适用于不同的数据处理领域,我们需要明白一个道理,这个道理在计算机世界里依然使用,并且随处可见,我们忽略这个逻辑而进行的思考和应对一定是不全面的,那就是:凡有收益必有代价。
hash shard详细的介绍网络上一搜一大堆,讲的都比较明白和透彻,这里不再赘述,我想强调的一点是当我们选择使用hash shard的时候需要明白这样带来收益的同时我们需要付出的代价,大致看来有两个代价,一个是分片分裂,一个是范围查询。针对分片分裂,业界也有很多解决方法,基本上这个问题的解决思路还是比较清晰,但是我们仍然需要看到在特殊领域其局限性,举个简单的例子,Elasticsearch就是采用hash shard分片策略,但是Elasticsearch是一个文档索引系统,除了存储文档本身外还存储了海量的倒排索引数据,这些索引数据和文档数据存在在一起(好处显而易见,就是search 的时候效率高)因此在分裂的时候,把这些文档产生的索引数据和文档(有时候elasticsearch并不存储文档本身)都按照设定的规则(一般都是按照文档ID hash)识别出来作为快照以分裂数据并不是一件容易的事情。(我们在容灾备份中会讲述分片的数据迁移问题。)hash天然对range查询不友好,因为两个相邻的数据经过hash计算之后很难再落在一个分片上,因此当你对你的数据有强烈的scan需求时,hash shard并不是很好的选择。
range shard也是一种很好的分片策略,很多系统都采用了这种策略,比如spanner,tiDB等。同样按照代价理论我们分析range shard的代价。range shard的一个优势就是对scan操作很友好,但是确必须要解决两个棘手的问题,这两个问题基本上必须是在系统设计的时候就考虑清楚。一个问题是分裂,hash shard的分片策略我们在预先可以估计的数据量前提下可以预先按照数据容量分片,这样如果数据规模变化不大,我们基本不用考虑分裂,这也是elasticsearch 6.5版本以前可以无视分裂的底气,实际生产环境中我也看到基本上都是研发事先估算数据规模,部署足够容量的存储资源,预先分好分片。但是在按照范围(一般是字典序)分片,因为存在热分片,许多时候就是会出现某一个范围段的数据特别多,并且我们事先并不好评估预判,导致某些分片的数据量急剧增加,这样我们想通过分片解决数据在单机上不能全部存储和处理的问题或者风险仍然存在,因此我们需要系统设计的时候就需要设计分裂方案。同时我们在前面的描述中也看到range shard的另一个问题就是热分片的问题,因为热点访问是客观存在的,当出现热分片(热读热写)的时候,目前看到的相对有效的解决办法有:1. 将热分片单独调度到空闲的机器上以提升处理能力;2. 分裂热分片,降低单片的热度,但是这个也不是无限分裂,需要平衡,因为分片越多,元数据也越多,管理的成本就会增加,极端的讲我们不能为每一个数据建一个分片,这样元数据的量是几乎任何架构不能承受的。3. 业务层面通过缓存等手段降低对分片的访问。
无论那种分片策略都需要面对一个问题,那就是分片多大合适?这个问题的回答需要特定的场景,总的概括来说大有大的难处,小有小的困惑。
首先分片越大,元数据的量就越少,对元数据的管理就比较简单,但是分片越大意味这分片上的数据量越多,这样一些操作比如search,一般都会在各个分片上并行执行,由一个工作节点进行数据的聚合处理,就意味着一个分片上需要处理的数据量就会很大,时间就会很长,一次search操作的耗时就长,同时因为一个分片物理上属于归属与一个机器,造成IO资源(网络,磁盘)和CPU资源的使用不均匀,尽管多副本的时候,可以让其他的副本承担一部分工作,但是一般副本不会很多,分担能力有限,同时还需要容忍脏读。分片大还有一个后果就是当因为故障或者负载均衡导致数据迁移的时候,一次从一个分片上迁移的数据量也会很多,迁移的时间就会很长,期间发生失败的概率就很高,一旦失败就要重试,如果没有断点续传的特性,成本很高,我曾见过一个分片发生数据迁移,因为各种原因期间经常失败导致不断重试,但是原来的分片的数据量还在增加,导致这个分片越来越大,下次重试需要迁移的数据量更多,问题越严重…… ,另一个问题就是如果分片很大,负载均衡不好做,迁移的时候造成的波动很大,迁移的时间长,故障恢复时间就比较长。
分片小,意味着元数据就多,对元数据的管理就比较复杂,当集群容量很大的时候,元数据都需要分级存储处理,复杂度很高,另外这种模式意味着客户端不适合缓存拓扑,因为拓扑太大了,只能采用代理的模式,相反大分片模式下有些系统可以设计为重客户端模式。分片小意味着归属的数据量也少,带来的问题主要集中两个方面。1. search的时候需要访问很多的分片。 2. 分片小意味着一台机器上部署很多的分片,因为一般分片都是多副本的,各个副本之间需要同步数据,存活检查,造成网络IO的压力很大。
同时我们也应该看到不同的分片大小选择,带来的收益也是不一样的。对于大分片,首先元数据量很少,对元数据的管理就比较轻松,单次查询获得数据量比较多,交互少,对于分布式事务,很多场景可以优化成本地事务。对于小分片,因为数据量少,数据迁移很快,故障恢复时间短,比如磁盘故障了,因为分片的其他副本仍然可用,又分散在集群的很多机器上,这样这些机器都可以参与到数据恢复过程中,加速故障恢复。同时负载均衡比较容易做,迁移调度影响的范围和时间都很好控制。
有些系统天然就是适合大分片的,比如文档检索系统,其中缘由也不难分析,这里不再展开。
网友评论