统计信息是查询优化器的基础,用于估算查询的代价、选择执行计划,从而实现高效的数据访问。在分布式环境中,不同节点可能持有不同的数据分片(shard),因此统计信息的使用与集中式数据库有显著不同。在分布式数据库中,查询优化器面临的一个核心挑战是如何在局部统计信息(local statistics)和全局统计信息(global statistics)之间做出选择。即什么时候使用局部统计信息,什么时候使用全局信息。
首先,对于分布式数据库来说,首要解决的第一个问题是维护局部统计信息还是全局统计信息。维护局部统计信息肯定会比维护全局统计信息简单,但是很显而易见的在分布式数据库系统里局部统计信息是不够用的。一些全局统计信息的生成和维护技术被提出,但是技术理论实践在工业产品上并能够上线取得成功并不容易,很多开源和商业数据库只维护局部统计信息,或者仅维护全局统计信息。
局部统计信息的局限性
每个节点上的局部统计信息只反映了该节点所持有的数据的特征,例如某一列数据的分布或数据的选择性。但对于整个查询的全局视角,局部统计信息可能不足以做出准确的决策。例如,当一个查询涉及多个节点的数据时,优化器可能需要全局视角来估算:
- 数据分布的偏斜:某个节点可能拥有比其他节点更多的匹配数据,如果没有全局统计信息,优化器无法做出正确的数据重分布策略和并行策略,导致计划整体被短板拖慢。
- 连接操作的代价:涉及多个节点的数据连接需要全局数据分布信息来进行数据重分布或者数据汇总后的数据特征。如果仅使用局部统计信息,优化器可能无法有效估算连接的代价,导致选择次优的执行计划。
局部统计信息的使用场景
局部统计信息是每个节点基于其持有的局部数据生成的,它们直接反映了本地数据分布情况。通常情况下,查询优化器可以使用局部统计信息来做出局部决策,特别是在查询只涉及单个节点的局部数据分片,局部统计信息足够提供准确的估算。这时,无需考虑全局数据的分布。
另外全局统计信息一般收集和更新相对困难,获取相对比局部统计信息更为耗时。为了减少统计信息获取开销,对于简单查询或较低复杂度的查询,局部统计信息提供的优化可能已足够。对于这种情况下,读取全局统计信息会带来额外的计算成本,而不会显著改善查询性能。
全局统计信息的使用场景
全局统计信息是对分布式系统中所有节点数据的全局视角。它们能够反映全局数据分布的特征,特别是面临以下场景时:
- 跨节点查询:当查询涉及多个节点的数据时,局部统计信息不能提供准确的全局数据分布信息。比如,查询条件需要跨多个数据分片进行过滤时,查询优化器需要知道全局数据的分布,以正确估算查询的代价,尤其是数据倾斜(data skew)问题会影响性能。
- 复杂查询:对于复杂的多表连接、子查询、聚合查询等,涉及到多个节点时,局部统计信息容易导致错误的估计,进而选择次优执行计划。全局统计信息有助于做出更准确的代价估算,进而选择更高效的执行计划。
- 负载均衡与数据倾斜:全局统计信息对于解决数据倾斜问题尤为关键。数据倾斜会导致某些节点的工作负载过高,而其他节点空闲。通过全局统计信息,查询优化器可以在执行时更好地分配工作负载,避免瓶颈节点的出现。
全局统计信息的挑战
- 维护成本:全局统计信息的生成和维护需要跨节点的数据汇聚。这种汇聚通常是昂贵的,尤其是在节点之间的网络延迟较高或数据量庞大时。如何在保证精度的同时降低维护全局统计信息的成本,是一个挑战。
- 更新时效性:数据库中的数据是动态变化的,数据的插入、更新、删除操作时刻会影响统计信息的准确性。在一个分布式环境中,数据频繁变化时动态更新全局统计信息,同时不影响系统性能,是全局统计信息的难点。
混合选择策略
在分布式数据库中,选择使用局部统计信息还是全局统计信息,并不是一个简单的二选一问题。在很多场景下,查询优化器需要根据查询类型、查询代价、网络开销等因素灵活选择使用局部或全局统计信息。这要求优化器具备更加智能的决策机制,结合多种因素动态做出选择,而不是简单地依赖单一类型的统计信息。最理想的策略是结合使用局部和全局统计信息。在执行查询时,查询优化器可以首先根据全局统计信息选择整体查询计划,然后在每个节点上基于局部统计信息进一步优化局部执行。这种方法能够兼顾全局视角和局部效率,平衡查询性能和网络代价。以下通过例子来说明局部统计信息和全局统计信息的混合选择。
考虑一个查询如下,其中T1表按照a列进行数据分片。
select
*
from
T1
where
a = 1008
and b < '20240831';
这个查询首先根据分区键定位到其中一个node上,所以b上的过滤条件就可以选择使用node上的局部统计信息进行选择率估计。当然也可以使用b列的全局统计信息,但是即便不考虑全局统计信息的获取成本,全局统计信息也会比局部统计信息的数据特征更加粗粒度,从而损失部分特征细节导致估算偏差。
在此基础上修改查询:
select
*
from
T1
where
a < 1008
and b < '20240831';
这个查询无法根据分片键删的条件把查询定位到其中一个节点上,所以每个节点都需要执行这个查询。此时使用全局统计信息肯定是不合理的,使用某一个节点的局部统计信息也不能兼顾所有节点的数据分布情况,最好的策略是各个节点根据不同的局部统计信息生成不同的计划。
进一步修改查询,其中T2表按照c数据分片:
select
*
from
T1, T2
where
T1.a < 1008
and T1.b < '20240831'
and T1.c = T2.c;
因为T1按照a数据分片而T2按照c数据分片,两个表的连接键和分片键不一致,所以一个可能的计划选择是把T1满足条件的数据按照c重分布,然后把join下推到各个节点,也就是partitioned wise join。那么每个节点上partitioned wise join的join算法应该如何选择呢,一个很重要的因素取决于T1表数据重分布之后在每个节点上的数据量。此时单一依靠每个节点上的局部统计信息就不能够得到这一全局信息,从而必须依赖全局统计信息。
发展方向
分布式数据库的统计信息以及分布式查询优化器在实际业务里遇到的问题比以往使用单机数据库更为复杂,挑战更多,本文仅是从一个角度简要分析。随着分布式数据库规模的扩大以及查询复杂度的增加,未来的查询优化器需要更智能的策略来处理局部与全局统计信息的选择。自动化统计信息维护、分层统计信息模型以及基于机器学习的优化器设计,可能成为解决这一问题的方向。