一次简单的分表实践

背景:笔者负责公司内部单点登录系统(inpass)已两年有余,登录服务接入逐渐增多,登录日志逐渐增多。笔者对登录日志也进行了几轮改造和优化,其中涉及到了一次简单的分表操作,特此记录下来。

改造之前的状况(2018.7月调研)

inpass日志表中id采用工具类(SnowflakeIdWorker)自动生成id,但是存在高并发下id重复问题,数据库插入存在主键冲突,导致业务方调用出现异常。同时原有数据库表login_log中数据量已达400w ,统计最近4天的登录日志,平均每天16000条,且有逐渐增多的趋势,因此有必要进行优化。当时调研了三种方案,如下:

方案1:

将id生成器工具类替换为使用共享服务部提供的ID生成器服务,其他不变。优点:可快速优化高并发下id重复问题,代码量比较少缺点:需要增加依赖服务,无法对现有表数据进行拆分优化。

方案2:

重新构建日志表(loginlognew),表字段不变,id改为数据库自增方式。优点:

  1. 不依赖外部id唯一工具,
  2. 可拆分登录日志,减少原有表数据逐渐增大的问题。
    缺点:
    需要新建表。

方案3:

删除当前日志表(login_log)中3个月之前的数据 方案1.优点:不需要新建表,缺点:线上删除大数据量可能会锁表,同时会丢失大量日志数据。综合考虑,方案2为优。那上面的方案是否是一劳永逸的呢?答案是否定的。

我们设想一下,现在的日均日志量已经不是16000 ,而是18000 了,假如以一年为单位,16000*365 = 584W条。18000*365 = 657W条。数据库是MySQL的,场景是插入多,查询少,比例与插入量差不多,但是如果要查的话,那看下面的sql:select count(*) from login_log where login_time >='2018-07-24 00:00:00' and login_time <='2018-07-24 23:59:59';这个sql至少执行了3.8秒。应该是可以优化的。

要查该表的背景

  1. 某员工违规操作,要查日志,什么时间登录的,登录到哪个IP地址。
  2. 某员工最近一个月通过inpass登录系统的日志。
    这种场景非常少,但是在单表这么多数据量的情况下一条sql执行下来估计也需要不少时间。

可以借用分表的设计想法,(分库就不用了哈,没那么多复杂的需求),以年为单位,预先设计3张表loginlog2018(已初始化到线上),loginlog2019(已初始化到线上),loginlog2020(未初始化到线上).假设每张表存储不超过1KW条数据,以年为单位记,即可满足未来3年内的日志增长量。同时满足所有日志可查。

按上述方案执行之后,到现在已经往loginlog2018表写入了480w登录日志数据了,其实已经到了需要写新表的时候了,此时也接到了需要对inpass进行内审安全合规性的改造,用户需要对登录日志进行查询。新的需求需要对登录过程中的所有操作进行记录,包括修改密码等环节。而原来的登录日志记录仅仅记录登录成功的场景,因此如果再写入数据库就不合适了。

此时需要进行新的改造,由于inpass只是提供单点登录和修改密码的功能,没有查询界面,如果给用户新开列表查询界面也不合适。经过和领导沟通,有如下操作和方案

  1. 将登录日志以埋点的方式接入公司的日志平台进行上报。
  2. inpass服务端使用线程池异步写日志(不阻塞登录主流程)
  3. 由于接入日志平台之后日志平台可以提供查询界面,所以inpass的改造仅仅在用日志埋点的接入,但是为了保障整个接入过程顺利还进行了双写的操作。
  4. 就是一方面将登录日志上报到日志平台,一方面写入数据库,此时需要新建表,因为新增了几个记录项,不适合在原有表中扩展,新表为loginlog2020.
  5. 第4步的操作也为了保障在接入过程中数据可以冗余保留,如果日志平台确实能满足用户查询需求,我们可以很快下掉写数据库的逻辑。
    架构设计@工程设计@服务稳定性之路
发布了159 篇原创文章 · 获赞 69 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/u010504064/article/details/103349010