早在公元前五百年,孙子就参透了数据库分区的真谛

宇文湛泉 2019-07-01 10:59:19
作者介绍

宇文湛泉,现任金融行业核心业务系统DBA,主要涉及Oracle、DB2、Cassandra等数据库开发工作。

 

数据库分区,我觉得是一个称得上“伟大”的数据库存储结构概念。

 

如果说,一个编程者(并非一个职业DBA)除了关注表结构本身以外,分区,可能就是所需要关注的最靠近底层的一个数据库的设计。

 

例如像数据库的表空间这样的概念,通常一个普通开发人员,就未必会去关注。但是分区概念却不一样,因为它与应用场景结合更加紧密。比如,按时间、按地区、按类别分区,等等。

 

我猜测很多人是不赞同「数据库分区是个“伟大”的概念」这个观点的。原因是,我翻阅了各种市面上主流数据库的书籍,入门级的如《XX数据库从入门到删库跑路》,看起来高端的如《XX数据库的技术内幕》,大部分书籍在介绍分区的内容的时候,都没有给很大的篇幅。

 

甚至有好几本书上,就这么简单的来了一句:对于一个很大的表,如果每次搜索都对全表进行扫描,会很耗时间,如果分区了之后,只要访问某个分区就会很快。

 

这难道不是一种误导么?分区真的能这么简单就讲清楚吗?我觉得当然不是,所以本文的内容就是想说说我认为的,分区的重要和伟大之处。

 

一、数据库为什么要有分区?

 

在《孙子兵法》的第五篇 —《兵势篇》中,孙子曰:凡治众如治寡,分数是也。斗众如斗寡,形名是也。

 

说人话:凡是在管理很大的数据库表时就和管理小的数据库表时一样,把它分区就好了;发挥好所有分区的功能就和发挥好其中一个分区的功能一样,他们能有一个统一的表名,可以使用统一的SQL语句来调度就好了。

 

上面这段话,从分号隔开,可以分成上下两句。那么“伟大”是在于前半句还是后半句呢?显然是后半句。为啥?你把人分了容易,但是你要把茫茫多的已经分开的人,指挥得像一个人一样,这件事情就伟大了。

 

1、考虑隔离与瓶颈
 

 

好,先讨论上半句,分治的思想。我常常遇到这样的对话场景。

 

场景一:

 

对话者:“我们的数据太大了,我们分表吧!”

 

我:“为什么不用分区呢?”

 

对话者:“分区可以解决吗?”(请自己脑补不信任的眼神和质疑的语气)

 

场景二:

 

演讲者:“我们考虑用最先进的分库技术理念来解决这个瓶颈”。

 

众人投去了羡慕嫉妒而又膜拜的眼神,等待着大神讲出华丽又牛叉的方案。

 

我:“这里有个瓶颈,我们需要给这个表分区。”

 

众人轻视又不懈说,好的,那分一下吧。

 

其实无论是分区、分表还是分库,我们都需要围绕两个重要概念,一是隔离,一是瓶颈。

 

在场景一种,为什么使用者要求分表呢?常见的情况中,例如,我这两个表,虽然结构一样,但是我一个是北京的数据,一个是上海的数据。如果北京的数据坏了,或者,我在对北京的数据进行奇奇怪怪的操作的时候,我需要对上海的数据完全没有影响,这就是所谓隔离。

 

又比如,可能全国的数据放在一起量太大,交易量太多,出现了,磁盘、IO、网络、CPU等撑不住的情况,这就是所谓瓶颈。

 

分区、分表、分库满足的是不同的隔离级别,以及解决不同的瓶颈。但是,他们的思想是非常接近的。

 

2、Partition与Sharding
 

 

Sharding这个词(通常译为分片),可以说是自带高贵的属性。每当与人讨论数据库技术,一聊到Sharding,就有一种自然而然上档次的感觉。

 

而且,它有很多好朋友,说出来各个华丽无比,比如分布式、集群、大数据等等。

 

而Partition这个词,虽然从很多角度上来看,都很类似于Sharding,或者说它们都是从“分数是也”的理念而来。但就是感觉LOW。

 

为啥?按我的理解,因为Partition的实现是由DBMS来完成的,使用者没感觉。而Sharding往往需要程序,设计模式,乃至整个架构的设计围绕着它服务,十分有存在感。

 

那它们在实际运用的时候有区别吗?当然有!那么什么时候应该Sharding,什么时候应该Partition呢?

 

我个人觉得Sharding的使用有两种情况,第一种叫没钱的时候,第二种是Partition用到极致,也搞不定的时候。

 

那么Partition什么时候搞不定呢?又有两种,一种叫没钱的时候,第二种叫超过了当今世上的硬件极限(买最贵的设备都抗不住)的时候。

 

1)没钱的时候

 

我们细细思量可以发现,分库分表这个套路在什么数据库上用的范围最广?或者你在什么地方见得会比较多?我觉得通常会指到同一个地方,叫MySQL。

 

虽然它有各种各样华丽的马甲,比如腾讯的TDSQL。为什么要用MySQL?因为开源免费。什么?InnoDB也很强大?如果明天Oracle开源免费,你选型的时候还会选MySQL?

 

不扯远了,MySQL为啥有那么多分库分表呢?我认为真相只有这一个。因为5.1版之前,MySQL不支持分区。这就是我说的第一种情况,叫没钱的时候,Oracle、SQL Server、DB2我通通买不起。好了,下一个免费的MySQL,做分库分表。

 

抛开玩笑的内容,Partition确实有解决不掉瓶颈需要使用分库分表的时候。但是,一个成熟的数据库使用者不应该滥用分库分表。这就是所谓DBA界分库铁律第一条,我非常赞同,叫做:能不分,就不分。然后,说说,第二种,Partition用到了极致的场景。

 

2)超过当前硬件极限

 

简单的说,我买了个Oracle,但是我的系统TPS要1万。在X86上跑Oracle,撑不住怎么办?

 

那么出路两条,第一条,Exadata了解一下?IBM主机买一台?买不起,好,我们在MySQL,或者X86上用Oracle做分库分表,这就是第二类里面的没钱的时候。

 

还有一种就是Exadata、IBM主机撑不住,所谓超越了地球上科技产品的极限,反正我没见过。

 

3、分库、分表、分区的使用场景
 

 

看到这里,是不是觉得扯淡内容太多了。到底什么情况分区?什么情况分表?什么时候分库呢?还是那句话,第一看隔离级别,第二看瓶颈。前一段主要说的是瓶颈场景,那么从隔离级别上来看呢?

 

1)分库选择

 

两份结构一样的数据。但他属于两个客户,客户说,我有监管要求,我不能把数据和别人的数据放在一起,必须绝对的隔离,有严格的访问控制,别人根本不能有我的DB服务器的登陆权限。

 

这就要求,数据在数据库层面就完全的隔开,就可以用分库。

 

2)分表选择

 

如果说我是一个云提供商,两个客户都用我的客户,他们分别有自己的用户(Schema),他们允许和他人共用数据库服务器,但是,严禁其他人使用归属于他的数据库表。

 

那么这时候,可以分表。

 

3)分区选择

 

如果,两份数据要求简单的隔离,相互处理不影响就可以了,有时候,我还希望一个用户一条SQL,就能对比分析他们的差异。

 

那这时候,分区就是一个极佳的选择。

 

二、数字库分区的优势

 

接下来讲后半部分,斗众如斗寡,形名是也。

 

1、分区的同时处理掉技术关口
 

 

斗众如斗寡,这句话讲起来简单,实现起来可不是那么简单。在主流的DBMS里面,不同的分区意味着不同的对象(如Oracle中,不同的Segment,DB2分区表不同分区对应数据文件)。

 

不夸张的说,很多数据库中间件产品,在反复纠缠、实现的内容,其本身就是主流DBMS在分区时要处理掉的技术关口。

 

比如有:跨库的SQL重写,一个运行在分布式场景的SQL,需要被中间件重写成多个SQL,丢到多个库去执行。尤其是,跨库的连结、聚合(包括Max、Min、Sum、Count)。其实在多分区的时候,一样存在这样的问题。

 

如果说这个问题,不太容易被开发者所关注到。再举一个例子:全局/本地索引。

 

在DBMS中,全局索引通常是要在设计上有所回避的。一般数据表一旦分区,意味着数据量比较大。例如最常见的B树索引,全局索引意味索引层次多,查询速度慢。

 

但是当无论如何,我在查询时,无法送入分区KEY时,全局索引总归还是最后的选择。而在分布式场景里,没有分库KEY就会非常尴尬,要么查不了,要么就要遍历所有的库,这个代价几乎就是不可接受的。

 

2、分区与优化密不可分
 

 

分区和分库面临的很多问题都存在着巨大的相似之处,开发者总是会重视分库所面临问题和困难,然而普遍会轻视分区可能带来的副作用。

 

我常常被质问,数据多了,为什么不加分区!?并发有冲突,为什么不加分区!?程序速度慢了,为什么不加分区!?PS:这一系列的质问,同样适用于为什么不加索引系列。

 

这种轻视源自于,主流DBMS系统对分区功能的很好的支撑。作为DBA,比较害怕的是有人懂一点数据库,且特别自信的站在他自己立场来和我讨论。

 

有一些开发者传递给我一个理念,分区多比分区少要好。只要用了分区,就能更快,至少不可能更差!开发者在说起这些的时候,非常的自信。

 

其实,在我看来分区设计和查询优化是密切相关的。分区设计一定是与特定的查询场景匹配,才能达到好的结果。

 

比如之前提到的没有分区列送入的查询需要逐个遍历每个分区,再比如说Hash分区后的范围查找。

 

Oracle中索引的Hash分区,在大规模的高并发插入的数据库表上应用十分常见,因为这种场景非常容易产生冲突事件 ,使用Hash分区之后,可以极大的缓解。然而这也挖下了一个坑,那就是这个索引上的范围查找就变得不那么理想。

 

由此推论,分区上可以能遇到的大坑,其实在分库的场景同样使用。

 

比如说,做了分库,却发现交易的业务场景拿不到Sharding Key。采用了Hash的算法,算Sharding Key,又突然发现有个交易要范围查Sharding Key,以至于可能要去各个库兜一圈。

 

分区面对的坑,可以说在分库场景下,是急剧放大了。所以,不可以随意滥用分区,更加不可以随便乱分库。

 

3、容错率提高
 

 

反过来讲,我为何在开篇说分区十分伟大,尤其是Oracle。在你违背了种种优化原则之后,做了NPI,做了跨分区的JOIN等等。尽管有性能上的损失,无论如何,数据库都帮你把那些复杂的工作给完成了。

 

当你的需求不是实时响应时,这些奇奇怪怪的跨分区要解决的问题,Rdbms都还是透明的把这些工作给你做完了,这怎么能不说它是伟大的呢?这一点我想那些开发分布式数据库中间件的人,最能认同。

 

三、写在最后

 

最后写到这里,作为一个DBA,我特别希望给程序开发者传递一个理念,那就是这世界上一定没有一个完美的优化方案。

 

你要一个表,又能高并发插入,又能做各种各样高效的查询,一会范围,一会模糊。我只想说,这是不可能的。所有优化方案都是以牺牲某一种性能来换另一种性能。

 

而DBA的职责,就是搞清楚业务需要什么,不需要什么。牺牲掉不需要的功能,换取需要的功能,甚至于牺牲不常用的功能,换取优异的常用功能。这其实就是完美的方案,分区也不例外。

 

在本文的最后,我想留一个我一直困惑的问题。在我看了无数篇分库分表放在一起的文章和帖子之后,我开始怀疑人生了。

 

我很想知道,无论在什么RDBMS中,是不是真的有应该采用分表,而不适合采用分区的场景呢?这个场景用分表解决了什么问题,是不可以依靠分区来解决的,欢迎留言。

活动预告