首页 > 其他分享 ><<高并发系统实战课>> 小记随笔 —— 用户中心案例优化

<<高并发系统实战课>> 小记随笔 —— 用户中心案例优化

时间:2023-03-14 17:44:17浏览次数:51  
标签:缓存 实体 用户 案例 key 随笔 数据 ID 小记

案例介绍

image

用户中心的主要功能是维护用户信息、用户权限和登录状态,它保存的数据大部分都属于读多写少的数据。常见的优化方式主要是将用户中心和业务彻底拆开,不再与业务耦合,并适当增加缓存来提高系统性能。
用户中心通常是系统改造中第一个要优化的模块,因为它常常和多个系统重度耦合,所以梳理这个模块对整个系统后续的高并发改造非常重要。

结构梳理

数据库表结构主要可以分为四类,从数据结构出发,先对一些场景进行改造,按照这四类进行数据整理后,再按需设计缓存策略会轻松很多。

image

数据实体表

实体表一般会作为主表 ,它的一行数据代表一个实体,每个实体都拥有一个独立且唯一的 ID 作为标识。其中,“实体”代表一个抽象的事物,具体的字段表示的是当前实体实时的状态属性。
但是实体表的字段属性需要确认,不要过多,尤其是不要包含 text 字段,否则会导致数据量过多,作缓存时容易过大且需要进行一些特定字段剔除的处理。
不常用的字段可以通过横向或纵向拆分到辅助表中。

实体辅助表

实体表字段过多,且部分字段使用频率并不高的场景下,可以把这些字段摘出来,形成一张实体辅助表。

  • 纵向拆分
    直接把字段拆出来,辅助表的通过记录主表 ID 进行关联,它们之间的常见关系为 1:1。

  • 横向拆分
    一张横表,通常如下:

create table params (
    id bigint not null auto_increment,
    external_id bigint not null,
    key varchar(64) not null,
    value varchar(128),
    primary key (id),
    index idx_cluster_id (external_id));
engine=InnoDB default charset=utf8mb4;

通过 kv 方式维护,辅助表的通过记录主表 ID 进行关联,他们之间的常见关系为 1: n。

实体关系表

  • 1: n 常用来表示从属关系
  • n: 1 常用来表示分类聚合
  • m:n 多对多关系,常见于社交等,建议简化为多组 1: n 关系进行维护

历史动作表

一般来说,动作历史数据表记录的是数据实体的动作或状态变化过程,比如用户登陆日志、用户积分消费获取记录等。这类数据会随着时间不断增长,它们一般用于记录、展示最近信息,不建议用在业务的实时统计计算上。

总结

  • 能够通过 ID 快速匹配的实体,以及通过关系快速查询的数据,适合放在长期缓存当中;
  • 通过组合条件筛选统计的数据,也可以放到临时缓存,但是更新有延迟;
  • 数据增长量大或者跟设计初衷不一样的表数据,这种不适合、也不建议去做做缓存。

image

缓存

不是所有的数据放在缓存就能有很好的收益,我们要从数据量、使用频率、缓存命中率三个角度去分析。

临时缓存

最实用简单的方式就是临时缓存形式

  • 先从缓存读,如果有,直接返回
  • 如果没有,从数据库读取,并存入缓存,设置 TTL 过期时间

但这个在一些场景会出现缓存更新不及时的情况,比如

  • 单条实体数据缓存刷新
  • 关系型和统计型数据缓存刷新

单条实体数据缓存刷新

这种比较简单,在数据更新时,直接提出缓存相关缓存,待下次请求进入时再重新从数据库加载即可。

关系型和统计型数据缓存刷新

监控 binlog 更新

通过订阅数据库来找到 ID 数据变化触发事件,然后监听事件触发到具体执行逻辑来实现缓存更新。

版本号缓存设计

如果我们表内的数据更新很少,那么可以采用版本号缓存设计。这个方式比较狂放:一旦有任何更新,整个表内所有数据缓存一起过期。
比如对 user_info 表设置一个 key,假设是 user_info_version,当我们更新这个表数据时,直接对 user_info_version 进行 incr +1。而在写入缓存时,同时会在缓存数据中记录 user_info_version 的当前值。

识别主要实体 ID 来刷新缓存关联型数据缓存

这要保证其他缓存保存的 key 也是主要实体 ID,这样当某一条关联数据发生变化时,就可以根据主要实体 ID 对所有缓存进行刷新。这个方式的缺点是,我们的缓存要能够根据修改的数据反向找到它关联的主体 ID 才行。

长期热数据缓存

数据库要是扛不住平时的流量,我们就不能使用临时缓存的方式去设计缓存系统,只能用长期缓存这种方式来实现热点缓存,以此避免缓存穿透打沉数据库的问题。要求我们的业务几乎完全不走数据库,并且服务运转期间所需的数据都要能在缓存中找到,同时还要保证使用期间缓存不会丢失。

img

这边提供一个长期热缓存的简单实现逻辑图。一些 key point:

  • 如何判定是否是热点数据?
    • 对于数据量小,可以直接通过 list、map 维护
    • 对于数据量大,可以通过 Bloom Filter
  • 锁的作用
    • 仅让抢到锁的服务去做更新操作,其他的直接读缓存,减少压力。
  • 为什么数据没查到要写 null 到缓存
    • 减少对于这个 key 查询 db 的压力

Token: 降低用户身份鉴权压力

传统的 Session 方式是把用户的登录信息通过 SessionID 统一缓存到服务端中,客户端和子系统每次请求都需要到用户中心去“提取”,这就会导致用户中心的流量很大,所有业务都很依赖用户中心。
为了降低用户中心的流量压力,同时让各个子系统与用户中心脱耦,我们采用信任“签名”的 token,把用户信息加密发放到客户端,让客户端本地拥有这些信息。而子系统只需通过签名算法对 token 进行验证,就能获取到用户信息。这种方式的核心是把用户信息放在服务端外做传递和维护,以此解决用户中心的流量性能瓶颈。
此外,通过定期更换 token,用户中心还拥有一定的用户控制能力,也加大了破解难度,可谓一举多得。

标签:缓存,实体,用户,案例,key,随笔,数据,ID,小记
From: https://www.cnblogs.com/BlueMountain-HaggenDazs/p/17215273.html

相关文章