精华内容
下载资源
问答
  • 分库分表架构实践

    2017-12-23 15:00:52
    分库分表架构实践 2017-12-18 丁浪 架构师小秘圈 作者介绍: 丁浪,现就职于某垂直电商平台,担任技术架构师。关注高并发、高可用的架构设计,对系统服务化、分库分表、性能调优等方面有深入...

    分库分表架构实践

    2017-12-18  丁浪  架构师小秘圈

    作者介绍:


    丁浪现就职于某垂直电商平台,担任技术架构师。关注高并发、高可用的架构设计,对系统服务化、分库分表、性能调优等方面有深入研究和丰富实践经验。热衷于技术研究和分享。

    来源:infoQ||聊聊架构

    1 题记


    “分库分表”是谈论数据库架构和优化时经常听到的关键词。那么对于这些业务量正在高速增长的公司,它有那么容易实践吗?

    在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战。

    让人感到担忧的是,他们系统真的就需要“分库分表”了吗?“分库分表”有那么容易实践吗?为此,笔者整理了分库分表中可能遇到的一些问题,并结合以往经验介绍了对应的解决思路和建议。


    2垂直分表


    垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中,如下图所示:

    小结

    在字段很多的情况下,拆分开确实更便于开发和维护(笔者曾见过某个遗留系统中,一个大表中包含100多列的)。某种意义上也能避免“跨页”的问题(MySQL、MSSQL底层都是通过“数据页”来存储的,“跨页”问题可能会造成额外的性能开销,这里不展开,感兴趣的朋友可以自行查阅相关资料进行研究)。

    拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。


    3垂直分库


    垂直分库在“微服务”盛行的今天已经非常普及了。基本的思路就是按照业务模块来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。如下图:

    小结

    系统层面的“服务化”拆分操作,能够解决业务系统层面的耦合和性能瓶颈,有利于系统的扩展维护。而数据库层面的拆分,道理也是相通的。与服务的“治理”和“降级”机制类似,我们也能对不同业务类型的数据进行“分级”管理、维护、监控、扩展等。

    众所周知,数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈,是大型分布式系统中优化数据库架构的重要手段。

    然后,很多人并没有从根本上搞清楚为什么要拆分,也没有掌握拆分的原则和技巧,只是一味的模仿大厂的做法。导致拆分后遇到很多问题(例如:跨库join,分布式事务等)。


    4水平分表


    水平分表也称为横向分表,比较容易理解,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。如下图所示:

    小结

    水平分表,能够降低单表的数据量,一定程度上可以缓解查询性能瓶颈。但本质上这些表还保存在同一个库中,所以库级别还是会有IO瓶颈。所以,一般不建议采用这种做法。


    5水平分库分表


    水平分库分表与上面讲到的水平分表的思想相同,唯一不同的就是将这些拆分出来的表保存在不同的数据中。这也是很多大型互联网公司所选择的做法。如下图:

    某种意义上来讲,有些系统中使用的“冷热数据分离”(将一些使用较少的历史数据迁移到其他的数据库中。而在业务功能上,通常默认只提供热点数据的查询),也是类似的实践。

    在高并发和海量数据的场景下,分库分表能够有效缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源的瓶颈。当然,投入的硬件成本也会更高。同时,这也会带来一些复杂的技术问题和挑战(例如:跨分片的复杂查询,跨分片事务等)


    6分库分表的难点


    垂直分库带来的问题和解决思路:

    跨库join的问题

    在拆分之前,系统中很多列表和详情页所需的数据是可以通过sql join来完成的。而拆分后,数据库可能是分布式在不同实例和不同的主机上,join将变得非常麻烦。而且基于架构规范,性能,安全性等方面考虑,一般是禁止跨库join的。那该怎么办呢?

    首先要考虑下垂直分库的设计问题,如果可以调整,那就优先调整。如果无法调整的情况,下面笔者将结合以往的实际经验,总结几种常见的解决思路,并分析其适用场景。

    跨库Join的几种解决思路

    全局表

    所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。

    字段冗余

    这是一种典型的反范式设计,在互联网行业中比较常见,通常是为了性能来避免join查询。

    举个电商业务中很简单的场景:

    “订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。

    字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场景也比较有限,比较适合依赖字段较少的情况。最复杂的还是数据一致性问题,这点很难保证,可以借助数据库中的触发器或者在业务代码层面去保证。

    当然,也需要结合实际业务场景来看一致性的要求。就像上面例子,如果卖家修改了Name之后,是否需要在订单信息中同步更新呢?

    数据同步

    定时A库中的tab_a表和B库中tbl_b有关联,可以定时将指定的表做同步。当然,同步本来会对数据库带来一定的影响,需要性能影响和数据时效性中取得一个平衡。这样来避免复杂的跨库查询。笔者曾经在项目中是通过ETL工具来实施的。

    系统层组装

    在系统层面,通过调用不同模块的组件或者服务,获取到数据并进行字段拼装。说起来很容易,但实践起来可真没有这么简单,尤其是数据库设计上存在问题但又无法轻易调整的时候。

    具体情况通常会比较复杂。下面笔者结合以往实际经验,并通过伪代码方式来描述。

    简单的列表查询的情况


    伪代码很容易理解,先获取“我的提问列表”数据,然后再根据列表中的UserId去循环调用依赖的用户服务获取到用户的RealName,拼装结果并返回。

    有经验的读者一眼就能看出上诉伪代码存在效率问题。循环调用服务,可能会有循环RPC,循环查询数据库…不推荐使用。再看看改进后的:

    这种实现方式,看起来要优雅一点,其实就是把循环调用改成一次调用。当然,用户服务的数据库查询中很可能是In查询,效率方面比上一种方式更高。(坊间流传In查询会全表扫描,存在性能问题,传闻不可全信。其实查询优化器都是基本成本估算的,经过测试,在In语句中条件字段有索引的时候,条件较少的情况是会走索引的。这里不细展开说明,感兴趣的朋友请自行测试)。

    小结

    简单字段组装的情况下,我们只需要先获取“主表”数据,然后再根据关联关系,调用其他模块的组件或服务来获取依赖的其他字段(如例中依赖的用户信息),最后将数据进行组装。

    通常,我们都会通过缓存来避免频繁RPC通信和数据库查询的开销。

    列表查询带条件过滤的情况

    在上述例子中,都是简单的字段组装,而不存在条件过滤。看拆分前的SQL:


    这种连接查询并且还带条件过滤的情况,想在代码层面组装数据其实是非常复杂的(尤其是左表和右表都带条件过滤的情况会更复杂),不能像之前例子中那样简单的进行组装了。试想一下,如果像上面那样简单的进行组装,造成的结果就是返回的数据不完整,不准确。 

    有如下几种解决思路:

    1. 查出所有的问答数据,然后调用用户服务进行拼装数据,再根据过滤字段state字段进行过滤,最后进行排序和分页并返回。

      这种方式能够保证数据的准确性和完整性,但是性能影响非常大,不建议使用。

    2. 查询出state字段符合/不符合的UserId,在查询问答数据的时候使用in/not in进行过滤,排序,分页等。过滤出有效的问答数据后,再调用用户服务获取数据进行组装。

      这种方式明显更优雅点。笔者之前在某个项目的特殊场景中就是采用过这种方式实现。

    跨库事务(分布式事务)的问题

    按业务拆分数据库之后,不可避免的就是“分布式事务”的问题。以往在代码中通过spring注解简单配置就能实现事务的,现在则需要花很大的成本去保证一致性。


    7垂直分库总结和实践建议


    本篇中主要描述了几种常见的拆分方式,并着重介绍了垂直分库带来的一些问题和解决思路。读者朋友可能还有些问题和疑惑。

    1. 我们目前的数据库是否需要进行垂直分库?

    根据系统架构和公司实际情况来,如果你们的系统还是个简单的单体应用,并且没有什么访问量和数据量,那就别着急折腾“垂直分库”了,否则没有任何收益,也很难有好结果。

    切记,“过度设计”和“过早优化”是很多架构师和技术人员常犯的毛病。 

    2. 垂直拆分有没有原则或者技巧?

    没有什么黄金法则和标准答案。一般是参考系统的业务模块拆分来进行数据库的拆分。比如“用户服务”,对应的可能就是“用户数据库”。但是也不一定严格一一对应。

    有些情况下,数据库拆分的粒度可能会比系统拆分的粒度更粗。笔者也确实见过有些系统中的某些表原本应该放A库中的,却放在了B库中。有些库和表原本是可以合并的,却单独保存着。还有些表,看起来放在A库中也OK,放在B库中也合理。

    如何设计和权衡,这个就看实际情况和架构师/开发人员的水平了。 

    3. 上面举例的都太简单了,我们的后台报表系统中join的表都有n个了, 
    分库后该怎么查?

    有很多朋友跟我提过类似的问题。其实互联网的业务系统中,本来就应该尽量避免join的,如果有多个join的,要么是设计不合理,要么是技术选型有误。请自行科普下OLAP和OLTP,报表类的系统在传统BI时代都是通过OLAP数据仓库去实现的(现在则更多是借助离线分析、流式计算等手段实现),而不该向上面描述的那样直接在业务库中执行大量join和统计。


    8送书环节


    介绍作者

    首先,我必须得介绍下作者,因为这个作者在IT领域,一直是一个非常努力的同学,虽然人生的起点很低,但是海到无边天作岸,山登绝顶我为峰!只要努力就能成功!这个作者就是 安晓辉!

    安晓辉,从最初的技术售后支持进入IT行业,到转型做开发宽带接入产品的软件工程师,再到研发部门经理,再到创业,创业失败,回归开发岗位,最后,脱离组织,成为自由职业者,出了好几本书!真的非常励志!这种从未放弃自己,一直认真的态度,努力的精神,值得大家学习!

    介绍书籍

    作为一名普通的程序员:

    • 你想买一套房子,不想再租住在远离公司的偏僻地带每天通勤 4 个小时上下班

    • 你想买一部车子,可以周末开着去山里转转,看看红叶听听鸟鸣

    • 你想买衣服时去窗明几净微笑服务的商场而不是每次都找一个不知名姓的小二网购经济适用款

    • 你想每年出去旅游 10 次 8 次,今天在苏梅岛潜水明天在魁北克吃枫糖

    • 你想每年给爸爸妈妈 5 万块的生活费,让他们露出欣慰的笑脸


    这些想法不能实现,会经常性地带给你痛苦。这种痛苦,会随着你工作时间的增长而加深,渐渐变成你生活的底色——你的底色原本简单明快,现在幽暗阴郁。


    实际上,这些所谓的痛苦,有 90% 都可以通过钱来解决。前提是:你的价值能够不断提升,赚钱速度超越需求膨胀。消除了这些痛苦,幸福感就有生长的环境,就不那么容易被淹没。


    可是作为普通的程序员,你却发现瓶颈一个接一个地扑过来。做技术,不知道怎么做到持续精进、怎么坚持;转管理,又不知如何开始。结果还没等想明白呢,半载一年就过去了,蓦然回首,好像自己的能力没怎么提高,薪水增速却越来越跑不过通货膨胀了。


    有时候你觉得开发工作越来越吃力,转型的呼声越来越高,却不知道如果离开开发岗位自己还能干什么。看着别人可以选择当自由职业者,或者能实现财务自由,内心羡慕,然而转过身却只能叹息:自己的路,究竟在哪里?


    仔细想想,你就会发现,要搞定这些事情和问题,只要能赚到更多的钱就可以了!


    这个结论很俗吗?


    不,现实正是如此!


    对于大部分开发者来讲,工作和生活的诸多烦恼,其实都源自于:怎么赚到更多的钱。


    要想赚到更多的钱,就要回到问题的原点,想想个人赚钱的本质是什么。


    个人赚钱的本质是——出售时间!对吗?


    从出售时间的角度来看:


    个人收入=每天可售时间数量×单位时间价格×单位时间出售次数


    在这个公式里,有三个要素,简单描述就是:


    • 每天可出售的时间数量

    • 单位时间价格

    • 同一份时间的出售次数


    结合开发者的具体情况,可以找到多种提升收入的方式。参考下表:




    或许你知道所有这些方式甚至知道更多,但是,怎么做到呢?


    这是个大问题!


    知道和做到之间有一道鸿沟,要想跨越它,你不但要努力,还要讲究方法。


    那么,到底要用什么样的思维、方法,才能找到适合你的提升收入的方式呢?安晓辉的新书

    《程序员的成长课》为您解惑!



    展开全文
  • 在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战...

    在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战。让人感到担忧的是,他们系统真的就需要“分库分表”了吗?“分库分表”有那么容易实践吗?为此,笔者整理了分库分表中可能遇到的一些问题,并结合以往经验介绍了对应的解决思路和建议。

    垂直分表

    垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中,如下图所示:

     

    小结

     

    在字段很多的情况下,拆分开确实更便于开发和维护(笔者曾见过某个遗留系统中,一个大表中包含100多列的)。某种意义上也能避免“跨页”的问题(MySQL、MSSQL底层都是通过“数据页”来存储的,“跨页”问题可能会造成额外的性能开销,这里不展开,感兴趣的朋友可以自行查阅相关资料进行研究)。

    拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。

    垂直分库

    垂直分库在“微服务”盛行的今天已经非常普及了。基本的思路就是按照业务模块来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。如下图:


    小结

    系统层面的“服务化”拆分操作,能够解决业务系统层面的耦合和性能瓶颈,有利于系统的扩展维护。而数据库层面的拆分,道理也是相通的。与服务的“治理”和“降级”机制类似,我们也能对不同业务类型的数据进行“分级”管理、维护、监控、扩展等。

    众所周知,数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈,是大型分布式系统中优化数据库架构的重要手段。

    然后,很多人并没有从根本上搞清楚为什么要拆分,也没有掌握拆分的原则和技巧,只是一味的模仿大厂的做法。导致拆分后遇到很多问题(例如:跨库join,分布式事务等)。

    水平分表

    水平分表也称为横向分表,比较容易理解,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。如下图所示:

     

    小结

    水平分表,能够降低单表的数据量,一定程度上可以缓解查询性能瓶颈。但本质上这些表还保存在同一个库中,所以库级别还是会有IO瓶颈。所以,一般不建议采用这种做法。

    水平分库分表

    水平分库分表与上面讲到的水平分表的思想相同,唯一不同的就是将这些拆分出来的表保存在不同的数据中。这也是很多大型互联网公司所选择的做法。如下图:

     

    某种意义上来讲,有些系统中使用的“冷热数据分离”(将一些使用较少的历史数据迁移到其他的数据库中。而在业务功能上,通常默认只提供热点数据的查询),也是类似的实践。在高并发和海量数据的场景下,分库分表能够有效缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源的瓶颈。当然,投入的硬件成本也会更高。同时,这也会带来一些复杂的技术问题和挑战(例如:跨分片的复杂查询,跨分片事务等)

    分库分表的难点

    垂直分库带来的问题和解决思路:

    跨库join的问题

    在拆分之前,系统中很多列表和详情页所需的数据是可以通过sql join来完成的。而拆分后,数据库可能是分布式在不同实例和不同的主机上,join将变得非常麻烦。而且基于架构规范,性能,安全性等方面考虑,一般是禁止跨库join的。那该怎么办呢?首先要考虑下垂直分库的设计问题,如果可以调整,那就优先调整。如果无法调整的情况,下面笔者将结合以往的实际经验,总结几种常见的解决思路,并分析其适用场景。

    跨库Join的几种解决思路

    全局表

    所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。

    字段冗余

    这是一种典型的反范式设计,在互联网行业中比较常见,通常是为了性能来避免join查询。

    举个电商业务中很简单的场景:

    “订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。

    字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场景也比较有限,比较适合依赖字段较少的情况。最复杂的还是数据一致性问题,这点很难保证,可以借助数据库中的触发器或者在业务代码层面去保证。当然,也需要结合实际业务场景来看一致性的要求。就像上面例子,如果卖家修改了Name之后,是否需要在订单信息中同步更新呢?

    数据同步

    定时A库中的tab_a表和B库中tbl_b有关联,可以定时将指定的表做同步。当然,同步本来会对数据库带来一定的影响,需要性能影响和数据时效性中取得一个平衡。这样来避免复杂的跨库查询。笔者曾经在项目中是通过ETL工具来实施的。

    系统层组装

    在系统层面,通过调用不同模块的组件或者服务,获取到数据并进行字段拼装。说起来很容易,但实践起来可真没有这么简单,尤其是数据库设计上存在问题但又无法轻易调整的时候。

    具体情况通常会比较复杂。下面笔者结合以往实际经验,并通过伪代码方式来描述。

    简单的列表查询的情况

     

    伪代码很容易理解,先获取“我的提问列表”数据,然后再根据列表中的UserId去循环调用依赖的用户服务获取到用户的RealName,拼装结果并返回。

    有经验的读者一眼就能看出上诉伪代码存在效率问题。循环调用服务,可能会有循环RPC,循环查询数据库…不推荐使用。再看看改进后的:

     

    这种实现方式,看起来要优雅一点,其实就是把循环调用改成一次调用。当然,用户服务的数据库查询中很可能是In查询,效率方面比上一种方式更高。(坊间流传In查询会全表扫描,存在性能问题,传闻不可全信。其实查询优化器都是基本成本估算的,经过测试,在In语句中条件字段有索引的时候,条件较少的情况是会走索引的。这里不细展开说明,感兴趣的朋友请自行测试)。

    小结

    简单字段组装的情况下,我们只需要先获取“主表”数据,然后再根据关联关系,调用其他模块的组件或服务来获取依赖的其他字段(如例中依赖的用户信息),最后将数据进行组装。

    通常,我们都会通过缓存来避免频繁RPC通信和数据库查询的开销。

    列表查询带条件过滤的情况

    在上述例子中,都是简单的字段组装,而不存在条件过滤。看拆分前的SQL:

     

    这种连接查询并且还带条件过滤的情况,想在代码层面组装数据其实是非常复杂的(尤其是左表和右表都带条件过滤的情况会更复杂),不能像之前例子中那样简单的进行组装了。试想一下,如果像上面那样简单的进行组装,造成的结果就是返回的数据不完整,不准确。 

    有如下几种解决思路:

    1. 查出所有的问答数据,然后调用用户服务进行拼装数据,再根据过滤字段state字段进行过滤,最后进行排序和分页并返回。

      这种方式能够保证数据的准确性和完整性,但是性能影响非常大,不建议使用。

    2. 查询出state字段符合/不符合的UserId,在查询问答数据的时候使用in/not in进行过滤,排序,分页等。过滤出有效的问答数据后,再调用用户服务获取数据进行组装。

      这种方式明显更优雅点。笔者之前在某个项目的特殊场景中就是采用过这种方式实现。

    跨库事务(分布式事务)的问题

    按业务拆分数据库之后,不可避免的就是“分布式事务”的问题。以往在代码中通过spring注解简单配置就能实现事务的,现在则需要花很大的成本去保证一致性。这里不展开介绍, 
    感兴趣的读者可以自行参考《分布式事务一致性解决方案》,链接地址: 
    http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency

    垂直分库总结和实践建议

    本篇中主要描述了几种常见的拆分方式,并着重介绍了垂直分库带来的一些问题和解决思路。读者朋友可能还有些问题和疑惑。

    1. 我们目前的数据库是否需要进行垂直分库?

    根据系统架构和公司实际情况来,如果你们的系统还是个简单的单体应用,并且没有什么访问量和数据量,那就别着急折腾“垂直分库”了,否则没有任何收益,也很难有好结果。

    切记,“过度设计”和“过早优化”是很多架构师和技术人员常犯的毛病。

    2. 垂直拆分有没有原则或者技巧?

    没有什么黄金法则和标准答案。一般是参考系统的业务模块拆分来进行数据库的拆分。比如“用户服务”,对应的可能就是“用户数据库”。但是也不一定严格一一对应。有些情况下,数据库拆分的粒度可能会比系统拆分的粒度更粗。笔者也确实见过有些系统中的某些表原本应该放A库中的,却放在了B库中。有些库和表原本是可以合并的,却单独保存着。还有些表,看起来放在A库中也OK,放在B库中也合理。

    如何设计和权衡,这个就看实际情况和架构师/开发人员的水平了。

    3. 上面举例的都太简单了,我们的后台报表系统中join的表都有n个了, 
    分库后该怎么查?

    有很多朋友跟我提过类似的问题。其实互联网的业务系统中,本来就应该尽量避免join的,如果有多个join的,要么是设计不合理,要么是技术选型有误。请自行科普下OLAP和OLTP,报表类的系统在传统BI时代都是通过OLAP数据仓库去实现的(现在则更多是借助离线分析、流式计算等手段实现),而不该向上面描述的那样直接在业务库中执行大量join和统计。

    由于篇幅关系,下篇中我们再继续细聊“水平分库分表”相关的话题。

    作者介绍

    丁浪,技术架构师。关注高并发、高可用的架构设计,对系统服务化、分库分表、性能调优等方面有深入研究和丰富实践经验。热衷于技术研究和分享。

    展开全文
  • 一、分库分表原则 二、分库分表方案 三、分库分表难点 四、常见分片规则和策略 五、跨分片技术问题 一、分库分表原则 关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量、连接数、处理能力等都很有限,...

    文章来源:http://www.ywnds.com/?p=7239

    目录

    一、分库分表原则

    二、分库分表方案

    三、分库分表难点

    四、常见分片规则和策略

    五、跨分片技术问题


    一、分库分表原则

    关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量、连接数、处理能力等都很有限,数据库本身的“有状态性”导致了它并不像Web和应用服务器那么容易扩展。在互联网行业海量数据和高并发访问的考验下,聪明的技术人员提出了分库分表技术(有些地方也称为Sharding、分片)。同时,流行的分布式系统中间件(例如MongoDB、ElasticSearch等)均自身友好支持Sharding,其原理和思想都是大同小异的。

    目前针对海量数据的优化,其分库分表是MySQL永远的话题,一般情况下认为MySQL是个简单的数据库,在数据量大到一定程度之后处理查询的效率降低,如果需要继续保持高性能运转的话,必须分库或者分表了。关于数据量达到多少大是个极限这个事儿,本文先不讨论,研究源码的同学已经证实MySQL或者Innodb内部的锁粒度太大的问题大大限制了MySQL提供QPS的能力或者处理大规模数据的能力。在这点上,一般的使用者只好坐等官方不断推出的优化版本了。

    在一般运维的角度来看,我们什么情况下需要考虑分库分表?

    首先说明,这里所说的分库分表是指把数据库数据的物理拆分到多个实例或者多台机器上去,而不是类似分区表的原地切分。

    原则零:能不分就不分

    是的,MySQL 是关系数据库,数据库表之间的关系从一定的角度上映射了业务逻辑。任何分库分表的行为都会在某种程度上提升业务逻辑的复杂度,数据库除了承载数据的存储和访问外,协助业务更好的实现需求和逻辑也是其重要工作之一。分库分表会带来数据的合并,查询或者更新条件的分离,事务的分离等等多种后果,业务实现的复杂程度往往会翻倍或者指数级上升。所以,在分库分表之前,不要为分而分,去做其他力所能及的事情吧,例如升级硬件,升级,升级网络,升级数据库版本,读写分离,负载均衡等等。所有分库分表的前提是,这些你已经尽力了。

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

    这里说的运维,例如:

    1)对数据库的备份。如果单表或者单个实例太大,在做备份的时候需要大量的磁盘IO或者网络IO资源。例如1T的数据,网络传输占用50MB的时候,需要20000秒才能传输完毕,在此整个过程中的维护风险都是高于平时的。我们在Qunar的做法是给所有的数据库机器添加第二块网卡,用来做备份,或者SST,Group Communication等等各种内部的数据传输。1T的数据的备份,也会占用大量的磁盘IO,如果是SSD还好,当然这里忽略某些厂商的产品在集中IO的时候会出一些BUG的问题。如果是普通的物理磁盘,则在不限流的情况下去执行xtrabackup,该实例基本不可用。

    2)对数据表的修改。如果某个表过大,对此表做DDL的时候,MySQL会锁住全表,这个时间可能很长,在这段时间业务不能访问此表,影响甚大。解决的办法有类似腾讯游戏DBA自己改造的可以在线秒改表,不过他们目前也只是能添加字段而已,对别的DDL还是无效;或者使用pt-online-schema-change,当然在使用过程中,它需要建立触发器和影子表,同时也需要很长很长的时间,在此操作过程中的所有时间,都可以看做是风险时间。把数据表切分,总量减小,有助于改善这种风险。

    3)整个表热点,数据访问和更新频繁,经常有锁等待,你又没有能力去修改源码,降低锁的粒度,那么只会把其中的数据物理拆开,用空间换时间,变相降低访问压力。

    原则二:表设计不合理,需要对某些字段垂直拆分

    这里举一个例子,如果你有一个用户表,在最初设计的时候可能是这样:

    id                   bigint             #用户的ID name               varchar           #用户的名字 last_login_time datetime       #最近登录时间 personal_info     text              #私人信息 xxxxx                 #其他信息字段

    1

    2

    3

    4

    5

    id                   bigint             #用户的ID

    name                 varchar            #用户的名字

    last_login_time      datetime           #最近登录时间

    personal_info        text               #私人信息

    xxxxx                                   #其他信息字段

    一般的users表会有很多字段,我就不列举了。如上所示,在一个简单的应用中,这种设计是很常见的。但是:

    设想情况一:你的业务中彩了,用户数从100w飙升到10个亿。你为了统计活跃用户,在每个人登录的时候都会记录一下他的最近登录时间。并且的用户活跃得很,不断的去更新这个login_time,搞的你的这个表不断的被update,压力非常大。那么,在这个时候,只要考虑对它进行拆分,站在业务的角度,最好的办法是先把last_login_time拆分出去,我们叫它 user_time。这样做,业务的代码只有在用到这个字段的时候修改一下就行了。如果你不这么做,直接把users表水平切分了,那么,所有访问users表的地方,都要修改。或许你会说,我有proxy,能够动态merge数据。到目前为止我还从没看到谁家的proxy不影响性能的。

    设想情况二:personal_info这个字段本来没啥用,你就是让用户注册的时候填一些个人爱好而已,基本不查询。一开始的时候有它没它无所谓。但是到后来发现两个问题,一,这个字段占用了大量的空间,因为是text嘛,有很多人喜欢长篇大论地介绍自己。更糟糕的是二,不知道哪天哪个产品经理心血来潮,说允许个人信息公开吧,以方便让大家更好的相互了解。那么在所有人猎奇窥私心理的影响下,对此字段的访问大幅度增加。数据库压力瞬间抗不住了,这个时候,只好考虑对这个表的垂直拆分了。

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

    例子很好举,各种的评论,消息,日志记录。这个增长不是跟人口成比例的,而是不可控的,例如微博的feed的广播,我发一条消息,会扩散给很多很多人。虽然主体可能只存一份,但不排除一些索引或者路由有这种存储需求。这个时候,增加存储,提升机器配置已经苍白无力了,水平切分是最佳实践。拆分的标准很多,按用户的,按时间的,按用途的,不在一一举例。

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

    这个很容易理解,鸡蛋不要放在一个篮子里,我不希望我的数据库出问题,但我希望在出问题的时候不要影响到100%的用户,这个影响的比例越少越好,那么,水平切分可以解决这个问题,把用户,库存,订单等等本来同统一的资源切分掉,每个小的数据库实例承担一小部分业务,这样整体的可用性就会提升。这对Qunar这样的业务还是比较合适的,人与人之间,某些库存与库存之间,关联不太大,可以做一些这样的切分。

    原则五:业务耦合性考虑

    这个跟上面有点类似,主要是站在业务的层面上,我们的火车票业务和烤羊腿业务是完全无关的业务,虽然每个业务的数据量可能不太大,放在一个MySQL实例中完全没问题,但是很可能烤羊腿业务的DBA 或者开发人员水平很差,动不动给你出一些幺蛾子,直接把数据库搞挂。这个时候,火车票业务的人员虽然技术很优秀,工作也很努力,照样被老板打屁股。解决的办法很简单:惹不起,躲得起。

    二、分库分表方案

    • 垂直拆分

    垂直拆分常见有垂直分库和垂直分表两种。垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中,如下图所示:

    从原则、方案、策略及难点阐述分库分表

    在字段很多的情况下,拆分开确实更便于开发和维护(笔者曾见过某个遗留系统中,一个大表中包含100多列的)。某种意义上也能避免“跨页”的问题(MySQL、MSSQL底层都是通过“数据页”来存储的,“跨页”问题可能会造成额外的性能开销,这里不展开,感兴趣的朋友可以自行查阅相关资料进行研究)。

    拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。

    垂直分库是根据数据库里面的数据表的相关性进行拆分,比如:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按常用字段和非常用字段进行拆分,每个表里面的数据记录数一般情况下是相同的,只是字段不一样,使用主键关联。

    另外,在“微服务”盛行的今天已经非常普及了,按照业务模块来划分出不同的数据库,也是一种垂直拆分。而不是像早期一样将所有的数据表都放到同一个数据库中。如下图:

    从原则、方案、策略及难点阐述分库分表

    垂直拆分优点:

    • 可以使得行数据变小,一个数据块 (Block) 就能存放更多的数据,在查询时就会减少 I/O 次数 (每次查询时读取的 Block 就少)。
    • 可以达到最大化利用 Cache 的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起。
    • 数据维护简单。

    垂直拆分缺点:

    • 主键出现冗余,需要管理冗余列。
    • 会引起表连接 JOIN 操作(增加 CPU 开销)可以通过在业务服务器上进行 join 来减少数据库压力。
    • 依然存在单表数据量过大的问题(需要水平拆分)。
    • 事务处理复杂。

    垂直拆分小结:

    系统层面的“服务化”拆分操作,能够解决业务系统层面的耦合和性能瓶颈,有利于系统的扩展维护。而数据库层面的拆分,道理也是相通的。与服务的“治理”和“降级”机制类似,我们也能对不同业务类型的数据进行“分级”管理、维护、监控、扩展等。

    众所周知,数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈,是大型分布式系统中优化数据库架构的重要手段。

    然后,很多人并没有从根本上搞清楚为什么要拆分,也没有掌握拆分的原则和技巧,只是一味的模仿大厂的做法。导致拆分后遇到很多问题(例如:跨库join,分布式事务等)。

    • 水平拆分

    水平拆分是通过某种策略将数据分片来存储,分为库内分表和分库分表两部分,每片数据会分散到不同的MySQL表或库,达到分布式的效果,能够支持非常大的数据量。

    库内分表,仅仅是单纯的解决了单一表数据过大的问题,由于没有把表的数据分布到不同的机器上,因此对于减轻 MySQL 服务器的压力来说,并没有太大的作用,大家还是竞争同一个物理机上的 IO、CPU、网络,这个就要通过分库分表来解决。

    最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。如下图所示:

    从原则、方案、策略及难点阐述分库分表

    当下分表有静态分表和动态分表两种:

    静态分表:事先估算出表能达到的量,然后根据每一个表需要存多少数据直接算出需要创建表的数量。如:1亿数据每一个表100W条数据那就要建100张表,然后通过一定的hash算法计算每一条数据存放在那张表。其实就有点像是使用partition table一样。静态分表有一个毙命就是当分的那么多表还不满足时,需要再扩展难度和成本就会很高。

    动态分表:同样也是对大数据量的表进行拆分,他可以避免静态分表带来的后遗症。当然也需要在设计上多一些东西(这往往是我们能接受的)。

    某种意义上来讲,有些系统中使用的“冷热数据分离”(将一些使用较少的历史数据迁移到其他的数据库中。而在业务功能上,通常默认只提供热点数据的查询),也是类似的实践。在高并发和海量数据的场景下,分库分表能够有效缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源的瓶颈。当然,投入的硬件成本也会更高。同时,这也会带来一些复杂的技术问题和挑战(例如:跨分片的复杂查询,跨分片事务等)。

    水平拆分优点:

    • 不存在单库大数据和高并发的性能瓶颈。
    • 应用端改造较少。
    • 提高了系统的稳定性和负载能力。

    水平拆分缺点:

    • 分片事务一致性难以解决。
    • 跨节点 Join 性能差,逻辑复杂。
    • 数据多次扩展难度跟维护量极大。

    三、分库分表难点

    垂直分库带来的问题和解决思路:

    • 跨库join的问题

    在拆分之前,系统中很多列表和详情页所需的数据是可以通过sql join来完成的。而拆分后,数据库可能是分布式在不同实例和不同的主机上,join将变得非常麻烦。而且基于架构规范,性能,安全性等方面考虑,一般是禁止跨库join的。那该怎么办呢?首先要考虑下垂直分库的设计问题,如果可以调整,那就优先调整。如果无法调整的情况,下面笔者将结合以往的实际经验,总结几种常见的解决思路,并分析其适用场景。

    跨库Join的几种解决思路

    全局表

    所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。

    字段冗余

    这是一种典型的反范式设计,在互联网行业中比较常见,通常是为了性能来避免join查询。

    举个电商业务中很简单的场景:“订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。

    字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场景也比较有限,比较适合依赖字段较少的情况。最复杂的还是数据一致性问题,这点很难保证,可以借助数据库中的触发器或者在业务代码层面去保证。当然,也需要结合实际业务场景来看一致性的要求。就像上面例子,如果卖家修改了Name之后,是否需要在订单信息中同步更新呢?

    数据同步

    定时A库中的tab_a表和B库中tbl_b有关联,可以定时将指定的表做同步。当然,同步本来会对数据库带来一定的影响,需要性能影响和数据时效性中取得一个平衡。这样来避免复杂的跨库查询。笔者曾经在项目中是通过ETL工具来实施的。

    系统层组装

    在系统层面,通过调用不同模块的组件或者服务,获取到数据并进行字段拼装。说起来很容易,但实践起来可真没有这么简单,尤其是数据库设计上存在问题但又无法轻易调整的时候。具体情况通常会比较复杂。

    • 跨库事务(分布式事务)问题

    按业务拆分数据库之后,不可避免的就是“分布式事务”的问题。想要了解分布式事务,就需要了解“XA接口”和“两阶段提交”。值得提到的是,MySQL5.5x和5.6x中的xa支持是存在问题的,会导致主从数据不一致。直到5.7x版本中才得到修复。Java应用程序可以采用Atomikos框架来实现XA事务(J2EE中JTA)。感兴趣的读者可以自行参考《分布式事务一致性解决方案》,链接地址:http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency

    四、常见分片规则和策略

    • 分布式全局唯一ID

    在很多中小项目中,我们往往直接使用数据库自增特性来生成主键ID,这样确实比较简单。而在分库分表的环境中,数据分布在不同的分片上,不能再借助数据库自增长特性直接生成,否则会造成不同分片上的数据表主键会重复。简单介绍下使用和了解过的几种ID生成算法。

    1. Twitter的Snowflake(又名“雪花算法”)

    2. UUID/GUID(一般应用程序和数据库均支持)

    3. MongoDB ObjectID(类似UUID的方式)

    4. Ticket Server(数据库生存方式,Flickr采用的就是这种方式)

    其中,Twitter的Snowflake算法是近几年在分布式系统项目中使用最多的,未发现重复或并发的问题。该算法生成的是64位唯一Id(由41位的timestamp+10位自定义的机器码+13位累加计数器组成)。这里不做过多介绍,感兴趣的读者可自行查阅相关资料。

    • 分片字段该如何选择

    在开始分片之前,我们首先要确定分片字段(也可称为“片键”)。很多常见的例子和场景中是采用ID或者时间字段进行拆分。这也并不绝对的,我的建议是结合实际业务,通过对系统中执行的sql语句进行统计分析,选择出需要分片的那个表中最频繁被使用,或者最重要的字段来作为分片字段。

    常见的分片策略有随机分片和连续分片这两种,如下图所示:

    从原则、方案、策略及难点阐述分库分表

    当需要使用分片字段进行范围查找时,连续分片可以快速定位分片进行高效查询,大多数情况下可以有效避免跨分片查询的问题。后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移。但是,连续分片也有可能存在数据热点的问题,就像图中按时间字段分片的例子,有些节点可能会被频繁查询压力较大,热数据节点就成为了整个集群的瓶颈。而有些节点可能存的是历史数据,很少需要被查询到。

    随机分片其实并不是随机的,也遵循一定规则。通常,我们会采用Hash取模的方式进行分片拆分,所以有些时候也被称为离散分片。随机分片的数据相对比较均匀,不容易出现热点和并发访问的瓶颈。但是,后期分片集群扩容起来需要迁移旧的数据。使用一致性Hash算法能够很大程度的避免这个问题,所以很多中间件的分片集群都会采用一致性Hash算法。离散分片也很容易面临跨分片查询的复杂问题。

    • 数据迁移,容量规划,扩容等问题

    很少有项目会在初期就开始考虑分片设计的,一般都是在业务高速发展面临性能和存储的瓶颈时才会提前准备。因此,不可避免的就需要考虑历史数据迁移的问题。一般做法就是通过程序先读出历史数据,然后按照指定的分片规则再将数据写入到各个分片节点中。

    此外,我们需要根据当前的数据量和QPS等进行容量规划,综合成本因素,推算出大概需要多少分片(一般建议单个分片上的单表数据量不要超过1000W)。

    如果是采用随机分片,则需要考虑后期的扩容问题,相对会比较麻烦。如果是采用的范围分片,只需要添加节点就可以自动扩容。

    五、跨分片技术问题

    • 跨分片的排序分页

    一般来讲,分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。如下图所示:

    从原则、方案、策略及难点阐述分库分表

    上面图中所描述的只是最简单的一种情况(取第一页数据),看起来对性能的影响并不大。但是,如果想取出第10页数据,情况又将变得复杂很多,如下图所示:

    从原则、方案、策略及难点阐述分库分表

    有些读者可能并不太理解,为什么不能像获取第一页数据那样简单处理(排序取出前10条再合并、排序)。其实并不难理解,因为各分片节点中的数据可能是随机的,为了排序的准确性,必须把所有分片节点的前N页数据都排序好后做合并,最后再进行整体的排序。很显然,这样的操作是比较消耗资源的,用户越往后翻页,系统性能将会越差。

    • 跨分片的函数处理

    在使用Max、Min、Sum、Count之类的函数进行统计和计算的时候,需要先在每个分片数据源上执行相应的函数处理,然后再将各个结果集进行二次处理,最终再将处理结果返回。如下图所示:

    从原则、方案、策略及难点阐述分库分表

    <摘之>

    周彦伟:http://mp.weixin.qq.com/s/BI2P45pnzUceCSQWU961zA

    丁浪:http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table

    展开全文
  • 分库分表

    2020-03-18 12:16:05
    1、分库分表的原因 随着业务快速发展,数据量越来越大,查询所需要的时间也越来越多,访问变慢,关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后...

    数据库瓶颈        

    IO瓶颈

    第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 ->  分库和垂直分表

    第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 ->  分库

    CPU瓶颈

    第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。

    第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 ->  水平分表

    1、分库分表的原因

    随着业务快速发展,数据量越来越大,查询所需要的时间也越来越多,访问变慢,关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索引,做很多操作时性能仍下降严重。

    分库的原因:QPS过高,数据库响应速度来不及,一般mysql单机也就1000左右的QPS,如果超过1000,就要考虑分库。

    分表的原因:单表太大,复杂SQL的查询速度变慢,一般mysql单表也就1000万左右的量,如果超过1000万,就要考虑分表

    单库发生意外的时候,需要修复的是所有的数据,而多库中的一个库发生意外的时候,只需要修复一个库(当然,也可以用物理分区的方式处理这种问题)

    分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。

    2、分库分表的常用策略

    分库分表包括分库和分表两个部分,在生产中通常包括:垂直分库、水平分库、垂直分表、水平分表四种方式。

    垂直分库:以 为依据,按照业务归属不同,将不同的 拆分到不同的 中 。在高并发场景下,垂直分库一定程度上能够突破 IO、连接数及单机硬件资源的瓶颈,是大型分布式系统中优化数据库架构的重要手段

    垂直分表:以 字段为依据,按照字段的活跃性,将 中字段拆到不同的 (主表和扩展表)中

    水平分库:以 字段为依据 ,按照一定策略(hash、range等),将一个 中的数据拆分到多个 

    水平分表:将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行 Hash 和取模后拆分。水平分表,能够降低单表的数据量,一定程度上可以缓解查询性能瓶颈。但本质上这些表还保存在同一个库中,所以库级别还是会有 IO 瓶颈。

    垂直分表带来的数据冗余,以及查询次数的增加

    水平分表表多大才需要考虑分表,确定分表的阈值

    垂直切分优缺点

    优点

    - 解决业务系统层面的耦合,业务清晰
    - 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
    - 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈

    缺点

    - 分库后无法Join,只能通过接口聚合方式解决,提升了开发的复杂度
    - 分库后分布式事务处理复杂
    - 依然存在单表数据量过大的问题(需要水平切分)

    水平切分优缺点

    优点

    - 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
    - 应用端改造较小,不需要拆分业务模块

    缺点

    - 跨分片的事务一致性难以保证
    - 跨库的Join关联查询性能较差
    - 数据多次扩展难度和维护量极大

     

    3、常用的分库分表中间件、工具

    简单易用的组建 

    强悍重量级的中间件

    sharding-jdbc和mycat,这两个都可以去考虑使用。通常来说,这两个方案其实都可以选用,但是我个人建议中小型公司选用sharding-jdbc,client层方案轻便,而且维护成本低,不需要额外增派人手,而且中小型公司系统复杂度会低一些,项目也没那么多;但是中大型公司最好还是选用mycat这类proxy层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护mycat,然后大量项目直接透明使用即可。

    4、分库分表步骤

    根据容量(当前容量和增长量)评估分库或分表个数 -> 选key(均匀)-> 分表规则(hash或range等)-> 执行(一般双写)-> 扩容问题(尽量减少数据的移动)。

    5、分库分表需要解决的问题:复杂查询,分布式事务

    • 扩容与迁移

    升级从库法

    双写迁移法

    第一步:(同步双写)应用配置双写,部署;

    第二步:(同步双写)将老库中的老数据复制到新库中;

    第三步:(同步双写)以老库为准校对新库中的老数据;

    第四步:(同步双写)应用去掉双写,部署;

    注:双写是通用方案。

    • 分库分表维度导致的查询问题:join操作,COUNT(*)操作,order by 操作,分页

    只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

    Join是关系型数据库中最常用的特性,但是在分片集群中,join也变得非常复杂。应该尽量避免跨分片的join查询(这种场景,比上面的跨分片分页更加复杂,而且对性能的影响很大)跨库join查询的解决办法:全局表、字段冗余、数据同步到一个表上、系统层组装、在系统层面,通过调用不同模块的组件或者服务,获取到数据并进行字段拼装

    在使用Max、Min、Sum、Count之类的函数进行统计和计算的时候,需要先在每个分片数据源上执行相应的函数处理,然后再将各个结果集进行二次处理,最终再将处理结果返回。但如果结果集很大,对应用程序内存的消耗是一个问题。

    • 跨库事务难以实现 

    不说“分布式事务”理论,直接上大厂阿里的解决方案,绝对实用

    解决事务问题目前有两种可行的方案:分布式事务和通过应用程序与数据库共同控制实现事务下面对两套方案进行一个简单的对比。

    • 方案一:使用分布式事务

    优点:实现简单,工作量小。由于多数应用服务器以及一些独立的分布式事务协调器做了大量的封装工作,使得项目中引入分布式事务的难度和工作量基本上可以忽略不计。基于两阶段提交,最大限度地保证了跨数据库操作的“原子性”,是分布式系统下最严格的事务实现方式。

    缺点:系统“水平”伸缩的死敌。基于两阶段提交的分布式事务在提交事务时需要在多个节点之间进行协调,最大限度地推后了提交事务的时间点,客观上延长了事务的执行时间,这会导致事务在访问共享资源时发生冲突和死锁的概率增高,随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平伸缩的"枷锁", 这是很多Sharding系统不采用分布式事务的主要原因。

    • 方案二:由应用程序和数据库共同控制

    原理:将一个跨多个数据库的分布式事务分拆成多个仅处 于单个数据库上面的小事务,并通过应用程序来总控 各个小事务。

    优点:性能上有优势

    缺点:需要应用程序在事务控制上做灵活设计。如果使用 了spring的事务管理,改动起来会面临一定的困难。

    基于Best Efforts 1PC模式的事务

    事务补偿(幂等值)

    对于那些对性能要求很高,但对一致性要求并不高的系统,往往并不苛求系统的实时一致性,只要在一个允许的时间周期内达到最终一致性即可,这使得事务补偿机制成为一种可行的方案。事务补偿机制最初被提出是在“长事务”的处理中,但是对于分布式系统确保一致性也有很好的参考意义。笼统地讲,与事务在执行中发生错误后立即回滚的方式不同,事务补偿是一种事后检查并补救的措施,它只期望在一个容许时间周期内得到最终一致的结果就可以了。事务补偿的实现与系统业务紧密相关,并没有一种标准的处理方式。一些常见的实现方式有:对数据进行对帐检查;基于日志进行比对;定期同标准数据来源进行同步,等等。

    6、路由规则

    • 范围路由:按照范围划分,将某张表的创建时间按照日期划分存为月表。也可以将某张表的主键按照范围划分,比如 【1~10000】在一张表,【10001~20000】在一张表。好处是自带水平扩展,不需要过多干预。缺点是可能会出现数据不均匀的情况(比如某个月请求暴增)。

    就是每个库一段连续的数据,一般按比如时间范围来的,但是这种一般较少用,因为很容易产生热点问题,大量的流量都打在最新的数据上了

    好处:后面扩容的时候,就很容易,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了

    缺点:但是大部分的请求,都是访问最新的数据。实际生产用range,要看场景,你的用户不是仅仅访问最新的数据,而是均匀的访问现在的数据以及历史的数据

    • hash算法:一下均匀分散,较为常用。

    好处:可以平均分配没给库的数据量和请求压力

    坏处:扩容起来比较麻烦,会有一个数据迁移的过程

    • 路由配置

    配置路由就是路由表,用一张独立的表来记录路由信息。同样以用户ID为例,我们新增一张ROUTER表,这个表包含table_Id两列,根据user_id就可以查询对应的修改路由表就可以了。
    配置路由设计简单,使用起来非常灵活,尤其是在扩充表的时候,只需要迁移指定的数据,然后修改路由表就可以了。
    其缺点就是必须多查询一次,会影响整体性能,而且路由表本身如果太大,性能会成为瓶颈点,如果我们再将路由表分库分表,则又面临一个死循环。

    7、分布式全局唯一ID

    分布式ID需要满足那些条件?

    • 全局唯一:必须保证ID是全局性唯一的,基本要求

    • 高性能:高可用低延时,ID生成响应要块,否则反倒会成为业务瓶颈

    • 高可用:100%的可用性是骗人的,但是也要无限接近于100%的可用性

    • 好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单

    • 趋势递增:最好趋势递增,这个要求就得看具体业务场景了,一般不严格要求

    分布式ID都有哪些生成方式?

    今天主要分析一下以下9种

    • UUID

    • 数据库自增ID

    • 数据库多主模式

    • 号段模式

    • Redis

    • 雪花算法(SnowFlake)

    • 滴滴出品(TinyID)

    • 百度 (Uidgenerator)

    • 美团(Leaf)

     

    参考:

    一口气说出 9种 分布式ID生成方式,面试官有点懵了

    分库分表的几种常见形式以及可能遇到的难

    突破Java面试(46)-分库分表

    分库分表的5大方案,百度、腾讯、阿里等大厂都在用!

    MySQL数据库之分库分表方案

    分库分表

    分库分表(1) --- 理论

    彻底搞清分库分表(垂直分库,垂直分表,水平分库,水平分表)

    面试官:“谈谈分库分表吧?

    分库分表

    分库分表需要考虑的问题及方案

    水平分库分表的关键问题及解决思路

    展开全文
  • 分库分表参照

    2016-03-28 10:16:42
    分库分表中间件参照: http://www.infoq.com/cn/news/2016/01/sharding-jdbc-dangdang 一号店分库分表参照: ...
  • 分库分表的几种常见形式公司业务的发展过程中,提高系统的处理承载能力,在数据库端通常都会选择分库分表。今天对数据库的分库分表进行了一次学习与总结。1、垂直分表垂直分表在日常开发和设计中比较常见,通俗的...
  • 在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战...
  • 在之前的文章中,我介绍了分库分表的几种表现形式和玩法,也重点介绍了垂直分库所带来的问题和解决方法。本篇中,我们将继续聊聊水平分库分表的一些技巧。 分片技术的由来 关系型数据库本身比较容易成为系统...
  • 很多人张嘴就是分库分表,好像不分库分表就不算牛逼的系统,不谈分库分表就没有逼格一样。 个人觉得,在设备配置别太挫、业务拆分别太渣、sql写法别太low、单表数据不上亿的情况下,靠读写分离、索引优化、表分区等...
  • 数据库为什么要分库分表

    万次阅读 2017-08-16 16:40:27
    1 基本思想之什么是分库分表? 从字面上简单理解,就是把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上。 2 基本思想之为什么要分库分表? 数据库中的数据量不一定是可控...
  • 在之前的文章中,我介绍了分库分表的几种表现形式和玩法,也重点介绍了垂直分库所带来的问题和解决方法。本篇中,我们将继续聊聊水平分库分表的一些技巧。 分片技术的由来 关系型数据库本身比较容易成为系统性能...
  • 数据库分库分表

    2016-04-07 18:03:00
    主从读写分离、分库分表 1、垂直切分 2、水平切分 解决方案有: mysql-agent, tddl, Amoeba, cobar, cobar client, mycat, sharding-jdbc 数据库 分区 partition 参考资料:  1、 数据库水平...
  • 分库分表的几种常见形式

    千次阅读 2018-02-10 09:56:11
    在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战...
  • 我介绍了分库分表的几种表现形式和玩法,也重点介绍了垂直分库所带来的问题和解决方法。本篇中,我们将继续聊聊水平分库分表的一些技巧。 分片技术的由来 关系型数据库本身比较容易成为系统性能瓶颈,单机存储...
  • 1. 唯品会订单分库分表的实践总结以及关键步骤http://www.infoq.com/cn/articles/summary-and-key-steps-of-vip-orders-depots-table 2. 又拍网架构中的分库设计 ...
  • 原址:作者:丁浪;...amp;utm_campaign=user_page&amp;utm_medium=link经常在面试的时候碰到一个问题,分库分表的时候如何保证全局分布式ID唯一性。在聊...
  • 当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从、优化索引,做很多操作时性能仍下降严重。此时就要考虑对其进行切分了,切分的目的就在于减少数据库的负担,缩短查询时间。 1、垂直分表 垂直...
  • 分库分表的几种常见形式以及可能遇到的难 在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在...
  • sharding-jdbc 分库分表

    2019-10-10 16:12:43
    1. 使用基础: 使用Sharding-JDBC 分库分表:https://www.cnblogs.com/coderzhw/p/11094305.html(文末有参考连接文章也都不错) 2.学习sharding-jdbc 之spring+mybatis+sharding-jdbc整合 ...
  • 在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(或者即将面临)高速增长,技术方面也面临着一些挑战...
  • 分库分表架构实践(文末送书)

    千次阅读 2017-12-18 00:00:00
    来源:infoQ||聊聊架构1题记“分库分表”是谈论数据库架构和优化时经常听到的关键词。那么对于这些业务量正在高速增长的公司,它有那么容易实践吗?在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,236
精华内容 494
关键字:

infoq分库分表