背景

虽然近十年来各种存储技术飞速发展,但关系数据库由于其 ACID 的特性和功能强大的 SQL 查询,目前还是各种业务系统中关键和核心的存储系统,很多场景下高性能的设计最核心的部分就是关系数据库的设计。

虽然关系数据库厂商(Oracle、DB2、MySQL 等)在优化和提升单个数据库服务器的性能方面也做了非常多的技术优化和改进。但业务发展速度和数据增长速度,远远超出数据库厂商的优化速度,尤其是互联网业务兴起之后,海量用户加上海量数据的特点,单个数据库服务器已经难以满足业务需要,必须考虑数据库集群的方式来提升性能。

高性能数据看集群方案有:

1、读写分离,其本质是将访问压力分散到集群中的多个节点,但是没有分散存储压力;
2、分库分表,既可以分散访问压力,又可以分散存储压力。

读写分离

读写分离的基本原理是将数据库读写操作分散到不同的节点上,下面是其基本架构图。

读写分离的基本实现

  • 数据库服务器搭建主从集群,一主一从、一主多从都可以。
  • 数据库主机负责读写操作,从机只负责读操作。
  • 数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据。
  • 业务服务器将写操作发给数据库主机,将读操作发给数据库从机。
    需要注意的是,这里用的是“主从集群”,而不是“主备集群”。“从机”的“从”可以理解为“仆从”,仆从是要帮主人干活的,“从机”是需要提供读数据的功能的;而“备机”一般被认为仅仅提供备份功能,不提供访问功能。所以使用“主从”还是“主备”,是要看场景的,这两个词并不是完全等同的。

读写分离的设计复杂度

复制延迟

主从复制延迟会带来一个问题:如果业务服务器将数据写入到数据库主服务器后立刻(1 秒内)进行读取,此时读操作访问的是从机,主机还没有将数据复制过来,到从机读取数据是读不到最新数据的,业务上就可能出现问题。

解决主从复制延迟有几种常见的方法:

  • 写操作后的读操作指定发给数据库主服务器
  • 读从机失败后再读一次主机
  • 关键业务读写操作全部指向主机,非关键业务采用读写分离

分库分表

背景

读写分离分散了数据库读写操作的压力,但没有分散存储压力,当数据量达到千万甚至上亿条的时候,单台数据库服务器的存储能力会成为系统的瓶颈,主要体现在这几个方面:

  • 数据量太大,读写的性能会下降,即使有索引,索引也会变得很大,性能同样会下降。
  • 数据文件会变得很大,数据库备份和恢复需要耗费很长时间。
  • 数据文件越大,极端情况下丢失数据的风险越高(例如,机房火灾导致数据库主备机都发生故障)。

基于上述原因,单个数据库服务器存储的数据量不能太大,需要控制在一定的范围内。为了满足业务数据存储的需求,就需要将存储分散到多台数据库服务器上。

业务分库

业务分库指的是按照业务模块将数据分散到不同的数据库服务器。

业务分库能够分散存储和访问压力,但同时也带来了新的问题:

  1. join 操作问题
    业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join 查询。
  2. 事务问题
    原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。
  3. 成本问题
    业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑备份,那就是 2 台变成了 6 台。

基于上述原因,对于小公司初创业务,并不建议一开始就这样拆分,主要有几个原因:

  • 初创业务存在很大的不确定性,业务不一定能发展起来,业务开始的时候并没有真正的存储和访问压力,业务分库并不能为业务带来价值。
  • 业务分库后,表之间的 join 查询、数据库事务无法简单实现了。
  • 业务分库后,因为不同的数据要读写不同的数据库,代码中需要增加根据数据类型映射到不同数据库的逻辑,增加了工作量。而业务初创期间最重要的是快速实现、快速验证,业务分库会拖慢业务节奏。

如果业务真的发展很快,岂不是很快就又要进行业务分库了?那为何不一开始就设计好呢?

  • 首先,这里的“如果”事实上发生的概率比较低,做 10 个业务有 1 个业务能活下去就很不错了,更何况快速发展,和中彩票的概率差不多。如果我们每个业务上来就按照淘宝、微信的规模去做架构设计,不但会累死自己,还会害死业务。
  • 其次,如果业务真的发展很快,后面进行业务分库也不迟。因为业务发展好,相应的资源投入就会加大,可以投入更多的人和更多的钱,那业务分库带来的代码和业务复杂的问题就可以通过增加人来解决,成本问题也可以通过增加资金来解决。
  • 第三,单台数据库服务器的性能其实也没有想象的那么弱,一般来说,单台数据库服务器能够支撑 10 万用户量量级的业务,初创业务从 0 发展到 10 万级用户,并不是想象得那么快。

而对于业界成熟的大公司来说,由于已经有了业务分库的成熟解决方案,并且即使是尝试性的新业务,用户规模也是海量的,这与前面提到的初创业务的小公司有本质区别,因此最好在业务开始设计时就考虑业务分库。

分表

将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。此时就需要对单表数据进行拆分。单表数据拆分有两种方式:垂直分表和水平分表。示意图如下:

单表进行切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定,并不强制要求单表切分为多表后一定要分散到不同数据库中。原因在于单表切分为多表后,新的表即使在同一个数据库服务器中,也可能带来可观的性能提升,如果性能能够满足业务要求,是可以不拆分到多台数据库服务器的,毕竟我们在上面业务分库的内容看到业务分库也会引入很多复杂性的问题;如果单表拆分为多表后,单台服务器依然无法满足性能要求,那就不得不再次进行业务分库的设计了。

垂直分表

垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。垂直分表引入的复杂性主要体现在表操作的数量要增加。
原来只要一次查询就可以获取到需要的数据,现在需要多次查询才可以。

InnoDB逻辑存储结构
MySQL表中的所有数据被存储在一个空间内,称之为表空间,表空间内部又可以分为段(segment)、区(extent)、页(page)、行(row),逻辑结构如下图:

最基本的一行一行数据成为 row,管理数据的基本单位称之为 page,在 mysql 中每一页的大小都是固定的 16k ,保存 page 的单位成为 extent,默认情况情况下 extent 有 1M 的存储空间,也就是一个区可以装载 64 个连续的 page。

MySQL 中一个概念叫做压缩页,其可以对数据进行压缩,实际的数据存储会比逻辑存储的数据小、既然有压缩必然有解压缩,但是压缩解压缩效率并不算高,所以咋表设计时要确保在每一页存多存储行数据,这样就可以减少跨页检索。

所以基于垂直分表需要注意每个表字段的多少。

水平分表

水平分表适合表行数特别大的表,一般单表行数超过 5000 万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于一些比较复杂的表,可能超过 1000 万就要分表了;而对于一些简单的表,即使存储数据超过 1 亿行,也可以不分表。但不管怎样,当看到表的数据量达到千万级别时,就要警觉起来,因为这很可能是架构的性能瓶颈或者隐患。
水平分表会引入更多的复杂性,主要表现在下面几个方面:

路由

水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,这个算法会引入一定的复杂性。
常见的路由算法有:

范围路由

范围路由:选取有序的数据列(例如,整形、时间戳等)作为路由的条件,不同分段分散到不同的数据库表中。

范围路由设计的复杂点主要体现在分段大小的选取上,分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适的分段大小。

范围路由的优点是可以随着数据的增加平滑地扩充新的表。例如,现在的数据是 100 万,如果增加到 1000 万,只需要增加新的表就可以了,原有的数据不需要动。

范围路由的一个比较隐含的缺点是分布不均匀,假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1000 条,而另外一个分段实际存储的数据量有 900 万条。

Hash 路由

Hash 路由:选取某个列(或者某几个列组合也可以)的值进行 Hash 运算,然后根据 Hash 结果分散到不同的数据库表中。

Hash 路由设计的复杂点主要体现在初始表数量的选取上,表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。而用了 Hash 路由后,增加子表数量是非常麻烦的,所有数据都要重分布。

Hash 路由的优缺点和范围路由基本相反,Hash 路由的优点是表分布比较均匀,缺点是扩充新的表很麻烦,所有数据都要重分布。

配置路由

配置路由:配置路由就是路由表,用一张独立的表来记录路由信息。

配置路由设计简单,使用起来非常灵活,尤其是在扩充表的时候,只需要迁移指定的数据,然后修改路由表就可以了。

配置路由的缺点就是必须多查询一次,会影响整体性能;而且路由表本身如果太大(例如,几亿条数据),性能同样可能成为瓶颈,如果我们再次将路由表分库分表,则又面临一个死循环式的路由算法选择问题。

  • join 操作
    水平分表后,数据分散在多个表中,如果需要与其他表进行 join 查询,需要在业务代码或者数据库中间件中进行多次 join 查询,然后将结果合并。
  • count() 操作
    水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上还是会将这些表当作一个表来处理。获取记录总数用于分页或者展示,水平分表前用一个 count() 就能完成的操作,在分表后就没那么简单了。常见的处理方式有下面两种:
    1、count() 相加:具体做法是在业务代码或者数据库中间件中对每个表进行 count() 操作,然后将结果相加。这种方式实现简单,缺点就是性能比较低。例如,水平分表后切分为 20 张表,则要进行 20 次 count(*) 操作,如果串行的话,可能需要几秒钟才能得到结果。
    2、记录数表:具体做法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。

这种方式获取表记录数的性能要大大优于 count() 相加的方式,因为只需要一次简单查询就可以获取数据。缺点是复杂度增加不少,对子表的操作要同步操作“记录数表”,如果有一个业务逻辑遗漏了,数据就会不一致;且针对“记录数表”的操作和针对子表的操作无法放在同一事务中进行处理,异常的情况下会出现操作子表成功了而操作记录数表失败,同样会导致数据不一致。

此外,记录数表的方式也增加了数据库的写压力,因为每次针对子表的 insert 和 delete 操作都要 update 记录数表,所以对于一些不要求记录数实时保持精确的业务,也可以通过后台定时更新记录数表。定时更新实际上就是“count() 相加”和“记录数表”的结合,即定时通过 count() 相加计算表的记录数,然后更新记录数表中的数据。

3、order by 操作
水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。

##总结
综上所有,我们可以根据实际业务情况来选择哪种策略。

高性能数据库集群方案相关推荐

  1. 高性能数据库集群:读写分离

    目录 1.前言 2.读写分离 2.1 什么是读写分离? 2.2 什么情况下需要读写分离? 2.3 复制延迟 2.4 分配机制 2.5 Mysq支持的复制类型及与原理 1.前言 关系数据库由于其 ACI ...

  2. 数据库中间件支持数据库集群方案

    咏南数据库中间件支持数据库集群方案 通过咏南数据库中间件作为PROXY(数据库代理)实现的数据库集群,非数据库层面集群. 数据库可以分库分表进行水平或垂直拆分成数据库集群. 咏南中间件作为数据库集群代 ...

  3. 浅谈高性能数据库集群——读写分离

    作者 陈彩华 贝聊Java后端工程师 文章转载交流请联系 caison@aliyun.com 复制代码 最近学习了阿里资深技术专家李运华的架构设计关于读写分离的教程,颇有收获,总结一下. 本文主要介绍 ...

  4. 浅谈高性能数据库集群 —— 读写分离

    1. 读写分离概述 2. 适用场景 3. 引入的系统复杂度问题 最近学习了阿里资深技术专家李运华的架构设计关于读写分离的教程,颇有收获,总结一下. 本文主要介绍高性能数据库集群读写分离相关理论,基本架 ...

  5. 架构设计之「数据库集群方案」

    在之前的文章中,我们知道数据库服务可能已经成为了很多系统的性能关键点,甚至是瓶颈了.也给大家介绍了数据库服务器从主备架构.到主从架构.再到主主架构的基础方案.但如果单台机器已经不能满足完整业务数据存储 ...

  6. 数据库集群方案及Oracle RAC架构分析

    应对业务量的不断增加场景通常有两个大方向,一种是纵向扩展,也就是增加单台服务器的CPU计算能力.内存容量和磁盘承载能力等:另外一种是横向扩展,也就是通过增加服务器的数量来增加处理能力.前者存在业务中断 ...

  7. 高性能数据库集群:分库分表

    读写分离分散了数据库读写操作的压力,但没有分散存储压力,当数据量达到千万甚至上亿条的时候,单台数据库服务器的存储能力会成为系统的瓶颈,主要体现在这几个方面: 数据量太大,读写的性能会下降,即使有索引, ...

  8. mysql数据库集群方案

  9. MySQL 集群方案介绍

    mysql集群方案这里介绍2种,PXC 和 Replication. 大型互联网程序用户群体庞大,所以架构设计单节点数据库已经无法满足需求.大家也深有体会,有一万人在学校网站查成绩或是选课的时候网站时 ...

最新文章

  1. 苹果:高通的“非法行为”损害了整个行业
  2. Matlab 非线性规划问题模型代码
  3. 8个排序算法的稳定性总结
  4. 重磅!李宏毅教授机器学习训练营
  5. 14、使用play搭建一个web应用用例
  6. 【Ant Design Vue】之Grid栅格和Space间距
  7. 一个在线文本比较工具
  8. at24c32 linux,AT24C32使用方法总结
  9. java毕业设计—— 基于java+JSP+SSH的网上购物系统设计与实现(毕业论文+程序源码)——网上购物系统
  10. java反射--Field用法实践
  11. 【动态规划】字符串编辑距离(Levenshtein距离)算法
  12. 当你们玩挂机游戏累了(_杰森大师_JAVA)
  13. 开源表单推荐:Tduck 填鸭 —— 表单收集器
  14. 哲理小故事---理想和现实
  15. [免费专栏] Android安全之数据存储与数据安全「详解」
  16. android相册幻灯片功能,玩机教程 篇四十五:「MIUI玩机技巧63」MIUI相册新增“幻灯片播放”功能...
  17. html下拉菜单制作方法,CSS3制作Dropdown下拉菜单的方法
  18. Python培训课程怎么学
  19. ios CAShapeLayer
  20. python数据类型——数字

热门文章

  1. CSS 字体拉伸 font-stretch属性
  2. SFS简介(明暗恢复形状)
  3. 用H5游戏进行节日营销案列分享
  4. 基于 Docker 搭建 Hadoop 平台
  5. xman day03 pwn海无涯,回头是岸
  6. 家庭整理-《怦然心动的人生整理魔法》书中的精髓:如何利用怦然心动家庭整理法,拥有整洁、高效、全新的人生?
  7. ercharts一个页面能放几个_在同一页面中显示多个echart图表
  8. drm之dma-fence介绍
  9. Java开发环境搭建与实验教程(适合自学)
  10. java-php-python-ssm汽车维修服务系统计算机毕业设计