1、什么是可扩展性
可扩展性。 即通过增加资源提升整个系统吞吐量的能力
一般会有如下的角度影响负载:
- 数据量
- 用户量 更多的用户,意味着更多的数据,更复杂的查询,更多的事务
- 用户活跃度 容易造成热点
- 相关数据集的大小 即关联数据,比如好友
可扩展性的数学表现:
- 最简单的是线性的,资源翻倍,吞吐量翻倍
- 但是由于有些工作是线性的,无法通过并发来提升,这样曲线就会趋于平缓
- 再引入扩展带来的内部节点或者是线程间的通信,会造成曲线的不降反升
- 考虑到有一些I/O密集型的应用,扩展只会可能变成全都读取内存,还有可能造成曲线上扬
2、扩展mysql
通常系统初建时要考虑一点可扩展性。在扩展之前,要先压榨单机性能,比如算法调优等等
2.1 向上扩展(Scale Up)
向上扩展(也叫垂直扩展)就是换CPU,磁盘等等。对部分应用来说这是唯一需要做的。
优点:
- 单台服务器比多台服务器更加容易开发和维护,能显著节省开销
- 在单台服务器上备份和恢复应用同样简单;
- 无需关心一致性;
为了能够更好的在大型服务器上运行MySQL,一定要尽量使用最新的版本。
如果应用变得非常庞大,向上扩展可能就没有办法。
2.2 向外扩展(Scale Out)
向外扩展有时也称为横向扩展或者水平扩展,策略划分为三个部分:复制、拆分,以及数据分片(sharding)。
最常见的向外扩展就是读写分离。通过复制将数据分发到多个服务器上,然后将备库用于读查询。这种技术对于以读为主的应用很有效。
另一个比较常见的向外扩展方法是将工作负载分布到多个 “节点”。接下来我们要了解的主要是这种扩展方法。
在此之前,我们先明确下节点的概念。在 MySQL 架构中,一个节点就是一个功能部件。一般的,我们会将一台服务器作为一个几点。但如果我们考虑到节点的高可用性,那么一个节点通常可能是下面的几种:
- 一个主 - 主 复制双机结构,拥有一个主动服务器和被动服务器。
- 一个主库和多个备库。
- 一个主动服务器,并使用分布式复制块设备(DRBD)作为备用服务器。
- 一个基于存储区域网络(SAN)的 “集群”。
2.2.1、按功能拆分
按功能拆分,或者说按职责拆分,意味着不同的节点执行不同的任务。
例如,如果有一个网站,各个部分无需共享数据,那么可以按照网站的功能区域进行划分。像我们常见的门户网站,一般都是把不同栏目放在一起,但实际上可以将网站新闻、论坛、寻求支持等功能放到专用的 MySQL 服务器。如图:
2.2.2、数据分片
Sharding 是把数据库横向扩展(Scale Out)到多个物理节点上的一种有效的方式,其主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。Shard这个词的意思是“碎片”。如果将一个数据库当作一块大玻璃,将这块玻璃打碎,那么每一小块都称为数据库的碎片。将整个数据库打碎的过程就叫做sharding,可以翻译为分片。
**一个shard可以包含多个表的内容甚至可以包含多个数据库实例中的内容**。每个shard被放置在一个数据库服务器上。**一个数据库服务器可以处理一个或多个shard的数据**。系统中需要有服务器进行查询路由转发,负责将查询转发到包含该查询所访问数据的shard或shards节点上去执行。
MySql的Sharding策略包括垂直切分和水平切分两种。
垂直(纵向)拆分:是指按功能模块拆分,以解决表与表之间的io竞争。比如分为订单库、商品库、用户库...这种方式多个数据库之间的表结构不同。
表结构设计垂直切分。常见的一些场景包括:
a). 大字段的垂直切分。单独将大字段建在另外的表中,提高基础表的访问性能,原则上在性能关键的应用中应当避免数据库的大字段
b). 按照使用用途垂直切分。例如企业物料属性,可以按照基本属性、销售属性、采购属性、生产制造属性、财务会计属性等用途垂直切分
c). 按照访问频率垂直切分。例如电子商务、Web 2.0系统中,如果用户属性设置非常多,可以将基本、使用频繁的属性和不常用的属性垂直切分开
水平(横向)拆分:将同一个表的数据进行分块保存到不同的数据库中,来解决单表中数据量增长出现的压力。这些数据库中的表结构完全相同。
表结构设计水平切分。常见的一些场景包括:
a). 比如在线电子商务网站,订单表数据量过大,按照年度、月度水平切分
b). Web 2.0网站注册用户、在线活跃用户过多,按照用户ID范围等方式,将相关用户以及该用户紧密关联的表做水平切分
c). 例如论坛的置顶帖子,因为涉及到分页问题,每页都需要显示置顶贴,这种情况可以把置顶贴水平切分开来,避免取置顶帖子时从所有帖子的表中读取
2.3、通过多实例扩展
上面提到过,MySQL 不能完全发挥现代硬件的性能。当扩展到超过 24 个 CPU 核心时,MySQL 的性能开始趋于平缓,不再上升。当内存超过 128G 时也同样如此。对于此种情况,我们可以通过多实例策略充分发挥硬件的性能。
多实例策略的基本思路是:
- 数据分片足够小,可以使得在每台机器上都能放置多个分片;
- 每台服务器运行多个实例;
- 给每个实例划分服务器的硬件资源;
可以看出,这是一种向上扩展和向外扩展的组合方案。这种方案还可以通过将每个 MySQL 实例绑定到特定的 CPU 核心上来优化性能。这种优化,主要有两个好处:
- 由于 MySQL 内部的可扩展性限制,当核心数较少时,能够在每个核心上获得更好的性能;
- 当实例在多个核心上运行线程时,由于需要在多核心上同步共享数据,因而会有额外的开销。
而我们把实例和 CPU 核心绑定后,可以减少 CPU 核心直接的切换和交互。要注意的,将进程绑定到具有相同物理套接字的核心上可以获得最优的效果。
2.4、向内扩展
对于不断增长的数据和负载,最简单的方法是对不再需要的数据进行归档和清理。这种操作可能会带来显著的效果。这种做法并不能代替其他策略,但可以作为争取时间的短期策略,也可以作为处理大数据量的长期计划之一。
在设计归档和清理策略时需要考虑如下几点:
- 对应用的影响。设计良好的归档系统能够在不影响事务处理的情况下,从一个高负债的 OLTP 服务器上移除数据。
- 要归档的行。考虑清楚哪些数据可以清理或归档。
- 维护数据一致性。数据间存在联系时,归档任务系统要能够保证数据的逻辑一致性。
- 避免数据丢失。归档时要保证归档数据已经成功保存,再讲源数据删除。
- 解除归档。考虑清楚归档系统中的解除归档策略。可以通过设置一些检查点让系统检查是否有需要归档的数据。
如果不能及时的把老数据归档和清理时,我们也可以通过以下隔离冷热数据的方式来提高性能:
- 将表划分为几个部分。分割大表中的冷热数据,保证加载到内存中的数据中,热数据的比例;
- MySQL 分区。使用MySQL 自带的分区的功能,可以帮助我们把最近的数据留在内存中;
- 基于时间的数据分区。如果应用不断有新数据尽量,一般新数据总是比旧数据更加活跃。因此,我们可以将新数据完整的保留在内存中,同时使用复制来保证主库失效时有一份可以的备份,而旧数据就而言放到别的地方。
3、负载均衡
前端负载均衡器,根据服务器性能情况分发请求。
目的: 更有效的使用资源。 可用性提高(保证有服务有用), 透明。
跟分片和复制关系密切,可以部署在任何一个环节,比如应用前端,数据库前端等
可选的方案: DNS, LVS, TCP代理等等。普遍使用F5, HAProxy
3.1、直接连接
应用直连。不使用中间件完全用业务来判断负载,如选择不同的备库做不同的逻辑。
- 读写分离
写用主库,读用主备分担,实时性的用主库,其他用备库
让应用检查复制延迟,提高容忍脏数据的能力
基于会话,自己修改的数据查看时查主库。
基于版本,查看下备库的时间戳版本类型的字段查看下如果太旧就查主库 - 修改DNS
比较粗的方式,把主库和备库绑定在不同的DNS中,然后切换。但是因为不是原子的,会被缓存等等原因并不是很好用。
3.2、引入中间件
- 负载均衡器
1.一般的负载均衡器都是HTTP协议的,但是Mysql需要TCP协议的,因此要使用能支持TCP协议的,但是这不是专门为Mysql设计的,还是会有一些限制。
不能很好的均衡,因为无法知道负载权重
2.均衡器并不能很好的分辨Mysql连接的状态,保证他能尽量练到固定的服务器,提高缓存效率
3.线程池可能用不到负载分发
4.要求均衡器支持TCP分发及TCP端口监测等等。 - 负载均衡算法
1.随机
2.轮询 依次发送请求
3.最少连接数
4.最快响应
5.哈希 对源IP进行hash
6.权重
7.排队,设置最大连接数。排队处理
3.3、一主多备间的负载均衡
- 功能分区
报表,分析,数据仓库,全文索引等 - 过滤和数据分区
通过复制的过滤功能,将不同的数据分配在不同的备库上 - 保证备库跟上主库 MASTER_POS_WAIT()可以保证主库等待备库同步
- 同步写操作, 有半同步机制
4、分片和分区的区别
Sharding 的思想是从分区的思想而来,但数据库分区基本上是数据对象级别的处理,比如表和索引的分区,每个子数据集上能够有不同的物理存储属性,还是单个数据库范围内的操作,而 Sharding 是能够跨数据库,甚至跨越物理机器的。
MySQL5.1提供的分区(Partition)功能确实可以实现表的分区,但是这种**分区是局限在单个数据库范围里的,它不能跨越服务器的限制。**如果能够保证数据量很难超过现有数据库服务器的物理承载量,那么只需利用MySQL5.1提供的分区(Partition)功能来改善数据库性能即可;否则,还是考虑应用Sharding理念吧,spider storage engine就是一个不错的选择。
1、什么是分库分表?
将一个表结构分为多个表,或者将一个表数据分片后放入多个表,这些表可以放在同一个数据库里,也可以放到不同的数据库中,甚至可以放到不同的数据库实例中
2、为什么分库分表?
随着我们的系统运行,存储在关系型数据库的数据量会越来越大,系统的访问的压力也会随之增大,如果一个库中的表数据超过了一定的数量,比如说mysql中的表数据达到千万级别,就需要考虑进行分库分表。
比如我们有一张表,随着业务的不断进行,mysql中表中数据量达到了10亿,若是将数据存放在一张表中,则性能一定不会太好,根据我们使用的经验,mysql数据库一张表的数据记录极限一般在5000万左右(500w-1000w之间性能比较好),所以我们需要对进行分片存储(水平拆分),按照5000万一个单位来拆分的话,需要切片数量20个,也就是20个数据库表。
如果将20个相同业务表存放在同一个数据库中,那么**单个数据库实例的网卡I/O、内存、CPU和磁盘性能是有限的,随着数据库访问频率的增加,会导致单个数据库实例和数据库达到性能瓶颈**,因此我们需要将20个表分到多个数据库和多个数据库实例中
3、如何分库分表?
3.1、垂直拆分
根据业务的维度,将原本一个库中的表拆分多个表,每个库中表与原有的结构不同
3.1.1、垂直分库
垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。与"微服务治理"的做法相似,每个微服务使用单独的一个数据库。如图:
3.1.2、垂直分表
垂直分表是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过"大表拆小表",更便于开发与维护,也能避免跨页问题,MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。
3.1.3、优缺点
垂直切分的优点:
- 解决业务系统层面的耦合,业务清晰
- 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
- 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈
缺点:
- 部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度
- 分布式事务处理复杂
- 依然存在单表数据量过大的问题(需要水平切分)
3.2、水平拆分
根据分片算法,将一个库拆分成多个库,每个库依旧保留原有的结构
当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。
水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。如图所示:
库内分表只解决了单一表数据量过大的问题,但没有将表分布到不同机器的库上,因此对于减轻MySQL数据库的压力来说,帮助不是很大,大家还是竞争同一个物理机的CPU、内存、网络IO,最好通过分库分表来解决。
3.2.1、优缺点
水平切分的优点:
- 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
- 应用端改造较小,不需要拆分业务模块
缺点:
- 跨分片的事务一致性难以保证
- 跨库的join关联查询性能较差
- 数据多次扩展难度和维护量极大
3.2.2、几种分片规则
水平切分后同一张表会出现在多个数据库/表中,每个库/表的内容不同。几种典型的数据分片规则为:
1、根据数值范围
按照时间区间或ID区间来切分。例如:按日期将不同月甚至是日的数据分散到不同的库中;将userId为19999的记录分到第一个库,1000020000的分到第二个库,以此类推。某种意义上,某些系统中使用的"冷热数据分离",将一些使用较少的历史数据迁移到其他库中,业务功能上只提供热点数据的查询,也是类似的实践。
这样的优点在于:
- 单表大小可控
- 天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移
- 使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。
缺点:
- 热点数据成为性能瓶颈。连续分片可能存在数据热点,例如按时间字段分片,有些分片存储最近时间段内的数据,可能会被频繁的读写,而有些分片存储的历史数据,则很少被查询
2、根据数值取模
一般采用hash取模mod的切分方式,例如:将 Customer 表根据 cusno 字段切分到4个库中,余数为0的放到第一个库,余数为1的放到第二个库,以此类推。这样同一个用户的数据会分散到同一个库中,如果查询条件带有cusno字段,则可明确定位到相应库去查询。
优点:
- 数据分片相对比较均匀,不容易出现热点和并发访问的瓶颈
缺点:
- 后期分片集群扩容时,需要迁移旧的数据(使用一致性hash算法能较好的避免这个问题)
- 容易面临跨分片查询的复杂问题。比如上例中,如果频繁用到的查询条件中不带cusno时,将会导致无法定位数据库,从而需要同时向4个库发起查询,再在内存中合并数据,取最小集返回给应用,分库反而成为拖累。
4、分库分表带来的问题
分库分表能有效的缓解单机和单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数的瓶颈,同时也带来了一些问题。下面将描述这些技术挑战以及对应的解决思路。
4.1、事务一致性问题
- 分布式事务
当更新内容同时分布在不同库中,不可避免会带来跨库事务问题。跨分片事务也是分布式事务,没有简单的方案,一般可使用"XA协议"和"两阶段提交"处理。
分布式事务能最大限度保证了数据库操作的原子性。但在提交事务时需要协调多个节点,推后了提交事务的时间点,延长了事务的执行时间。导致事务在访问共享资源时发生冲突或死锁的概率增高。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平扩展的枷锁。
- 最终一致性
对于那些性能要求很高,但对一致性要求不高的系统,往往不苛求系统的实时一致性,只要在允许的时间段内达到最终一致性即可,可采用事务补偿的方式。与事务在执行中发生错误后立即回滚的方式不同,事务补偿是一种事后检查补救的措施,一些常见的实现方法有:对数据进行对账检查,基于日志进行对比,定期同标准数据来源进行同步等等。事务补偿还要结合业务系统来考虑
4.2、跨节点关联查询 join 问题
切分之前,系统中很多列表和详情页所需的数据可以通过sql join来完成。而切分之后,数据可能分布在不同的节点上,此时join带来的问题就比较麻烦了,考虑到性能,尽量避免使用join查询。
解决这个问题的一些方法:
1)全局表
全局表,也可看做是"数据字典表",就是系统中所有模块都可能依赖的一些表,为了避免跨库join查询,可以将这类表在每个数据库中都保存一份。这些数据通常很少会进行修改,所以也不担心一致性的问题。
2)字段冗余
一种典型的反范式设计,利用空间换时间,为了性能而避免join查询。例如:订单表保存userId时候,也将userName冗余保存一份,这样查询订单详情时就不需要再去查询"买家user表"了。
但这种方法适用场景也有限,比较适用于依赖字段比较少的情况。而冗余字段的数据一致性也较难保证,就像上面订单表的例子,买家修改了userName后,是否需要在历史订单中同步更新呢?这也要结合实际业务场景进行考虑。
3)数据组装
在系统层面,分两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据。最后将获得到的数据进行字段拼装。
4)ER分片
关系型数据库中,如果可以先确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上,那么就能较好的避免跨分片join问题。在1:1或1:n的情况下,通常按照主表的ID主键切分。如下图所示:
4.3、全局主键避重问题
在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自增长将无用武之地,某个分区数据库自生成的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重复问题。