单个表的字段数量建议控制在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条。
图6-4两个案例:
银行的交易流水表,所有进出的交易都需要登记这张表,因为绝大部分时候客户都是查询当天的交易和一个月以内的交易数据,所以我们根据使用频率把这张表拆分成三张表:
当天表:只存储当天的数据。
当月表:我们在夜间运行一个定时任务,前一天的数据,全部迁移到当月表。用的是insert into select,然后delete。
历史表:同样是通过定时任务,把登记时间超过30天的数据,迁移到history历史表(历史表的数据非常大,我们按照月度,每个月建立分区)。
费用表:消费金融公司跟线下商户合作,给客户办理了贷款以后,消费金融公司要给商户返费用,或者叫提成,每天都会产生很多的费用的数据。为了方便管理,我们每个月建立一张费用表,例如fee_detail_201901……fee_detail_201912。
但是注意,跟分区一样,这种方式虽然可以一定程度解决单表查询性能的问题,但是并不能解决单机存储瓶颈的问题。
多库水平分表多库水平分表,其实有点类似于分库分表的综合实现方案,从分表来说是减少了单表的数据量,从分库层面来说,降低了单个数据库访问的性能瓶颈,如图6-5所示。
图6-5 常见的水平分表策略分库更多的是关注业务的耦合度,也就是每个库应该放那些表,是由业务耦合度来决定的,这个在前期做领域建模的时候都会先考虑好,所以问题不大,只是分库之后带来的其他问题,我们在后续内容中来分析。
而分表这块,需要考虑的问题会更多一些,也就是我们应该根据什么样的策略来水平分表?这里就需要涉及到分表策略了,下面简单介绍几种最常见的分片策略。
哈希取模分片哈希分片,其实就是通过表中的某一个字段进行hash算法得到一个哈希值,然后通过取模运算确定数据应该放在哪个分片中,如图6-6所示。这种方式非常适合随机读写的场景中,它能够很好的将一个大表的数据随机分散到多个小表。
图6-6 hash取模的问题