一些简单实用的小设计(2)
主题
一些简单实用的小设计(2)
问题
数据太大,查不动,写不动
解决方案
分库分表
将一个大型表,拆解为多张表,维持表的数据量大小在可接受范围。
两种方法,垂直分表和水平分表。
垂直分表
行内数据变小,一行拆到多张表。这就是垂直分表。
这种拆分的内在思路是基于行内数据热度不一,有些字段热度明显高于其他字段,这时可以将这些高热度字段抽离,单独建表。
create table UserInfo( |
上图的表,对应的是用户信息,和积分。
这时我们发现除了 pwd
、userPoints
字段外,其他都是相对静态的。这里 pwd
、userPoints
字段成为热点字段。
为了解决这个问题,我们尝试拆分:
create table UserBase( |
这时三张表已经独立,唯一的联系是 id
。这时,
UserBase
表的数据,可以缓存,不会大面积更新。UserPwd
表和UserPoints
表的数据,均已独立,不会影响UserBase
一旦采用这种模式,就不应该再使用 join
模式连表。而应该考虑在应用层加工数据,基于主键 id
针对性查询。
实质上,这是微服务的前置手段,只有合理切分数据表,才能细化出各具功能的微服务。更进一步,由于表之间关系已经降低,可以进行分库操作,不影响业务实现,成倍提升性能。
分库注意点
- 统计成为问题,需要解决方案代替
join
- 分布式事务重要性提升,必须考虑
- 机器数增加,成本上升,运维难度加大
水平分表
行内数据不变,但多行数据分布在不同的表。这就是水平分表。
这种拆分的内在思路是一个箩筐装不下,我就准备多个箩筐。这时,按照事前约定的规则,数据唯一丢到一个筐里。
create table UserOrder( |
假设积分兑换的单子不断,数据行数超过5000千万级。这时对该表的插入和查询已经颇为耗时。
为解决这个问题,可以对该表进行拆分。常见的拆分方法有:
范围路由
以某数据分片管理。优点是增加表平滑;缺点是分布不均匀。
另一个需要考虑的问题是数据分片范围需要仔细权衡,太大失去意义,太小表太多
Hash
路由
基于一个或多个字段进行 Hash
,基于结果再分发。
最简单的方法是建立$n$张表,然后通过某字段进行取模获得具体的表。这里我们以主键 id
为例,假设有$id\ mod\ n = 3$,这样,我们的记录就插入 UserOrder03
表。
优点是分布相对均匀;缺点是新加表不容易,需要重分布。
同样的,初始容量是一个需要经验权衡的值,大小至关重要。
配置路由
专门抽取一张表,记录用户使用哪张表。这就是配置路由。
这个模式灵活性较强。但是唯一的缺点是,需要多读取一次数据库获得具体表。当然,这个可以通过缓存解决。
请注意,若该表也极大了,就不得不退化到上面两种。因为再为该路由表配置路由表的路由表,总是觉得怪。
注意点
- 统计成为大难题,需要统计多表
- 分页查询,特别是按时间的,非常困难
小结
业务的特性,以及对已有系统的妥协,决定我们最终使用哪种方式解决问题。
解决问题没有最佳方案,只有阶段性满足方案。
IT系统建设是在各种妥协下达成共识的过程,不要贪快贪全,也不要保守畏缩。
BTW
写到最后才发现数据库读写分离还没写,却先写了分库分表。明天写读写分离。