高性能实践总结:读写分离、分库分表、缓存设计

针对数据库集群

  • 读写分离:将访问压力分散到集群的多个节点,没有分散存储压力
  • 分库分表:分散访问压力,分散存储压力

读写分离

架构图

007S8ZIlgy1gh6n276wxcj30v00hodgw.jpg

其中,主机负责读写,复制数据到从机,从机负责读。引出 2 个问题:复制延迟、分配机制

对于「复制延迟」,解决思路:

  • 写操作后的读操作,都发给主机。比如注册账号后,立即登陆(读操作),请求主机。
  • 二次读取。从机读取失败后,再读主机。
  • 业务分离。关键业务读写是主机,非关键业务读操作是从机。
  • 引入缓存。例如,注册后将账号加入缓存,设置失效时间。登陆先查 redis,没有再查从机,此时数据已经同步完成。最后没有数据,再读主机。

对于「分配机制」,2 种常用方法:

  • 代码封装。在代码种封装个 Apdater 层,对外提供 API
  • 数据库中间件。类似云开发的云数据库,开发者无需关心负载、备份等问题,由云厂商解决

读写分离应用场景?

适用于:读请求多的场景。

读写分离后,主机(读写)可以去掉索引,提高写入速度;从机(读)增加索引,提高读取速度。

分库分表

分库:按照业务模块将数据分散到不同的数据库服务器。

问题:一些数据库操作(例如 join、分布式事务)需要自己在代码中模拟,开发难度大。

分表:分为垂直分表和水平分表。

垂直分表:用于将不常用且占大量空间的字段拆分出去,会导致表的操作数增加。

水平分表:记录数增加时,需要考虑。

水平分表会带来一些问题,例如路由、总数、排序、join 等表操作。

  • 路由:决定数据属于哪个子表
    • 范围路由:1-1000 属于子表 1,2-1000 属于子表 2。优点是直接扩表即可,缺点是数据分配不均匀。
    • hash 路由:自定义 hash 规则,例如根据 id 取余,就是子表 id。优缺点和范围路由相反。
    • 配置路由:新建一张新表存储“路由信息”。例如用户表、信息
  • 总数:
    • 代码封装:多次读取统计
  • 新建表存储记录数

分库分表何时引入?

  1. 改善硬件条件,垂直扩容
  2. 考虑读写分离、引入缓存、全文检索(针对搜索)
  3. 单库单表满足不了,考虑分库 => 分表 => 垂直分表(按照业务逻辑拆分) => 水平分表

缓存问题

缓存穿透

描述:缓存没有发挥作用,业务系统需要再次去存储系统查询数据

原因:

  • 原始数据不存在 9(给默认值)
  • 数据第一次被访问,没在缓存中(拉取)
  • 数据生成消耗过多资源(优化业务逻辑)

缓存雪崩

描述:指当缓存失效(过期)后引起系统性能急剧下降的情况。例如重启系统,大量请求落到缓存,缓存要重新生成;存储系统被频繁访问,压力大。

解决方法:

  • 如果是多线程服务器,可以加锁。效率低,不推荐。
  • 开启独立更新线程,定时刷缓存。业务访问时,数据不存在,可以返回默认值,或者通知更新线程更新缓存。

缓存热点

描述:举个例子,微博明星告白,突然涌入大量需求。

解决方法:加机器。不同机器缓存过期时间不同,防止雪崩。