前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >大型分布式业务平台数据库优化方法(下)

大型分布式业务平台数据库优化方法(下)

作者头像
用户2991389
发布于 2018-09-05 04:32:01
发布于 2018-09-05 04:32:01
1.1K0
举报

微信版的链接地址

文章摘要:当单表数据达到千万以上时,通过加索引或者表分区优化提升的效果就比较有限了,应该如何应对呢???

当MySQL数据库的单表数据量达到千万级别以上时,不管是业务逻辑的查询,还是更新,或者删除都会使得数据库的平均响应时间过长。这时再通过(上)篇中的单表SQL优化技术解决方案收效就微乎其微了。对于如此大量数据,我们还可以利用以下几种业务平台的架构方案进行进一步的技术改造。

一、分离热点数据方案

当单库数据量比较大影响了查询/更新/删除的SQL执行效率时,我们可以直接想到在不影响业务逻辑的前提下,如果可以直接减少数据库中单表的数据量,那就能够达到我们的优化数据库的目标。该种方案称之为“分离热点数据”。

基于最近时间段内写入数据一般会成为热点数据的假设,同时考虑到在业务平台中对于存量历史数据的需求基本都是查询,而对于平台中的最近时段生成的数据一般会涉及查询/更新/删除等操作。因此可以设定一个时间的阀值,比如6个月,根据这个时间点来进行分表。对于6个月以内的数据,存热点库的数据表,6个月以上的数据存在历史库数据表里,业务平台的应用工程通过配置多数据源以及增加代理层即可实现服务对于不同数据库的访问,用以区分对热点数据库和历史库的访问。由于历史存量数据会越来越多,因此可以根据业务需要对历史库内的数据进行实现MYSQL分区表。而业务平台迁移历史数据可以在业务平台访问压力和流量较小的午夜通过设置Quartz定时任务程序的方式执行迁移。该种业务平台架构方案图如下:

二、采用本地或分布式缓存方案

为了缓解数据库单库单表的IO压力,尽可能地降低数据库操作(CRUD)平均响应时间。我们可以采用本地缓存或者分布式缓存的技术方案为DB缓解读写压力。

这里的本地缓存主要指通过利用JDK原有的诸如HashMap/ConcruuentHashMap数据结构或者Google guava包中的localCache在应用服务器内部构建的一块缓存区域。分布式缓存即指redis、mencached这一类的缓存中间件(限于篇幅和主题,对于这两类缓存深度技术和应用优化的介绍将在后续的篇幅中会单独介绍)。

对于业务平台中的局数据(比如,资源规格、类型、价格、模板等),该类数据从写入至数据表后就极少改动,其业务需求通常是只读。考虑到该特点,可以将这一部分数据在应用服务启动时就放入本地/分布式缓存中,服务需要读取时直接从缓存中读取,这样即可在一定程度上减少对数据库读的压力。

而对于另外一部分的业务数据(比如订单、资源、性能、用户账户),往往涉及新增/修改/删除等对数据库写的操作,且在一些业务场景中(比如大促/营销活动)这部分数据经常容易成为热点数据。因此可以考虑在数据库并发量较大的情况下,用分布式缓存做为缓冲,在缓存中先完成数据的汇总以及其他业务逻辑操作,然后使用批量插入/更新/删除的方式对数据库完成一次或若干次的批处理操作。这样可以在保证平台业务不受影响的同时,减轻数据库的写压力。下图即为热点数据缓冲汇总统计的示意图:

三、分库分表

1.分库分表的必要性

当数据库中一张单表的数据量达到几千万时,且还有不断增长趋势,同时系统的并发访问量达到一定规模的时,前面篇幅中介绍的数据表的B-Tree/B+ Tree索引就无法起作用了。除非是索引覆盖查询,否则数据库服务器需要根据索引扫描的结果回表,查询所有符合条件的记录。如果数据量巨大,这将产生大量随机I/O,随之,数据库的平均响应时间将大到不可接受的程度。另外,索引维护(磁盘空间、I/O操作)的代价也非常高。

为了解决单台数据库服务器的性能问题,提高系统的吞吐量时,就需要根据业务逻辑把表拆分成若干个,分别放在不同的数据库服务器中以降低单台DB的负载和缓解单库IO的读写压力,降低访问数据库的平均响应时间。

2.拆分方式

(1)垂直拆分

这里主要是指按业务平台的不同类型功能模块进行拆分,比如分为订单库、资源库、用户库。拆分之后,每个业务平台中的应用工程只访问对应的业务数据库,一方面将单点数据库拆分成了多个,分摊了单库的读写IO压力;另一方面,拆分后的数据库各自独立,实现了业务隔离,不再互相影响。下图为“垂直拆分”方式的结构示意图:

(2)水平拆分

按业务垂直切分后,可能有些单表数据还是很大,访问性能低下,这时需要对这个单库单表上的数据进行水平切分。按照一定的分片算法(比如按照主键ID的Hash)将同一个表的数据进行切分并分别保存到不同数据库的数据表中,且这些数据库中的表结构完全相同。以下为“水平拆分”示意图:

在SOA/微服务架构普遍流行的今天,从某种意义上来说,业务平台的建设基本都是按照“垂直拆分”的思路来的,即不同业务子系统访问对应不同的业务数据库。但是,“垂直拆分”的方案并没有根本解决当某一业务微服务的数据量剧增后对单库带来的读写IO压力。因此同一个业务平台中,比如资源/性能数据库,在“垂直拆分”后,我们一般会考虑“水平拆分”来应对单库单表不断增加的场景。

在这里讲到的“水平拆分”其实跟上篇中提到的MySQL分区表有些类似,只是不同之处在于分区表是在单库的情况下,通过MYSQL存储引擎来实现水平拆分,平台系统本身的业务逻辑不需要感知这一改变。

“水平拆分”优点:

a、经过拆分后,不存在单库数据量大和高并发的性能瓶颈;

b、提高了系统的稳定性和负载能力;

c、缓解单库IO压力;

“水平拆分”缺点:

a、分库事务的一致性难以解决;

b、跨库Join表性能问题,逻辑复杂;

c、跨库count/order by/group by以及聚合函数问题;

d、切分策略如何选择,策略问题很可能导致数据分布不均匀问题;

e、全局主键问题;

(3)应对水平拆分问题的方案

a、跨库事务问题:

解决跨库事务问题主要可以通过两种方法。第一种方法,使用前面篇幅介绍的分布式事务,简单有效但是性能代价高。第二种方式,应用程序和数据库共同控制保证,将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,并通过应用程序来总控各个小事务。该方案优点在于能够灵活控制,缺点在于改造量较大。

b、跨库Join表的问题

对于业务平台的数据持久层来说,涉及复杂的Join多表查询在所难免。解决这一问题的普遍做法是分两次查询解决。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

c、跨节点的count,order by,group by以及聚合函数问题

与解决跨节点join问题的类似,只需要分别在各个单库上得到结果后在业务应用端进行合并。和join不同的是每个结点的查询可以采用多线程方式并行执行(在jdk8中可以用CompletableFuture解决),因此很多时候它的合并速度要比单个大数据量的表快很多。

d、数据分片策略选择

水平拆分中一个比较重要的问题就是按照什么逻辑策略来进行数据分片(即为拆分库表)。一种方案是按照地域类的属性进行拆分;另外一种方案则是按照订单ID/用户ID进行拆分。按照地域类的属性进行拆分会使得数据聚合度比较高,做聚合查询比较简单,实现也相对简单,缺点是数据分布不均匀。按订单ID拆分则正相反,优点是数据分布均匀,不会出现一个数据库数据极大或极小的情况,缺点是数据太分散,不利于做聚合查询。比如,按订单ID拆分后,一个客户的订单可能分布在不同的数据库中,查询一个客户下面的所有订单,可能需要查询多个数据库。对于这种情况,一种解决方案是将需要聚合查询的数据做冗余表,冗余的表不做拆分,同时在业务开发过程中,减少聚合查询。

e、全局主键问题

原本依赖数据库生成主键(比如自增)的表在拆分后需要自己实现主键的生成,因为一般拆分规则是建立在主键上的,所以在插入新数据时需要确定主键后才能找到存储的表。在实际应用中,可以参考flickr的全局主键生成方案和uuid的全局主键生成方案。

3.拆分库表解决方案

那么问题来了:采用分库分表技术方案的话,用开源方案还是自研?

一般在业务平台建设前期综合评估系统容量、性能、吞吐量等因素后,会考虑采用分库分表的解决方案,那么就会遇到以上的问题。如果自主研发的话,时间周期长、成本高、项目风险大,因此都会考虑采用目前业界比较成熟的开源分库分表方案。下面主要阐述几种开源的分库分表解决方案:

(1)Sharding-JDBC

Sharding-JDBC是当当网开源的分布式数据库中间件,其代表了客户端类的分库分表框架。它定位为轻量级java框架,由客户端直连数据库,以jar包形式提供服务,未使用中间层,无需额外部署,无其他依赖,数据库运维人员无需改变原有的运维方式,即为增强版的JDBC驱动,旧代码迁移成本几乎为零。同时,Sharding-JDBC完整的实现了分库分表,读写分离和分布式主键功能,并初步实现了柔性事务。下面是Sharding-JDBC的框架图:

从上面的框架图中可以看出Sharding-JDBC这一款分库分表的中间件分为分片规则配置、SQL解析、SQL改写、SQL路由、SQL执行以及结果归并等模块。

a.分片规则配置

Sharding-JDBC的分片逻辑非常灵活,支持分片策略自定义、复数分片键、多运算符分片等功能。举个例子:根据用户ID分库/订单ID分表这种分库分表结合的分片策略;或根据年分库,月份+用户区域ID分表这样的多片键分片。Sharding-JDBC除了支持等号运算符进行分片,还支持in/between运算符分片,提供了更加强大的分片功能。Sharding-JDBC提供了spring命名空间用于简化配置,以及规则引擎用于简化策略编写。

b.JDBC规范重写

Sharding-JDBC中间件对标准JDBC规范的重写思路是针对DataSource、Connection、Statement、PreparedStatement和ResultSet五个核心接口封装,将多个JDBC实现类集合(如:MySQL JDBC实现/DBCP JDBC实现等)纳入Sharding-JDBC实现类管理。

c.SQL解析

SQL解析作为分库分表类中间件框架的核心之一,其性能和兼容性是最重要的衡量指标。目前常见的SQL解析器主要有fdb/jsqlparser和Druid。Sharding-JDBC采用解析速度最快的Druid作为SQL解析器。

d.SQL改写

这里一部分是将分表的逻辑表名称替换为真实表名称。另一部分是根据SQL解析结果替换一些在分片环境中不正确的功能。

e. SQL路由

SQL路由是指根据分片规则配置,将待执行的SQL定位至真正的DB数据源。

f. SQL执行

这里指的是路由至真实的DB数据源后,Sharding-JDBC将采用多线程并发执行SQL,并完成对addBatch等批量方法的处理。

g.结果归并

Sharding-JDBC支持通遍历类、排序类、聚合类和分组类四种结果并归方式。普通遍历类最为简单,只需按顺序遍历ResultSet的集合即可。排序类结果则将结果先排序再输出,因为各分库的分片结果均按照各自条件完成排序,所以采用归并排序算法整合最终结果。聚合类分为3种类型,比较型、累加型和平均值型。分组类相对最为复杂,需要将所有的ResultSet结果放入内存,使用Map-Reduce算法分组,最后根据排序和聚合条件做相关处理。最为消耗内存,损失性能的地方就是这里了,可以考虑使用limit合理的限制分组数据大小。

鉴于以上讲的几个Sharding-JDBC中间件框架特点,该方案的优点在于,业务平台中的服务应用可以直接连数据库,降低外围系统依赖所带来的不稳定风险,系统集成难度较低,无需额外运维组件。

其框架的缺点是,限制服务应用只能在业务逻辑层中进行定制化的开发和优化,扩展性一般。对于,比较复杂的系统可能会力不从心,将分片逻辑的压力放在应用服务器上,造成额外风险。(由于本文篇幅所限,对于Sharding-JDBC中间件开源代码的深度分析将在后续篇幅中会讲到)

(2)MyCat

MyCat是一个开源的分布式数据库系统(属于代理类框架),是一个实现MySQL协议的服务器,用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。其框架如下:

MyCat分库分表方案的优点在于,能够处理非常复杂的需求,不受数据库访问层原来实现的限制,扩展性强且对于应用服务透明且没有增加任何额外负载。其缺点是上线部署需要额外的中间件,增加运维成本;应用服务需经过代理来连接数据库,网络上多了一层链接的开销,性能有损失且有额外风险。

本文从几个不同的应用开发视角,分别阐述了作者自己工作中用到过的业务平台数据库架构优化方案,包括分离热点数据、本地/分布式缓存、分库分表的三种技术架构。限于作者的才疏学浅,可能对几种方案的理解不够深入,如有阐述不合理之处还望留言一起探讨。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.10.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
设计模式——解释器模式
设计模式——解释器模式
Java架构师必看
2021/05/14
4140
设计模式——解释器模式
图解Java设计模式之解释器模式
通过解释器模式来实现四则运算,如计算 a + b + c 的值,具体要求 1)先输入表达式的形式,比如 a + b + c + d + e,要求表达式的字母不能重复 2)在分别输入a,b,c,d,e的值 3)最后求出结果
海仔
2020/04/08
1K0
图解Java设计模式之解释器模式
设计模式-解释器模式
具体做法是创建几个解释器,在创建一个解释器封装类,在解释器封装类中完成语法树的构建。然后在场景类中完成递归调用。
mySoul
2019/01/25
3780
解释器模式
@派大星
2023/06/28
1590
解释器模式
解释器模式-破解算术验证码
我头两年工作的时候,写过一些爬虫程序,爬取过京东的商品数据,今日影视的视频资源等等。有些资源是很容易爬的,只要发一个HTTP请求,无需任何处理服务端就会返回给你数据。但是对于一些比较珍贵的数据,服务端就会做「反爬虫」处理,我曾经在爬取第三方网站的文章时就遇到过,幸运的是人家的反爬虫机制比较简单:给出一个图片,图片里面是一个「算术题」,你必须输入算术题的正确答案,服务端才会响应文章的完整内容。算术题都是很简单的四则运算,小学生都会的那种,因此很容易破解。
全栈程序员站长
2022/09/05
7560
解释器模式-破解算术验证码
设计模式实战 - 解释器模式(Interpreter Pattern)
● 公式可以运行时编辑,并且符合正常算术书写方式,例如a+b-c ● 高扩展性,未来增加指数、开方、极限、求导等运算符号时较少改动 ● 效率可以不用考虑,晚间批量运算
JavaEdge
2018/12/17
9520
设计模式实战 - 解释器模式(Interpreter Pattern)
设计之禅——解释器模式(译文)
解释器模式在平时基本上用不到,因此笔者也不打算花太多精力在这上面,但强迫症使然,所以翻译了GeeksForGeeks上面的一篇文章,本文采取意译及注解方式,原文链接Interpreter Pattern
夜勿语
2020/09/07
3050
解释器模式 Interpreter 行为型 设计模式(十九)
如果形势变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无穷种组合方式
noteless
2018/12/26
5630
设计模式----解释器模式
解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的 “语言” 是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
大忽悠爱学习
2021/11/15
9680
设计模式实战-解释器模式,今天给你解疑答惑
解释器模式,这个模式我觉得是这些模式中最不好理解的模式,解释器模式是用来干啥的呢?比如说我们有一段英文或者一段公式,我们需要知道其中表达的意思到底是啥?(假如我们起初并不理解)也就是说,我们需要一个"解释人",该角色就是我们的联络官或者叫做解释器,用来翻译我们的文本或者公式,翻译成我们能理解的最小的基础单元,听着是不是还云里雾里地?大家都知道编译器吧,一般的编译器分为词法分析器、语法分析器、语义分析器、中间代码优化器以及最终的代码生成器等,而我的理解,解释器就类似于其中的语法分析器的作用,专门负责语法文本的解析作用。
架构师修炼
2020/07/17
3000
设计模式21之解释器模式
在软件开发中,可能会出现某些相似的功能多次出现,这些功能有一定的相似性与规律性。这是我们就可以将其归纳成一种简单的语言。这就是解释器模式的来源。
Lvshen
2022/05/05
2280
设计模式21之解释器模式
【地铁上的设计模式】--行为型模式:解释器模式
解释器(Interpreter)是一种行为型设计模式,它用于解释一种特定的编程语言或表达式。它提供了一种解释一组语言语法的方法,使得用户可以按照特定的规则定义自己的语言,并通过解释器将其转化成可执行代码。 在解释器模式中,包含两个角色:终结符和非终结符。终结符表示语法规则中的基本单元,而非终结符表示由终结符组成的语法规则。解释器模式通常使用抽象语法树(Abstract Syntax Tree, AST)来实现对语法规则的解释。 解释器模式的优点在于它可以轻松地添加新的语法规则,同时保持代码的灵活性和可扩展性。它也能够在运行时动态生成代码,从而更好地支持动态编程。 然而,解释器模式的缺点在于它可能会导致性能问题,因为它需要在解释器中进行大量的运算和计算。此外,解释器模式的设计较为复杂,需要开发者具备较强的编程能力和领域知识。 在软件开发中,解释器模式通常应用于解析和执行脚本、编译器、数据库查询语言等场景。例如,JavaScript的解释器就是一种常见的解释器实现。
喵叔
2023/05/09
3090
设计模式之解释器模式
解释器模式(Interpreter Pattern)是一种行为型设计模式,用于定义语言的文法规则,并提供一个解释器来解释执行这些规则。它属于行为型模式,适用于需要解释语言语法或表达式的场景。
孟斯特
2024/02/06
820
设计模式之解释器模式
Java描述设计模式(14):解释器模式
一、解释器模式 1、基础概念 解释器模式是对象的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的表达式。 2、模式图
知了一笑
2019/10/18
5180
Java设计模式(二十三)----解释器模式
解释器模式 定义:解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。 解释器模式的结构 下
汤高
2018/01/11
5580
Java设计模式(二十三)----解释器模式
【设计模式】行为型模式-第 3 章第 3 讲【解释器模式】
 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
跟着飞哥学编程
2022/12/02
3670
设计模式 | 解释器模式及典型应用
解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的 "语言" 是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
小旋锋
2019/01/21
8940
JAVA中的23种设计模式(GOF)
volatile 保证数据的可见性,让线程内存数据的变化立刻显示到主存中,而且有序性可以避免指令重排,但是不保证原子性。
HcodeBlogger
2020/08/05
9880
JAVA中的23种设计模式(GOF)
设计模式--解释器模式
解释器模式是一种行为型设计模式,它定义了一种语言解释器的方式,用于解释特定的语言或符号。在该模式中,定义一个语法,用于解释特定的输入,并把这个语法表示为一个解释器。
软件架构师Michael
2023/07/19
2900
设计模式(二十三):行为型之解释器模式
注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法
冬天vs不冷
2025/01/21
880
设计模式(二十三):行为型之解释器模式
相关推荐
设计模式——解释器模式
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 一、分离热点数据方案
  • 二、采用本地或分布式缓存方案
  • 三、分库分表
    • 1.分库分表的必要性
    • 2.拆分方式
      • (1)垂直拆分
      • (2)水平拆分
      • (3)应对水平拆分问题的方案
    • 3.拆分库表解决方案
      • (1)Sharding-JDBC
      • (2)MyCat
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档