mysql可扩展(读书笔记)

1、知识框架图

2、MySQL可扩展的理解

     2.1 可扩展的定义?与高可用的区别?

     2.2 扩展与投入的关系?

     2.3 扩展和拆分的概念区别?

3、垂直拆分

4、水平拆分

     关注下面几个核心问题:

     4.1 什么是水平拆分?     

     4.2 拆分原则有哪些?      

     4.3 如何进行水平拆分,有哪些切片方式,优劣比较?     

     4.4 如何选择分区键?     

     4.5 如何生成全局唯一ID?

5、实践

6、参考资料

一、知识框架图


mysql可扩展(读书笔记)
       

二、可扩展的理解

       可扩展性,是指随着系统负载的扩充,系统的可持续扩充能力,是一种软件系统处理能力相关的设计指标。系统扩充中,在尽量不影响现有系统的前提下,能够通过较少的变更或新增硬件资源,就能获取系统处理能力的提升,保持高性能。理解了这层定义,就不难理解可扩展和高可用的区别。“可扩展”代表了系统的伸缩能力,当系统负载迅速变高时,能以较小代价的调整来扩充系统的处理能力,持续提供有效支撑。而”高可用“则关注了有效服务处理能力的持续可用性,比如系统单点故障、某些节点的性能瓶颈导致服务无法响应或处理能力严重降低、宕机等都属于高可用相关。
       关于扩展与收益的关系。现实中大部分系统都不能获得线性扩展,而是以更低的扩展系数进行扩展。这部分可以参考USL可扩展性定律(《高性能mysql》)。 注意,存在扩展系数高于线性扩展的情况,比如某些条件下扩充一倍的缓存资源后,应用程序的查询命中率获得了超出一倍的提升,这个要结合场景具体分析。

       关于”扩展“和“拆分”的概念理解。当数据量大到一定程度后,读写效率会降低,如果想保持mysql服务器高效运转的话,就需要想办法进行扩展,比如各种分库分表方案。在《高性能Mysql》一书中,作者划分了”垂直扩展“,”水平扩展“,”向内扩展“,”多实例“,”集群“等多种方式。在《Mysql运维内参》中则主要关注了“水平拆分”和“垂直拆分”。起先并没意识到“扩展”和“拆分”的区别,其实两者切入点还是不同的,个人理解如下:“扩展”侧重于mysql服务器方面,“拆分”则更多是数据库(设计)层面。垂直扩展是想办法提升单机硬件性能,提升单实例的狐狸能力;而水平扩展的思路则是将数据库分布到多台机器上,分化负载。整体的关系可以参考第一部分的知识结构图,本篇文章主要是谈谈对“拆分”的理解和总结。

 

三、垂直拆分

       垂直拆分,所谓的”垂直"可以理解为被分隔后的数据库相互独立且各不相同,一般在考虑拆分时应优先考虑垂直拆分。

比如一个论坛将用户数据和内容数据统一存储到单个数据库,随着业务量的增长,单个库已不能满足读写TPS量。这时研发人员对业务架构进行重构升级,拆分出3个业务库帖子、评论、用户,部署到同一个实例,暂时满足需求;

业务量进一步增长,单个数据库实例已经接近性能瓶颈,无法满足快速增长的存储需求和高并发需求,研发人员将3个业务库分别拆分到单独的mysql实例上;

业务量进一步增长,又面临DB瓶颈,研发人员这次将三个库的多张表分别拆分到多个实例上,比如将用户数据的多张表遍布于多个实例中,缓存了服务器压力。

业务量进一步增长,用户数达到数亿级,不同运营部门对数据的处理和使用需求日益增多和多样化,热帖的并发访问量巨大,这时垂直拆分已经无法满足,需要考虑使用水平拆分了。


mysql可扩展(读书笔记)
上面只是个简单的例子。另外,在业务发展中因为原先数据库设计不合理或单表容量过大等原因而对表结构进行改造,将单表拆分为多张不同结构的表也属于垂直拆分,如下图,mysql可扩展(读书笔记)“垂直拆分”的优点是各部分数据仍然是相对完整的对象,并没有显著降低业务的理解复杂度和处理复杂度。但如果数据量持续膨胀,垂直拆分并非终极解决方案,当单表容量超过数百G时,很难满足公司的多查询维度和高并发场景。个人觉得这部分关键是理解什么是垂直拆分,何时可进行拆分,如何从某切入点(业务,实例,库,表等)进行拆分,垂直拆分的扩展局限性等。

四、水平拆分

      什么是水平拆分?

      拆分原则有哪些?

      如何进行水平拆分,有哪些切片方式,优劣比较?

      如何选择分区键?

      如何生成全局唯一ID?

 

(1)什么是水平拆分?

       水平拆分是对某个库或表按照指定算法或规则拆分为多个相同结构的库或表,以减少单库/表体积,将读写请求分配到各分库或分表上从而成倍的提高整体处理能力。下面是一个案例,


mysql可扩展(读书笔记)

最初是一张订单表,承载用户中心、支付中心、商户中心等多个部门的写请求和多维度查询请求。单表容量迅速达到了上千万条记录,研发同学先将订单表按用户id拆分为10个表,降低每张表的容量。为了满足商户中心查询需求同时减少对订单应用的影响,又通过复制和处理组件得到一份按商户id分表的备份,如下。 ==>>>


mysql可扩展(读书笔记)单库吞吐能力成为瓶颈,研发同学按照特定分库分表算法将订单库拆分为多库多表,这是能满足今后几年业务发展了。==》

mysql可扩展(读书笔记)
 

(2)拆分原则有哪些?

原则1:能不拆就不拆。       

        一般建议能不拆就不拆,而是优先考虑使用常用方法,如提升服务器性能,使用读写分离,优化数据库设计,使用和优化索引。现实中,存储模型与业务模型有一定关系,一定程度上反映了业务逻辑,任何拆分行为都或多或少的提升业务处理复杂度。分库分表可能会造成查询、合并和更新条件的分离,以及事务的分离(分库时可能要使用分布式事务保证数据强一致),使得业务处理时必须考虑这些因素,成倍提高了复杂度。 

     

下面是需要拆分的场景,

原则2:数据量太大,日常的运维影响了正常的业务访问       

        数据量太大,单库或单表数据很大(比如超过5千万条记录),此时日常备份、迁移数据、更新等运维需求会影响服务器处理能力,造成业务响应变慢,部分业务处理失败。此时,需考虑拆分。       

        > 备份、迁移数据:资源瓶颈是磁盘IO和网络IO;        

        > 更新:DDL时会长时间锁表,影响业务访问;热点数据频繁更新,会造成锁等待,表压力大,经常出问题。

 

原则3:某些数据出现了无穷增长       

        这个场景的典型案例是微博和社交评论系统的数据存储。比如大V的每条微博都会扩散给上千万粉丝,可能一些索引或路由有存储需求,此时最佳解决方案是水平切分,使用按用户、日期、用途、类别等各种方式。(延伸下... 实际中,这个场景在设计层面有个“推拉模式”来做优化,参考“微博fed系统推拉模式和时间分区模式架构谈谈”http://www.cnblogs.com/sunli/archive/2010/08/24/twitter_feeds_push_pull.html 和 新浪微博架构和FEED架构分析 http://blog.sina.com.cn/s/blog_53b95aec0100ujim.html )

 

原则4:业务耦合性考虑,拆分不同业务或对单业务重构模型       

        从业务高度,需要对业务架构和技术架构进行重构升级,比如将一个论坛数据库拆分为用户、文章、评论三个库,或者将用户中心、商户中心的数据库拆分到不同的实例中以避免相互干扰。


原则5:表设计不合理,需要对某些表字段做拆分         

        见“垂直拆分”部分的例子。

 

原则6:安全性和可用性的考虑

        分库分表客观上提升了安全性和高可用,单个实例的问题不至于影响整体业务。

(3)如何进行水平拆分,有哪些切片方式,优劣比较?     

        《高性能mysql》中作者将切分方式划分为几大类:固定分配、动态分配、混合分配、显示分配。分类方式有很多,这里暂时遵循着书中的这个框架来。

3.1 固定分配      

        指借助于固定规则的分区函数,只使用分区键就能计算出分库分表的结果,常用的hash和取模、移位、按顺序运算都属于这类,见上图x。      

        优点是简单、高效、未引入外部依赖等;      

        缺点是      

        a. 分片较少时,难以平衡负载;      

        b. 无法自定义将数据放到哪个分片,可能出现热点数据分布不合理的情况。比如按商户id进行hash运算时,几个大商户的数据被放置到同一张表,造成该表压力很大。      

        c. 修改分配策略比较麻烦。比如原先的分表规则是%32, 后来数据据量增大需增加分表数量,遂将策略调整为%64,需要提前做数据迁移工作。

3.2 动态分配      

        将分区键和分库分表的映射关系提前配置和存储,该存储节点所存的内容相当于分配函数。      

        优点是能够细粒度的控制“分配规则”,比如将热点数据均匀分布在多个库或表中,将不同用途的数据做隔离等;      

        缺点是引入外部依赖,可用性和效率方面不如固定分配等。  

3.3 混合分配      

        综合使用固定规则和动态配置两种策略,取两者优点,适用于一些特殊场景。比如根据url字段做分片,可以先对url做hash运算得出一个数值,然后根据动态分配方式映射到具体分表。

3.4 显式分配      

        将数据分片编码到ID中,感觉和静态分配本质上是一样的。见https://tech.meituan.com/dianping_order_db_sharding.html的分表键格式方案。

【这部分这样分类不是特别好理解,就实际中接触的项目中,使用最多的hash、取模、按顺序(如时间)等,需要根据业务数据的存储和处理特性来确定最佳方式。】 

(4)如何选择分区键 ?     

        首先要明白分区键的功能和影响,然后思考下随便选个分区键会产生什么问题?实际中应该根据业务需求来选择适用的分区键,避免跨分片查询,方便存取且尽量不降低存取性能。

        查询维度是多样的,有时可能需要多个分区键,比如在做一个评论应用中,有需要根据用户ID来查询用户所有的评论,也需要根据文章ID查询所有的评论,我们按用户ID或文章ID分片都不是好方法,因为总有一种要跨分片查询,我们的做法是存两份,分别以用户ID和文章ID进行分表,以满足上述两种查询场景。

        有些数据遍布在个分片,又需要频繁跨片查询,可以分析是否有其他等同策略。还是上面提到的评论应用,能够将一些信息抽象为整体数据,比如数量统计,可以抽取出单张公共表。

        这部分的重点是“选择分区键的方法”、“多分区键”、“跨分片查询问题”、“跨分片数据一致性”。

(5)全局唯一ID的生成     

        全局唯一ID推荐几个优秀的资源,

        1. 美团的分布式ID生成系统     https://tech.meituan.com/MT_Leaf.html

          2. 阅文集团分布式ID (类snowflake方法)              https://mp.weixin.qq.com/s?__biz=MzIxMzgxMjQ1Mw==&mid=2247483661&idx=1&sn=4e459c618a2169f994f62f28f2159eba&chksm=97b05487a0c7dd91e45cd2c9d0dcc837586ee2652abc9efb3252e69b1bd2d6b2bb450c8c2e58&mpshare=1&scene=2&srcid=0525IV9AydzO0CEN4CXkEFuk&key=cf192bba2f919fcb70ed66331b7f4f5d4c61927abd688c4674e6088b2743fb2e495a694189f8dab322e8030e8a9143b9932ed7851e58024d51807a665ab48b9c0bd8f176efa0b7383fe7e857d55eddb6&ascene=0&uin=MjM3ODQzNjgwMA%3D%3D&devicetype=iMac+Macmini7%2C1+OSX+OSX+10.11.5+build(15F34)&version=12020810&nettype=WIFI&fontScale=100&pass_ticket=iZnDMUQlGa2cTsRI6ffMQ%2B585Qs0ZJK0s9FedcMFAmailXPsFNG2IPQ3oF3mBt5o

          3. 贝聊亿级数据库分库分表实践 https://zhuanlan.zhihu.com/p/27363448

          4. 唯品会订单分库分表实践  http://www.infoq.com/cn/articles/summary-and-key-steps-of-vip-orders-depots-table

          5. 大众点评订单分库分表实践  https://tech.meituan.com/dianping_order_db_sharding.html

 

五、实践      

我们这边目前使用的是类似“数据库生成-步长方案”,数据库和代码片段如下。
数据库表:
+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field       | Type         | Null | Key | Default           | Extra                       |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| biz_tag     | varchar(128) | NO   | PRI |                   |                             |
| max_id      | bigint(20)   | NO   |     | 1                 |                             |
| step        | int(11)      | NO   |     | NULL              |                             |
| desc        | varchar(256) | YES  |     | NULL              |                             |
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+

代码摘要:IdTypeEnum即接入业务的枚举,mysql可扩展(读书笔记)

 

六、参考资料

 1、《高性能mysql》第三版

 2、《mysql运维内参》

相关推荐