千万级并发架构下,关系型数据库应该如何优化?大厂是如何做分库分表的!(转)
随着互联网的高速发展,带来了海量数据存储的问题,比如像物联网行业,每个智能终端每天进行数据采集和上报,每天能够产几千万甚至上亿的数据。在互联网电商行业,或者一些O2O平台,每天也能产生上千万的订单数据,这些量级的数据在传统的关系型数据库中已经无法支撑了,那么如何解决海量数据存储和计算等问题,在业内引入了分布式存储和分布式计算等解决方案,特别是NoSql的生态,我在前面讲过的k-v数据库、文档数据库、图形数据库等,都是比较主流的分布式数据库解决方案。
即便如此,关系型数据库仍然有它不可替代的特性,所以关系型数据库仍然是核心业务的基础数据平台,因此关系型数据库必然会面临数据量日益增长带来的海量数据处理问题。
Mysql数据库海量数据带来的性能问题
目前几乎所有的互联网公司都是采用mysql这个开源数据库,根据阿里巴巴的《Java开发手册》上提到的,当单表行数超过500W行或者单表数据容量超过2G时,就会对查询性能产生较大影响,这个时候建议对表进行优化。
其实500W数据只是一个折中的值,具体的数据量和数据库服务器配置以及mysql配置有关,因为Mysql为了提升性能,会把表的索引装载到内存,innodb_buffer_pool_size 足够的情况下,mysql能把全部数据加载进内存,查询不会有问题。
但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制,这里,增加硬件配置,可能会带来立竿见影的性能提升。
innodb_buffer_pool_size 包含数据缓存、索引缓存等。
Mysql常见的优化手段
当然,我们首先要进行的优化是基于Mysql本身的优化,常见的优化手段有:
增加索引,索引是直观也是最快速优化检索效率的方式。
基于Sql语句的优化,比如最左匹配原则,用索引字段查询、降低sql语句的复杂度等
表的合理设计,比如符合三范式、或者为了一定的效率破坏三范式设计等
数据库参数优化,比如并发连接数、数据刷盘策略、调整缓存大小
数据库服务器硬件升级
mysql大家主从复制方案,实现读写分离
这些常见的优化手段,在数据量较小的情况下效果非常好,但是数据量到达一定瓶颈时,常规的优化手段已经解决不了实际问题,那怎么办呢?
大数据表优化方案
对于大数据表的优化最直观的方式就是减少单表数据量,所以常见的解决方案是:
分库分表,大表拆小表。
冷热数据分离,所谓的冷热数据,其实就是根据访问频次来划分的,访问频次较多的数据是热数据,访问频次少的数据是冷数据。冷热数据分离就是把这两类数据分离到不同的表中,从而减少热数据表的大小。
其实在很多地方大家都能看到类似的实现,比如去一些网站查询订单或者交易记录,默认只允许查询1到3个月,3个月之前的数据,基本上大家都很少关心,访问频次较少,所以可以把3个月之前的数据保存到冷库中。
历史数据归档,简单来说就是把时间比较久远的数据分离出来存档,保证实时库的数据的有效生命周期。
其实这些解决方案都是属于偏业务类的方案,并不完全是技术上的方案,所以在实施的时候,需要根据业务的特性来选择合适的方式。
详解分库分表
分库分表是非常常见针对单个数据表数据量过大的优化方式,它的核心思想是把一个大的数据表拆分成多个小的数据表,这个过程也叫(数据分片),它的本质其实有点类似于传统数据库中的分区表,比如mysql和oracle都支持分区表机制。
分库分表是一种水平扩展手段,每个分片上包含原来总的数据集的一个子集。这种分而治之的思想在技术中很常见,比如多CPU、分布式架构、分布式缓存等等,像前面我们讲redis cluster集群时,slot槽的分配就是一种数据分片的思想。
如图6-1所示,数据库分库分表一般有两种实现方式:
水平拆分,基于表或字段划分,表结构不同,有单库的分表,也有多库的分库。
垂直拆分,基于数据划分,表结构相同,数据不同,也有同库的水平切分和多库的切分。
垂直拆分
垂直拆分有两种,一种是单库的垂直拆分,另一种是多个数据库的垂直拆分。
单库垂直分表
单个表的字段数量建议控制在20~50个之间,之所以建议做这个限制,是因为如果字段加上数据累计的长度超过一个阈值后,数据就不是存储在一个页上,就会产生分页的问题,而这个问题会导致查询性能下降。
所以如果当某些业务表的字段过多时,我们一般会拆去垂直拆分的方式,把一个表的字段拆分成多个表,如图6-2所示,把一个订单表垂直拆分成一个订单主表和一个订单明细表。
图6-2
在Innodb引擎中,单表字段最大限制为1017 参考: https://dev.mysql.com/doc/mysql-reslimits-excerpt/5.6/en/column-count-limit.html
多库垂直分表
多库垂直拆分实际上就是把存在于一个库中的多个表,按照一定的纬度拆分到多个库中,如图6-3所示。这种拆分方式在微服务架构中也是很常见,基本上会按照业务纬度拆分数据库,同样该纬度也会影响到微服务的拆分,基本上服务和数据库是独立的。
图6-3
多库垂直拆分最大的好处就是实现了业务数据的隔离。其次就是缓解了请求的压力,原本所有的表在一个库的时候,所有请求都会打到一个数据库服务器上,通过数据库的拆分,可以分摊掉请求,在这个层面上提升了数据库的吞吐能力。
水平拆分
垂直拆分的方式并没有解决单表数据量过大的问题,所以我们还需要通过水平拆分的方式把大表数据做数据分片。
水平切分也可以分成两种,一种是单库的,一种是多库的。
单库水平分表
如图6-4所示,表示把一张有10000条数据的用户表,按照某种规则拆分成了4张表,每张表的数据量是2500条。
两个案例:
银行的交易流水表,所有进出的交易都需要登记这张表,因为绝大部分时候客户都是查询当天的交易和一个月以内的交易数据,所以我们根据使用频率把这张表拆分成三张表:
当天表:只存储当天的数据。
当月表:我们在夜间运行一个定时任务,前一天的数据,全部迁移到当月表。用的是insert into select,然后delete。
历史表:同样是通过定时任务,把登记时间超过30天的数据,迁移到history历史表(历史表的数据非常大,我们按照月度,每个月建立分区)。
费用表:消费金融公司跟线下商户合作,给客户办理了贷款以后,消费金融公司要给商户返费用,或者叫提成,每天都会产生很多的费用的数据。为了方便管理,我们每个月建立一张费用表,例如fee_detail_201901……fee_detail_201912。
但是注意,跟分区一样,这种方式虽然可以一定程度解决单表查询性能的问题,但是并不能解决单机存储瓶颈的问题。
多库水平分表
多库水平分表,其实有点类似于分库分表的综合实现方案,从分表来说是减少了单表的数据量,从分库层面来说,降低了单个数据库访问的性能瓶颈,如图6-5所示。
常见的水平分表策略
分库更多的是关注业务的耦合度,也就是每个库应该放那些表,是由业务耦合度来决定的,这个在前期做领域建模的时候都会先考虑好,所以问题不大,只是分库之后带来的其他问题,我们在后续内容中来分析。
而分表这块,需要考虑的问题会更多一些,也就是我们应该根据什么样的策略来水平分表?这里就需要涉及到分表策略了,下面简单介绍几种最常见的分片策略。
哈希取模分片
哈希分片,其实就是通过表中的某一个字段进行hash算法得到一个哈希值,然后通过取模运算确定数据应该放在哪个分片中,如图6-6所示。这种方式非常适合随机读写的场景中,它能够很好的将一个大表的数据随机分散到多个小表。
hash取模的问题
hash取模运算有个比较严重的问题,假设根据当前数据表的量以及增长情况,我们把一个大表拆分成了4个小表,看起来满足目前的需求,但是经过一段时间的运行后,发现四个表不够,需要再增加4个表来存储,这种情况下,就需要对原来的数据进行整体迁移,这个过程非常麻烦。
一般为了减少这种方式带来的数据迁移的影响,我们会采用一致性hash算法。
一致性hash算法
在前面我们讲的hash取模算法,实际上对目标表或者目标数据库进行hash取模,一旦目标表或者数据库发生数量上的变化,就会导致所有数据都需要进行迁移,为了减少这种大规模的数据影响,才引入了一致性hash算法。
如图6-7所示,简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),什么意思呢?
就是我们通过0-232-1的数字组成一个虚拟的圆环,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到232-1,也就是说0点左侧的第一个点代表232-1。我们把这个由2的32次方个点组成的圆环称为hash环。
图6-7
那一致性hash算法和上面的虚拟环有什么关系呢?继续回到前面我们讲解hash取模的例子,假设现在有四个表,table_1、table_2、table_3、table_4,在一致性hash算法中,取模运算不是直接对这四个表来完成,而是对2的32次方来实现。
hash(table编号)%232
通过上述公式算出的结果一定是一个0到232-1之间的一个整数,然后在这个数对应的位置标注目标表,如图6-8所示,四个表通过hash取模之后分别落在hash环的某个位置上。
-----------------------------------
千万级并发架构下,关系型数据库应该如何优化?大厂是如何做分库分表的!
https://blog.51cto.com/u_15082391/4344106
感谢您的阅读,您的支持是我写博客动力。