Redis实现唯一计数的3种方法分享

唯一计数是网站系统中十分常见的一个功能特性,例如网站需要统计每天访问的人数 unique visitor (也就是 UV)。计数问题很常见,但解决起来可能十分复杂:一是需要计数的量可能很大,比如大型的站点每天有数百万的人访问,数据量相当大;二是通常还希望扩展计数的维度,比如除了需要每天的 UV,还想知道每周或每月的 UV,这样导致计算十分复杂。

在关系数据库存储的系统里,实现唯一计数的方法就是 select count(distinct <item_id>),它十分简单,但是如果数据量很大,这个语句执行是很慢的。用关系数据库另外一个问题是插入数据性能也不高。

Redis 解决这类计数问题得心应手,相比关系数据库速度更快,消耗资源更少,甚至提供了 3 种不同的方法。

1.基于 set

Redis 的 set 用于保存唯一的数据集合,通过它可以快速判断某一个元素是否存在于集合中,也可以快速计算某一个集合的元素个数,另外和可以合并集合到一个新的集合中。涉及的命令如下:

代码如下:

SISMEMBER key member  # 判断 member 是否存在
SADD key member  # 往集合中加入 member
SCARD key   # 获取集合元素个数

基于 set 的方法简单有效,计数精确,适用面广,易于理解,它的缺点是消耗资源比较大(当然比起关系数据库是少很多的),如果元素个数很大(比如上亿的计数),消耗内存很恐怖。

2.基于 bit

Redis 的 bit 可以用于实现比 set 内存高度压缩的计数,它通过一个 bit 1 或 0 来存储某个元素是否存在信息。例如网站唯一访客计数,可以把 user_id 作为 bit 的偏移量 offset,设置为 1 表示有访问,使用 1 MB的空间就可以存放 800 多万用户的一天访问计数情况。涉及的命令如下:

代码如下:

SETBIT key offset value  # 设置位信息
GETBIT key offset        # 获取位信息
BITCOUNT key [start end] # 计数
BITOP operation destkey key [key ...]  # 位图合并

基于 bit 的方法比起 set 空间消耗小得多,但是它要求元素能否简单映射为位偏移,适用面窄了不少,另外它消耗的空间取决于最大偏移量,和计数值无关,如果最大偏移量很大,消耗内存也相当可观。

3.基于 HyperLogLog

实现超大数据量精确的唯一计数都是比较困难的,但是如果只是近似的话,计算科学里有很多高效的算法,其中 HyperLogLog Counting 就是其中非常著名的算法,它可以仅仅使用 12 k左右的内存,实现上亿的唯一计数,而且误差控制在百分之一左右。涉及的命令如下:

代码如下:

PFADD key element [element ...]  # 加入元素
PFCOUNT key [key ...]   # 计数

这种计数方法真的很神奇,我也没有彻底弄明白,有兴趣可以深入研究相关文章。

redis 提供的这三种唯一计数方式各有优劣,可以充分满足不同情况下的计数要求。

(0)

相关推荐

  • Ubuntu下安装redis的2种方法分享

    前言 redis是目前公认的速度最快的基于内存的键值对数据库,但redis的缺点也非常明显,仅提供最基本的hash set, list, sorted set等基于数据类型,不分表,没有schema,没有索引,没有外键,缺少int/date等基本数据类型,多条件查询需要通过集合内联(sinter,zinterstore)和连接间接实现,操作不便,开发效率低,可维护性不佳: 因此一般不将其视为完整的数据库单独使用,很多网站将redis作为高速缓存和session状态存储层,然后再与其他数据库搭配使

  • redis常用命令、常见错误、配置技巧等分享

    1. redis查看当前所有的key 复制代码 代码如下: KEYS * 2. 查看当前redis的配置信息 复制代码 代码如下: CONFIG GET * 3. MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis

  • redis实现多进程数据同步工具代码分享

    复制代码 代码如下: package com.happyelements.odin.util; import static com.google.common.base.Preconditions.checkNotNull; import org.jetbrains.annotations.NotNull; import com.happyelements.odin.jedis.JedisClient;import com.happyelements.rdcenter.commons.util.

  • PHP 使用redis简单示例分享

    示例很简单,注释里也都做了说明,这里就不多废话了. 复制代码 代码如下: <?php /*从平台获取数据库名*/ $dbname = ""; /*从环境变量里取host,port,user,pwd*/ $host = ''; $port = ''; $user = ''; $pwd = ''; try {     /*建立连接后,在进行集合操作前,需要先进行auth验证*/     $redis = new Redis();     $ret = $redis->conne

  • RedisRepository 分享和纠错

    一.   写在前面 毕业工作后,今天终于能回家了,回想了一些这半年来所做的内容,总是觉得还停留在那么基础的水平 ,在解决各种问题的过程中,自己的创新思路比较少,靠搜索来的比较多 .不想做16年的总结了 ,希望17年能学更多的我爱的技术,看更多的开源代码,能学到更多的设计思想和代码思路,能再更新这两年来对代码的理解. 这篇分享,主要是弥补我之前RedisRepository的不足. 半年前由于我StackExchange.Redis文档阅读不足,所分享的RedisRepository有所错误.下面

  • php操作redis缓存方法分享

    php redis缓存操作 <?php /** * Redis缓存操作 * @author hxm * @version 1.0 * @since 2015.05.04 */ class RCache extends Object implements CacheFace { private $redis = null; //redis对象 private $sId = 1; //servier服务ID private $con = null;//链接资源 /** * 初始化Redis * *

  • Linux下Redis数据库的安装方法与自动启动脚本分享

    安装Redis  (1) 下载Redis wget http://redis.googlecode.com/files/redis-2.2.11.tar.gz tar xzvf redis-2.2.11.tar.gz  (2) 编译并安装Redis make && make install  (3) 复制并修改配置文件 cp redis.conf /etc/redis.conf vi /etc/redis.conf 注意修改以下几项: daemonize yes loglevel warn

  • 详解StackExchange.Redis通用封装类分享

    前两天朋友问我,有没有使用过StackExchange.Redis,问我要个封装类,由于之前都是使用ServiceStack.Redis,由于ServiceStack.Redis v4版本后是收费版的,所以现在也很有公司都在使用StackExchange.Redis而抛弃ServiceStack.Redis了.其实个人觉得,两个驱动都不错,只是由于ServiceStack.Redis收费导致目前很多公司都是基于V3版本的使用,也有人说V3版本有很多Bug,没有维护和升级,不过至少目前我是没发现B

  • Redis实现唯一计数的3种方法分享

    唯一计数是网站系统中十分常见的一个功能特性,例如网站需要统计每天访问的人数 unique visitor (也就是 UV).计数问题很常见,但解决起来可能十分复杂:一是需要计数的量可能很大,比如大型的站点每天有数百万的人访问,数据量相当大:二是通常还希望扩展计数的维度,比如除了需要每天的 UV,还想知道每周或每月的 UV,这样导致计算十分复杂. 在关系数据库存储的系统里,实现唯一计数的方法就是 select count(distinct <item_id>),它十分简单,但是如果数据量很大,这

  • Redis实现分布式锁的几种方法总结

    Redis实现分布式锁的几种方法总结 分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁. 我们来假设一个最简单的秒杀场景:数据库里有一张表,column分别是商品ID,和商品ID对应的库存量,秒杀成功就将此商品库存量-1.现在假设有1000个线程来秒杀两件商品,500个线程秒杀第一个商品,

  • Redis实现分布式锁的五种方法详解

    目录 1. 单机数据一致性 2. 分布式数据一致性 3. Redis实现分布式锁 3.1 方式一 3.2 方式二(改进方式一) 3.3 方式三(改进方式二) 3.4 方式四(改进方式三) 3.5 方式五(改进方式四) 3.6 小结 在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁. 在分布式架构中,我们同样会遇到数据共享操作问题,本文章使用Redis来解决分布式架构中的数据一致性问题. 1. 单机数据一致性 单机数据一致性架构如下图所示:多个可客户访

  • Redis优雅地实现延迟队列的方法分享

    目录 前言 使用 依赖配置 配置文件 demo代码 执行效果 原理分析 队列创建 生产者 消费者 整个流程 总结思考 前言 工作中常常会遇到这样的场景,如订单到期未支付取消,到期自动续费等,我们发现延迟队列非常适合在这样的场景中使用.常见的延迟队列的优秀实现有rabbitMQ的死信队列,RocketMQ的延迟队列等,但是了有时候项目没有特别的大,没有引入类似的消息中间件,但是了又遇到了特别适合使用延迟队列的场景,我们一般会利用已有的redis实现一个简陋的延迟队列.常见的实现方式有监听过期key

  • python 统计代码耗时的几种方法分享

    时间戳相减 在代码执行前后各记录一个时间点,两个时间戳相减即程序运行耗时. 获取时间戳time.time() import time start_time = time.time() sum = 0 for i in range(100000000):     sum += i print(sum) end_time = time.time() print("耗时: {:.2f}秒".format(end_time - start_time)) 输出: 4999999950000000

  • Java中ArrayList同步的2种方法分享

    目录 方法一:使用Collections.synchronizedList()方法 方法2:使用CopyOnWriteArrayList 向量同步时为什么要使用arrayList? 前言: arrayList 的实现是默认不同步的.这意味着如果一个线程在结构上修改它并且多个线程同时访问它,它必须在外部同步.结构修改意味着从列表中添加或删除元素或显式调整后备数组的大小.改变现有元素的值不是结构修改. 有两种方法可以创建同步Arraylist: 1. Collections.synchronized

  • C语言实现倒置字符串的两种方法分享

    目录 前言 一.题目 二.思路讲解 三.代码实现 四.代码总结 五.第二种方法倒置字符串 六.总结 前言 今天在牛客网上刷了一道题,我感觉挺有意义的,现在我就分享给大家,并且告诉大家解题思路. 一.题目 事例: 输入下列字符: I like beijing. 输出的字符: beijing. like I 二.思路讲解 1.设立一个数组存放输入的字符串 2.将其中每个单词进行逆置 得到 I ekil .gnjieb 3.再将字符串逆置  得到beijing.like I 三.代码实现 1.设立一个

  • iOS获取设备唯一标识的8种方法

    8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可以改变设备的 UDID),移动网络可以利用 UDID 来识别移动设备. 许多开发者把 UDID 跟用户的真实姓名.密码.住址.其它数据关联起来,网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据,同时大部分应用确实在频繁传输 UDID 和私人信息. 为了避免集体诉讼,苹果最终决

  • 用Mootools获得操作索引的两种方法分享

    我们先说一下第一种方法,(此方法为一位QQ好友提供,感谢此人,此人昵称是:exf) 复制代码 代码如下: <body> <ul>Title1 <li>11111</li> </ul> <ul>Title2 <li>22222</li> </ul> <ul>Title3 <li>33333</li> </ul> <ul>Title4 <

随机推荐