分区表(Partioning)是关系型数据库里非常重要的性能优化手段,主要用于优化数据总量较大的表的请求筛选。

MySQL自5.1版本引入该特性,并一直完善至今。PostgreSQL也在最近的10.0大版本推出该项功能。

什么是分区表

从使用的角度来讲,分区表和普通的数据表没有什么区别。但是从维护的角度来看,分区表的底层是由一组数据表组成的。这些数据表的结构是一致的,区别在于它们会根据一些表字段按照某些规则来划分实际的数据。底层表的数据加总起来,构成了逻辑表的数据源。

相比于普通表,分区表的索引结构建立在底层表中,是相互独立的。由此带来的性能提升显而易见:每次查询的索引结构比普通表的更小。

对于B树索引,这意味着更小的B树,由此树的深度更浅,意味着更少的IO请求,甚至全树加载至内存中,查询效率更高。

对于Hash索引,这意味着Hash碰撞的概率更低。一般来讲,Hash索引的查询效率是O(1),而Hash碰撞会导致查询效率退化,最轻会退化至二叉树等级的查询效率O(log n),严重情况下会退化至链表等级的查询效率O(n)。目前不清楚MySQL和PostgreSQL是怎么处理Hash碰撞的,估计是前者。顺便吐槽一下,MySQL只在Memory引擎中支持Hash索引。

分区表的描述听起来很像数据分片(sharding)。

分区表的所有底层表都在同一个数据中实例中。

而数据分片功能是将数据按某种划分将数据分散存储在多个数据库实例中。

也就是说,数据分表不但拥有分区表的优势,同时将表的计算压力分布在多个计算资源上,有更好的性能优势。当然缺点就是,拥有所有分布式系统都有的一致性问题。

MySQL的数据分片可以使用NDB引擎实现,也可以依赖某些数据库中间件实现,例如YouTube的Vitess;而PostgreSQL的数据分片功能可以用Citus来实现,这个一个商业开源的数据库中间件,在苏宁易购有商业部署。

分区表是怎么划分的

MySQL的分区表功能发展已久,支持更多的划分方式:

  • 范围分区:分区字段指定了分区表划分的范围;
  • 列表分区:分区字段明确指定了分区表划分的键值;
  • Hash分区:分区字段按照用户指定的Hash函数计算出一个整数值,然后MySQL对该值按分区数求模,得到最终的存储分区编号
  • 键值分区:和Hash分区很像,只不过这个Hash函数是MySQL自己选择的。

而PostgreSQL目前支持两种方式:

  • 范围分区
  • 列表分区

如果要在实现PostgreSQL实现其他分区方式,文档推荐使用数据表继承、视图等方式来实现。

私以为,Hash分区也可以用触发器维护一个字段的Hash值求模列,对模值使用列表分区,可以实现同样的效果。当然这个方案的写入效率很可能不高,有机会的话测一测。

一些要点

关于创建和维护

分布表不是SQL标准,MySQL和PostgreSQL都支持分布表,但各自的管理方式并不一样,具体看手册。

关于使用

MySQL支持手动指定分区来完成查询。这样可以避免MySQL选择分区,有更好的计算效率。

SELECT * FROM tr PARTITION (p2);

总的来讲

感觉MySQL在分区表的功能更多一些,大象(PostgreSQL)还需要加把劲哟。

手册