分享

基于Redis位图实现系统用户登录统计

 JhouShuai 2018-12-18
1|0需求

  1.  实现记录用户哪天进行了登录,每天只记录是否登录过,重复登录状态算已登录。不需要记录用户的操作行为,不需要记录用户上次登录时间和IP地址(这部分以后需要可以单独拿出来存储)
  2.  区分用户类型
  3.  查询数据需要精确到天

2|0分析


考虑到只是简单的记录用户是否登录,记录数据比较单一查询需要精确到天。以百万用户量为前提,前期考虑了几个方案

2|1使用文件


使用单文件存储:文件占用空间增长速度快,海量数据检索不方便,Map/Reduce操作也麻烦

使用多文件存储:按日期对文件进行分割。每天记录当天日志,文件量过大

2|2使用数据库


不太认同直接使用数据库写入/读取

  1.  频繁请求数据库做一些日志记录浪费服务器开销。
  2.  随着时间推移数据急剧增大
  3.  海量数据检索效率也不高,同时使用索引,易产生碎片,每次插入数据还要维护索引,影响性能

所以只考虑使用数据库做数据备份。

2|3使用Redis位图(BitMap)


这也是在网上看到的方法,比较实用。也是我最终考虑使用的方法,

首先优点:

数据量小:一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。1亿人每天的登陆情况,用1亿bit,约1200WByte,约10M 的字符就能表示。

计算方便:实用Redis bit 相关命令可以极大的简化一些统计操作。常用命令 SETBITGETBITBITCOUNTBITOP

再说弊端:

存储单一:这也算不上什么缺点,位图上存储只是0/1,所以需要存储其他信息就要别的地方单独记录,对于需要存储信息多的记录就需要使用别的方法了

3|0设计


3|1Redis BitMap


Key结构:前缀_年Y-月m_用户类型_用户ID

标准Key:KEYS loginLog_2017-10_client_1001 检索全部:KEYS loginLog_* 检索某年某月全部:KEYS loginLog_2017-10_* 检索单个用户全部:KEYS loginLog_*_client_1001 检索单个类型全部:KEYS loginLog_*_office_* ...

每条BitMap记录单个用户一个月的登录情况,一个bit位表示一天登录情况。

设置用户1001,217-10-25登录:SETBIT loginLog_2017-10_client_1001 25 1
获取用户1001,217-10-25是否登录:GETBIT loginLog_2017-10_client_1001 25
获取用户1001,217-10月是否登录:BITCOUNT loginLog_2017-10_client_1001
获取用户1001,217-10/9/7月是否登录:BITOP OR stat loginLog_2017-10_client_1001 loginLog_2017-09_client_1001 loginLog_2017-07_client_1001 ...

关于获取登录信息,就得获取BitMap然后拆开,循环进行判断。特别涉及时间范围,需要注意时间边界的问题,不要查询出多余的数据

获取数据Redis优先级高于数据库,Redis有的记录不要去数据库获取

Redis数据过期:在数据同步中进行判断,过期时间自己定义(我定义的过期时间单位为“天”,必须大于31)。

在不能保证同步与过期一致性的问题,不要给Key设置过期时间,会造成数据丢失。

上一次更新时间: 2107-10-02 下一次更新时间: 2017-10-09 Redis BitMap 过期时间: 2017-10-05 这样会造成:2017-10-09同步的时候,3/4/5/6/7/8/9 数据丢失

所以我把Redis过期数据放到同步时进行判断  

我自己想的同步策略(定时每周一凌晨同步):

一、验证是否需要进行同步: 1. 当前日期 >= 8号,对本月所有记录进行同步,不对本月之前的记录进行同步 2. 当前日期 < 8号,对本月所有记录进行同步,对本月前一个月的记录进行同步,对本月前一个月之前的所有记录不进行同步 二、验证过期,如果过期,记录日志后删除

3|2数据库,表结构


每周同步一次数据到数据库,表中一条数据对应一个BitMap,记录一个月数据。每次更新已存在的、插入没有的

3|3暂定接口


  1.  设置用户登录
  2.  查询单个用户某天是否登录过
  3.     查询单个用户某月是否登录过
  4.  查询单个用户某个时间段是否登录过
  5.  查询单个用户某个时间段登录信息
  6.  指定用户类型:获取某个时间段内有效登录的用户
  7.  全部用户:获取某个时间段内有效登录的用户

4|0Code


TP3中实现的代码,在接口服务器内部库中,Application\Lib\

  ├─LoginLog

  │ ├─Logs 日志目录,Redis中过期的记录删除写入日志进行备份

  │ ├─LoginLog.class.php 对外接口

  │ ├─LoginLogCommon.class.php 公共工具类

  │ ├─LoginLogDBHandle.class.php 数据库操作类

  │ ├─LoginLogRedisHandle.class.php Redis操作类




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多