连续登陆活动,或许大家都不会陌生,简单理解就是用户连续登陆了多少天之后,系统就会送一些礼品给相应的用户。最常见的

莫过于游戏和商城这些。游戏就送游戏币之类的东西,商城就送一些礼券。正值国庆,应该也有不少类似的活动。

  下面就对这个的实现提供两个思路,并提供解决方案。

  思路1(以用户为维度):

  连续登陆活动,必然是要求连续登陆,不能有间隔。用1表示登陆,0表示没有登陆,这样我们可以为每个用户创建一个key去存储

他的登陆情况,就可以得到类似这样的一个二进制序列:1110111,如果是7个1,就表示连续7天,如果不是7个1就表示没有连续登

陆7天。所以就能实现这个登陆活动的要求了。

  思路2(以天数为维度):

  一天之内,用户要么是登陆过,要么是没有登陆过。同样的用1来表示登陆过,用0表示没有登陆过。假设我们连续登陆的活动是2天,

同时有3个用户,那么就要有2个key去存储这3个用户的登陆信息,这样就会得到类似这样的两个二进制序列:101(key1),111(key2)。

此时,对这两个key的每一位都进行逻辑与运算,就会得到101,就表明,用户1和用户3连续登陆了两天。从而达到活动的要求。

  之前在string的基础教程中曾经说过关于二进制的相关操作会用一个简单的案例来给大家讲解,现在是兑现这个诺言的时候了。

  下面就简单模拟一下国庆7天假期连续登陆七天的活动。

  方案1 :以用户为维度

  先为每个用户创建一个key(holiday:用户标识),对于我们的例子来说,每个key就会有7位二进制位。这时key会有这样的结构
  

  这时我们就会得到每个用户对应的二进制序列,然后就可以用bitcount命令去得到key含有的1的个数。如果等于7,就是连续登陆了

七天。这样就可以在第七天用户登陆的时间去处理了是否发送礼品了。处理的逻辑是十分简单的。控制器简单逻辑如下:

          [HttpPost]
public IActionResult LoginForEveryone()
{
Random rd = new Random();
var tran = _redis.GetTransaction();
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
string activity_key = string.Format("holiday:{0}", j.ToString());
// login 1(true) other 0(false)
if (rd.Next(,) > )
{
tran.StringSetBitAsync(activity_key, i, true);
}
}
}
tran.ExecuteAsync(); List<int> res = new List<int>();
for (int i = ; i < ; i++)
{
string activity_key = string.Format("holiday:{0}", i.ToString());
//7 days
if (_redis.BitCount(activity_key) == )
{
res.Add(i);
}
}
return Json(new { code = "", data = res, count = res.Count });
}
  在这里还是用随机数的方法来模拟数据。主要操作有两个,一个是模拟登陆后把当天对应的偏移设置为1(true),另一个是取出用户

登陆的天数。这是一次性模拟操作,与正常情况的登陆操作还是有些许不同的。大致如下:

         [HttpPost]
public IActionResult LoginForEveryone()
{
//1.login and get the identify of user
//2.get the Current day and write to redis
string activity_key = string.Format("holiday:{0}", "identify of user");
_redis.SetBit(activity_key, currend day, true);
//3.send gift
if(currend day==&& _redis.BitCount(activity_key)==)
{
send gift
}
return ...;
}

  回到我们模拟的情况,在界面展示时,模拟登陆后会显示累计登陆用户的id。

  <script id="everyoneTpl" type="text/html">
<span>total:{{count}}</span>
<ul>
{{each data as item}}
<li>
{{item}}
</li>
{{/each}}
</ul>
</script>
<script>
$(function () {
$("#btn_everyone").click(function () {
$.ajax({
url: "/Holiday/LoginForEveryone",
dataType: "json",
method:"post",
success: function (res) {
if (res.code == "000") {
var html = template('everyoneTpl', res);
$("#div_everyone").html(html);
}
}
})
});
})
</script>
  
  下面来看看效果:
 
  

  演示中:38、103、234、264、412、529这6位用户将得到连续登陆7天的礼品。
 
 
  方案2 :以天数为维度 
  既然是以天数为维度,那么就要定义7个redis的key用来当作每天的登陆记录,类似:
  

  这样的话就要让我们的用户标识是数字才行,如果是用guid做的用户标识就要做一定的处理将其转化成数字,这样方便我们

在给用户设置是否登陆。现在假设我们的用户标识是从1~1000。用户标识对应的就是在key中的偏移量。这时我们就会得到每天

对应的二进制序列,然后就可以用bitop命令去得到逻辑与运算之后的key/value。如果这个key对应偏移量(用户标识)是1,就是

连续登陆了七天,处理的逻辑是十分简单的。控制器简单逻辑如下:

         [HttpPost]
public IActionResult LoginForEveryday()
{
var tran = _redis.GetTransaction(); for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
//i day,j userId
SetBit(i, j, tran);
}
}
tran.Execute();
//get the result
_redis.BitOP(_bitWise, _res, _redisKeys.ToArray());
IList<int> res = new List<int>();
for (int i = ; i < ; i++)
{
if (_redis.GetBit(_res, i) == true)
{
res.Add(i);
}
}
return Json(new { code = "", data = res, count = res.Count });
} private void SetBit(int day, int userId, StackExchange.Redis.ITransaction tran)
{
switch (day)
{
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_first, userId, true);
}
else
{
tran.StringSetBitAsync(_first, userId, false);
}
break;
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_second, userId, true);
}
else
{
tran.StringSetBitAsync(_second, userId, false);
}
break;
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_thrid, userId, true);
}
else
{
tran.StringSetBitAsync(_thrid, userId, false);
}
break;
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_fourth, userId, true);
}
else
{
tran.StringSetBitAsync(_fourth, userId, false);
}
break;
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_fifth, userId, true);
}
else
{
tran.StringSetBitAsync(_fifth, userId, false);
}
break;
case :
if (_rd.Next(, ) > )
{
tran.StringSetBitAsync(_sixth, userId, true);
}
else
{
tran.StringSetBitAsync(_sixth, userId, false);
}
break;
case :
if (_rd.Next(, ) >)
{
tran.StringSetBitAsync(_seventh, userId, true);
}
else
{
tran.StringSetBitAsync(_seventh, userId, false);
}
break;
default:
break;
}
}
  
  前台的处理与方案一的一模一样,所以就不贴代码了。下面来看看效果图。
 
  

   可能光看效果图没太大意义,还是要看一下redis中的数据来验证一下的。图中取了76和991这两个用户标识(偏移量)来验证。
  

  可以看到76和991这两个偏移量(用户标识)对应的二进制位是1,也验证了其连续登陆了7天。当然,更多的明细数据也贴出来了

一大堆16进制的东西,有兴趣可以去转换成二进制试试。
 
  对这两种方案简单的总结一下:
 
方案 优点 缺点
以用户为维度 1.可以无缝对接,无论用户标识是数字还是其他
2.key对应的数据较少便于观察
随着用户数量的增长,要管理的key会越来越多
以天数为维度 1.有确定数量的key,方便管理
2.key对应的基数大
1.偏移量可能需要根据实际情况处理
2.数据查看不是很清晰
 

  可至于实际中用那种方案更合适,要根据情况来选择。用户量少的时候,可以用第一种方案,也可以用第二种方案,当用户量很大的时候,

建议采用第二种方案,毕竟只要用户数量没有超过43亿,就不会超出其二进制位数的限制。是可以比较放心使用的。

Redis简单案例(三) 连续登陆活动的简单实现的更多相关文章

  1. Python三次登陆

    题目:Python实现三次登陆 不要急于马上把三次登陆写出来,一定要将复杂的程序简单化,必须一步一步地去扩展,这样才保证不会出错. 步骤一:实现简单的一次登陆 # 事先定义 user = 'dark_ ...

  2. 大数据学习day25------spark08-----1. 读取数据库的形式创建DataFrame 2. Parquet格式的数据源 3. Orc格式的数据源 4.spark_sql整合hive 5.在IDEA中编写spark程序(用来操作hive) 6. SQL风格和DSL风格以及RDD的形式计算连续登陆三天的用户

    1. 读取数据库的形式创建DataFrame DataFrameFromJDBC object DataFrameFromJDBC { def main(args: Array[String]): U ...

  3. SpringDataRedis操作Redis简单案例

    Jedis Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用.可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis.SRP等等,推荐使 ...

  4. 用redis统计大量用户的登陆情况[只判断是否活跃]

    有这样的一个场景需求:有上亿的用户,要统计这批用户的登陆情况,例如一周连续登陆,连续三天是是否登陆,一周活跃天数等用户 存在的挑战 数据如何尽可能用小的空间存储 如何能快速获取指定的数据 如果使用文件 ...

  5. 案例学python——案例三:豆瓣电影信息入库

    闲扯皮 昨晚给高中的妹妹微信讲题,函数题,小姑娘都十二点了还迷迷糊糊.今天凌晨三点多,被连续的警报声给惊醒了,以为上海拉了防空警报,难不成地震,空袭?难道是楼下那个车主车子被堵了,长按喇叭?开窗看看, ...

  6. Java基础之UDP协议和TCP协议简介及简单案例的实现

    写在前面的废话:马上要找工作了,做了一年的.net ,到要找工作了发现没几个大公司招聘.net工程师,真是坑爹呀.哎,java就java吧,咱从头开始学呗,啥也不说了,玩命撸吧,我真可怜啊. 摘要: ...

  7. zabbix生产环境案例(三)

    生产环境案例(三) 链接:https://pan.baidu.com/s/1q5YwJMTcZLcS5OQ0iOu44A 提取码:8gdi 复制这段内容后打开百度网盘手机App,操作更方便哦 1. Z ...

  8. 搭建一个简单struts2框架的登陆

    第一步:下载struts2对应的jar包,可以到struts官网下载:http://struts.apache.org/download.cgi#struts252 出于学习的目的,可以把整个完整的压 ...

  9. asp:第三平台登陆

    第三平台登陆接口申请网址: http://open.51094.com/ 文档: 第三方合作登录平台使用说明 为方便更多的开发朋友,本人特将当前市面上所有支持第三方联合登录的接口集为一体,以前需要多次 ...

随机推荐

  1. 学习ASP.NET Core, 怎能不了解请求处理管道[4]: 应用的入口——Startup

    一个ASP.NET Core应用被启动之后就具有了针对请求的处理能力,而这个能力是由管道赋予的,所以应用的启动同时意味着管道的成功构建.由于管道是由注册的服务器和若干中间件构成的,所以应用启动过程中一 ...

  2. Linux常用指令指南,终端装逼利器

    最近搞了台Macbook Pro,就学习了一下Linux命令,在网上查了些资料,看了本书叫<快乐的 Linux 命令行>,里面涉及到了各个方面的命令. 在此将常用的整理出来,以备将来使用. ...

  3. spider RPC入门指南

    本部分将介绍使用spider RPC开发分布式应用的客户端和服务端. spider RPC中间件基于J2SE 8开发,因此需要确保服务器上安装了JDK 8及以上版本,不依赖于任何额外需要独立安装和配置 ...

  4. js刷新页面方法大全

    如何实现刷新当前页面呢?借助js你将无所不能. 1,reload 方法,该方法强迫浏览器刷新当前页面.语法:location.reload([bForceGet])   参数: bForceGet, ...

  5. iOS 数据存储之SQLite3的使用

    SQLite3是iOS内嵌的数据库,SQLite3在存储和检索大量数据方面非常有效,它使得不必将每个对象都加到内存中.还能够对数据进行负责的聚合,与使用对象执行这些操作相比,获得结果的速度更快. SQ ...

  6. Android—自定义开关按钮实现

    我们在应用中经常看到一些选择开关状态的配置文件,做项目的时候用的是android的Switch控件,但是感觉好丑的样子………… 个人认为还是自定义的比较好,先上个效果图:

  7. Orcale 三层嵌套分页代码

    select * from( select emp.*,rownum a from ( select * from emp ) emp where rownum<7) where a>3

  8. IOS开发之—— 在AFN基础上进行的网络请求的封装

    网络请求的思路:如果请求成功的话AFN的responseObject就是解析好的. 1发送网络请求:get/post/或者别的 带上URL,需要传的参数 2判断后台网络状态码有没有请求成功: 3 请求 ...

  9. 惊心动魄的一上午,感谢eclipse 的文件恢复功能

    昨晚倒腾了半天android 的程序,夜里三点多了,不争气的笔记本由于太热,突然熄火.话说就在昨天还在想着一定要把东西放到svn上,防止文档找不到或者笔记本丢失带来的严重后果.呵呵,就是这么想着,今天 ...

  10. 延迟求值-如何让Lo-Dash再提速x100?

    「注释」作者在本文里没有说明这么一个事实: 目前的版本Lo-Dash v2.4.1并没有引入延迟求值的特性,Lo-Dash 3.0.0-pre中部分方法进行了引入,比如filter(),map(),r ...