数据库优化之分库分表(异构索引)
1_优点
一般互联网项目,随着时间和业务的发展,数据库中的数据量会一直增多,数据量的增多,这时因为更大的系统开销和更多的IO读取会导致数据库性能会急剧下降,毕竟单个机器的性能是有极限的,所以要对数据库和表进行拆分,使其分布在多个机器上,这样就能支持更高的数据库并发量.
还有,根据mysql执行过程来说,1.我们接受到sql,2.把sql放进等待队列中,3.执行sql,4.返回执行结果.这个过程最花费时间的是什么呢?第一,等待时间.第二,执行时间.准确的来说执行时间会导致等待时间变长.
mysql中有一种机制是表锁定和行锁定,为什么要出现这种机制,是为了保证数据的完整性,我举个例子来说吧,如果有二个sql都要修改同一张表的同一条数据,这个时候怎么办呢,是不是二个sql都可以同时修改这条数据呢?很显然mysql对这种情况的处理是,一种是表锁定(myisam存储引擎),一个是行锁定(innodb存储引擎)。表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行。行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作。如果数据太多,一次执行的时间,等待的时间就越长,这也是我们为什么要分表的原因。
2_缺点
分库分表并不是完美的,任何一种解决方案在解决一个问题的同时都可能会引入其他问题.
2-1.分库
1.在执行分库操作后,原本逻辑关联性很强的数据难免会划分到多个库中,这时,表的关联操作难免受到影响.因为多数公司都命令禁止跨库sql,我们无法用join做联表操作,结果本来一次查询能完成的业务,可能需要多次查询才能完成.
2.原本单数据库中,可以用事务来保证一些操作的原子操作,但是执行分库操作后,统一管理将会变得困难,虽然一些大厂提供了不少跨库的事务解决方案,但是性能上仍然有着不小的欠缺,所以很多情况下并不实用.而且多数情况下,分布式事务无法避免,根据业务情况,适当使用分布式事务.
3.分库意味着开发和数据库管理的工作量成倍的增加,原来只需要一个数据库管理人员,分库后可能需要两个或三个,导致公司在人力上的投入大大增加.
2-2.分表
1.数据表垂直拆分后,原来只需要一次查询的sql可能会变成需要join的连表查询,这会导致性能上的损失
2.表才分后如果遇到有order by(排序)的操作,数据库就无能为力了,只能由业务代码或数据库中间件来实现了.
3.数据库横向拆分时,常用的主要有两种规则:范围切分和哈希值切分.范围 切分是指按照某个字段的范围来切分,比如用户表按照用户ID来切分,ID为1到10万的位于User1中,100001到200000万的位于User2中,这样切分的优势是可以无限扩容下去,不用考虑数据迁移的问题,劣势就是新表和旧表数据分布不均匀,而且分表的范围选取有一定难度,范围太小会导致表太多,太大会导致问题根本上没有解决的困惑。另外一种分表策略就是把某一列按照哈希值来路由到不同的表中,同样以用户ID为例,假如我们一开始就规划了10个数据库表,路由算法可以简单地用 user_id %10的值来表示数据所属的数据库表编号,ID为985的用户放到编号为5的子表中,ID为10086的用户放到编号为6的字表中。这种切分规则的优势是每个表的数据分布比较均匀,但是后期扩容会设计到部分数据的迁移工作。
3_分库分表策略
3_1.根据业务垂直拆分
分库策略,业务按照规则划分好边界,每个业务对应的数据库自然就诞生了.举例:一个典型的电商系统可以划分为商品,订单,用户等模块.
分表策略,表也可以按照业务需求来拆分,比如一个数据库中的订单表,根据业务需求和访问频繁程度,可以划分为,经常要访问的订单数据表和不经常访问的订单详情表.
3_2.根据数据量横向切分
横向切分是诸多业务中最常用的切分方式,本质是把一个表中的数据行按照规则分散到多个表中,比如最常见的按照ID范围,按照业务主键的哈希值等。至于表数据到达什么数量级之后进行切分,这和表中存的数据格式有关,比如一个表只有几列的int字段肯定要比几列text类型的表存储的极限要高,姑且认为这个极限是1000万吧。但是作为一个系统的负责人或者架构师来说,当表的数据量级到达千万级别要引起重视,因为这是一个系统性能瓶颈的隐患。
相对于数据表的横向切分,在符合业务优化的场景下我更倾向于做表分区,按照规则把不同的分区分配到不同的物理磁盘,这样的话,业务里的SQL语句几乎可以不用改动。
4_异构索引表
基于订单数据的分库分表场景,按照订单id取模虽然很好地满足了订单数据均匀地保存在数据库中,但在买家查看自己订单的业务场景中,就出现了全表扫描的情况,而且买家查看自己订单的请求是非常频繁的,必然给数据库带来扩展和性能的问题,有违“尽量减少事务边界”这一原则。
针对这类场景问题,最常用的是采用“异构索引表”的方式解决,即采用异步机制将原表的每一次创建或更新,都换另一个维度保存一份完整的数据表或索引表。这是另一种解决思路:拿空间换时间。
也就是应用在穿件或更新一条订单ID为分库分表键的订单数据时,也会再保存一份按照买家ID为分库分表键的订单索引数据,其结果就是同一买家的所有订单索引表都保存在同一数据库中,这就是给订单创建了异构索引表。
这样, 最终通过两次访问效率最高的sql请求代替了之前的需要进行全表扫描的问题。
有人可能会指出,为什么不是将订单的完整数据按照买家id维度进行一次分库保存,这样就只需要进行一次按照买家id维度进行数据库的访问就获取到订单的信息了?
这是一个好问题,其实淘宝的没订单数据就是在异构索引表中全复制的,即订单按照买家id维度进行分库分表的订单索引表跟以订单id维度进行分库分表的订单表中的字段完全一样,这样确实避免了多一次的数据库访问。但一般来说,应用可能会按照多个维度创建多个异构索引表,如果全部采用全复制的方法会带来大量的数据冗余,从而增加不少数据存储成本。
实际场景下,也并非不可采取全复制的方式.虽然可能会带来高额存储成本,但是也能带来相对的性能提升,可以提高数据库并发量和用户体验.
扩展链接,数据库分区,分库分表,分片:https://blog.csdn.net/qq_28289405/article/details/80576614