失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 如何让mysql存储海量数据

如何让mysql存储海量数据

时间:2019-05-27 09:46:40

相关推荐

如何让mysql存储海量数据

关系型数据库是日常工作中常用的数据存储中间件,而mysql又是关系型数据库中最流行的数据库之一。无论是中小型系统还是大型互联网系统,都会有mysql的身影。

在中小型系统中,由于数据普遍比较少,通常使用一个mysql实例,再加上合适的业务索引,足可以支撑完整的业务系统。而对于大型的互联网系统,需要存储的数据量是海量的,像某宝,某多等电商系统,一张订单表,每天数据增量可能多达千万甚至上亿,采用小型系统中数据存储策略,显然是不合适的,下面小编介绍两种可以提高数据性能的架构方案。

读写分离

读写分离的基本原理是将数据库的读写操作分散到不同的节点上。使用多个节点分散客户端的读请求压力。对于一个关系型数据库来说,读操作相对于写操作而言更加的耗费系统资源。

读写分离的架构图如下:

读写分离的基本实现如下:

1.数据库服务器搭建主从集群,一主一从,一主多从都是可以的。主要就是结合当前数据库请求量的情况。

2.数据库主库通过复制,将数据同步到从库,每台数据库实例,都存储了所有的业务数据。

3.业务服务器将写操作发送给服务器主库,将读操作发送给数据库从库。

读写分离主要使用多个数据库实例,来提高数据库的计算能力,使用一个或者多个从机来分担原本需要主机来处理的读请求,从而来提高数据库集群的整体的吞吐量。

从读写分离的架构图来看,读写分离的实现逻辑并不复杂,但是有两个细节将引入设计的复杂度:读写分配机制和主从复制延迟。

复制延迟

因为写请求有主库进行处理,所以最新的数据只存在主库上,为了获取最新的数据,从库需要不断的从主库复制最新数据。但是这个复制过程由于网络,数据库服务器负载等原因存在延迟,导致主从库中数据的不一致。

以mysql为例,主从复制延迟可能达到1秒。如果存在大批量的数据需要同步,再加上主库或者从库负载压力过大等原因,延迟一分钟也是有可能的。

主从复制延迟会导致"已经插入的数据,却查询不到"的问题",这个问题对业务的影响程度取决于业务的属性,如果是社交平台中用户昵称或者描述的变更,复制延迟问题,对业务影响不大。如果是注册登录型业务的话,可能会导致已经注册成功的用户,进行登录时提示未注册,这个影响就是不可接受的了。

常用的解决主从延迟有以下两种方法:

1.对于实时性要求比较高关键业务,全部由主服务器处理,非关键业务采用读写分离。

2.读从库失败后再读一次主库。这就是常说的"二次读取"机制,该方案的不足之处在于,如果二次读取的次数过多的话,主机被访问的次数就会大大增加,例如:黑客暴力破解账号,导致大量二次读取操作。这样就失去了读写分离的意义了。

分配机制

读写分配机制是指,将业务系统发起的读写操作区分开来,进行分别处理:读请求由从库处理,写请求由主库处理。

读写分配机制的实现一般有两种:代码封装和中间件封装。

代码封装

在业务系统中,抽象出一个数据访问层,在该层中实现读写操作的分离和数据库的连接管理。

系统架构如下:

中间件分装:

中间件封装,通常是使用一个独立的系统,来实现读写操作分离和数据库的连接管理。该独立系统,对业务系统提供sql兼容协议,业务系统无须自己进行读写分离,对于业务系统来说,访问中间件和访问数据库没有区别,数据库的主从切换对于业务系统而言是无感的,中间件屏蔽了这些细节。

系统架构如下:

中间件封装的实现相比代码封装而言,实现复杂度更高,开发成本也更大,但是一旦做好,接入的业务系统越多,节省的程序开发投入也就越大,价值也就越大。而代码封装实现的复杂度相对低一些,但是业务系统需要感知对于数据库的主从切换等变化,使业务系统和数据库的变更耦合在了一起。

分库分表

读写分离,分散了数据库的读操作压力。但是没有分散存储压力(所有从库都会存放主库中完整的数据)。如果数据库性能降低是由于数据表中数据量过多导致的话,如:在业务流量低峰期,执行一条查询,就特别耗时的话,那么此时再进行读写分离,增加从库数量,就没有意义了。

除此之外,单台数据库服务器如果存储过多数据的话(单表数量过亿),数据库的存储能力会成为整个系统的瓶颈:

1.数据量大,读写性能都会降低,即使有索引,索引也会变得很大,性能同样会降低。

2.数据文件变大,数据库备份和恢复需要耗费的时间更长。

基于上述原因,单台数据库服务器存储的数据量要控制在一定的范围内,为了满足业务数据的存储需求,需要将存储分散到多台数据库服务器上。目前业界常见的分散储存方法为分库和分表两大类。

分库

业务分库,是指按照业务模块将数据分散到不同的数据库服务器。这也是系统微服务化过程中,不同业务系统数据隔离的常用做法。这样可以使原本存放在一个数据库服务器上的数据,根据业务的拆分,分散到不同的数据库服务器上,这样可以让每个数据库服务器存放的数据量都有所降低。

这种做法虽然降低了数据库服务器的存储压力,但是会给业务实现带来新的问题:

1.join的实现

分库会使同一个数据库中的表,分散到不同的数据库中,导致无法使用sql中的join查询。

2.事务问题:

分库前,同一个库中多个表的操作可以在一个事务中完成,分库后,表被分散到其他库中,如果需要实现事务操作,那么只能使用一些分布式事务的方案,但是目前现有的分布式事务实现方案,性能相对于数据库自身的事务实现来说都比较低。

分表

分库是一种将不同业务数据分散到不同数据库服务器,降低单台数据库服务器存储的数据量,来提升数据库服务器性能的方案。但是,如果同一个业务的单表数据量很大的话(某音大几亿的用户信息),此时也会导致单台数据库服务器存储压力过大。

对于单表数据量过多的场景,常用的做法是分表。

垂直分表

垂直分表:是指将表进行垂直切分,这种切分方式,会改变数据表的表结构,切分后的多个表的表字段,是原表表字段的子集,切分后的多个表的数据行数是相同的。垂直分表示意图如下:

垂直分表可以将表中一些不常用且占用了大量空间的字段拆分出去。这样单表的数据量会大幅度减低,表中需要建立的索引也会大大减少,对于数据的读写都会变得友好。

但是垂直分表带来不足之处就是对数据表操作的次数要增加。原本读写操作只需要访问一张表,现在可能需要访问多张表才可以。例如:插入一条完整的数据,需要将这条数据,分成多部分插入到对应的多个子表中。

水平分表

水平分表是指将表进行水平切分,切分后的多个表,拥有相同的表结构,水平切分,主要适用于表行数过多的场景。这里,多少算是过多,需要结合实际的业务场景。有些表结构比较简单,可能过亿也不算多,有些表超过千万行,就已经算很多了。水平分表示意图如下:

单表水平分表后,会面临以下问题:

路由

路由是指:水平分表后,某条数据具体属于分表后的哪个子表的问题。例如,现在需要插入一条数据,这条数据应该插入到哪个子表中。常用的路由算法有以下三种。

范围路由:

选择有序的数据列作为路由条件,再确定每个子表存放的数据量,然后根据数据列的值,确定数据存储到哪个子表。例如:用户表中,以用户id作为路由条件,每个子表存放1千万行数据。那么,id为前1千万的用户存放到子表1中,id在1千万-2千万的用户存放到子表2中,依次类推。

范围路由设计的复杂度主要体现在分段的选取上:分段太小导致分表后子表过多,增加维护的复杂度,分段太大,会导致单表存放数据量依旧很大。

范围路由的好处在于,随着数据量的增加可以平滑的扩充表,原有的数据存放的位置不需要变更。不足之处也比较明显,范围路由会产生数据分布不均的问题,范围路由的算法思想就是使用有序的字段,作为路由条件,这样会导致只有前面子表填满,才会路由到后面的子表

Hash路由:

选择某个列的值或者某几个列的组合的值,进行hash运算,根据hash运算的结果,确定需要路由到的目的子表。Hash路由设计的复杂度主要体现在原始表需要分成多少个子表。

如果子表过多,维护难度较大,如果子表较少,会导致单表性能存在问题。而使用Hash路由后,增加子表又是很麻烦的。需要所有数据都进行重分布,所以hash路由的优点在于:数据在各个子表中分布的比较均匀,缺点在于,扩充新表比较麻烦。

配置路由:

上面的范围路由会存在数据分布不均的问题,hash路由会存在表扩充困难的问题。而出现这两个问题的原因主要在于,范围路由在在设计时,限制了子表可以存放的数据量。hash路由在设计时,限制了子表的个数。为了解决这两个问题,配置路由就比较自由了,没有那么多的限制。

配置路由的实现是,采用一张独立路由表来记录路由信息,这张路由表主要有两个字段信息:路由条件字段和路由的目的表名称。

以用户表为例,路由表中包含user_id和table_id两列。table_id记录了user_id指定的数据存放的子表,这种方式更加灵活,当需要扩充子表或者修改数据行存放的子表,可以通过修改路由表的内容来完成。

不过由于路由信息是单独存储的,那么每次读写操作,都需要访问这个路由表,而且如果路由表数据量很大的话,同样会出现性能问题。除此之外,如果路由表数据出现丢失,那么将是灾难性性的,所有存放到子表中的数据,都将不可访问,成为脏数据。

count

水平分表后,虽然物理上数据分散到了多个表中,但是某些业务逻辑还是会将这些子表当做一个表来处理,例如获取数据总数。对于计算数据总量的问题,在分表前,只需要一个count函数即可完成,而水平分表后,就没有那么简单了。常用到处理方式有一下两种。

count()相加:就是将所有子表各自count()的结果求和。这种方式实现起来比较简单,但是性能较低,如果子表比较多情况下,需要执行多次count()。

记录表法:具体就是新建一张表,这张表包含两个字段:table_name,row_count,每次更新子表,都需要同时更新该表。这种方式相比较count相加,性能有很大的提升,只需要查记录表即可,但是这种方式会存在数据不一致的问题:因为记录表不一定和其他子表在一个数据库服务器中,所有的更新操作无法在一个事务中进行保障,可能存在子表更新成功,记录表更新失败的情况。

orderby

水平分表后,数据分散到多个子表中,排序操作无法在同一个数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。

总结

以上讨论的分库分表和读写分离的架构设计,虽然可以提高关系型数据库的处理性能,但是也带来了诸多问题,所以很多云厂商就提供了相关的云产品:分布式数据库,例如:tidb。这些云产品,兼容mysql的协议,而且可以轻松实现单表存储超10亿的数据量。什么分库分表,压根不存在的。这也就是所谓的"需求驱动发展"吧。

如果觉得《如何让mysql存储海量数据》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。