Chapter 2. Advanced Data Types (Earning a Black Belt)

本章介绍数据类型:Set, Sorted Set, Bitmap, HyperLogLog。

Sets

Set是未排序的,无重复值的String集合。内部一年hash table实现,因此如成员的增删查都是固定时间,O(1)数量级。

如果Set的成员都是Integer,内存还可以优化。

一个Set最多存储232-1成员,约40亿。

Set的使用场景包括:
* 数据过滤:如从某城市出发到另一城市的航班
* 数据分组: 查看近似产品的用户,如Amazon.com的推荐系统
* 成员检查: 某用户是否在白名单/黑名单中

推荐阅读:

使用Set过滤数据: http://robots.thoughtbot.com/redis-set-intersection-using-sets-to-filter-data.

这是一个航班时刻表查询程序,有多个集合,所有航班集合,按出发城市的集合,按到达城市的集合。然后通过这些集合的操作,得到所需的结果,如指定发到站的航班,指定出发时间段的航班等。

文章中有段话说的很好:

Redis is a fantastic lightweight key-value store. It also acts as a data structure server for storing data types like lists, sets, and hashes.

Set examples with redis-cli

下面就是些集合操作了,交集,并集,差集…

SADD可为Set添加一个或多个成员,返回添加的成员数目,如成员已存在则忽略。

$ redis-cli
127.0.0.1:6379> SADD user:john:favorite_fruits "Apple" "Peach" "Banana" "Lemon"
(integer) 4
127.0.0.1:6379> SADD user:danny:favorite_fruits "Apple" "Melon" "Lemon"
(integer) 3
127.0.0.1:6379> SADD user:rose:favorite_fruits "Apple" "Melon" "Pear"
(integer) 3

SINTER返回多个个Set的交集:

127.0.0.1:6379> SINTER user:john:favorite_fruits user:danny:favorite_fruits
1) "Lemon"
2) "Apple"
127.0.0.1:6379> SINTER user:john:favorite_fruits user:danny:favorite_fruits user:rose:favorite_fruits
1) "Apple"

SDIFF返回多个Set的差集(存在于第一个Set,在余下Set中不存在),结果和key的顺序有关:

127.0.0.1:6379> SDIFF user:john:favorite_fruits user:danny:favorite_fruits
1) "Peach"
2) "Banana"
127.0.0.1:6379> SDIFF user:danny:favorite_fruits user:john:favorite_fruits
1) "Melon"
127.0.0.1:6379> SDIFF user:john:favorite_fruits user:danny:favorite_fruits user:rose:favorite_fruits
1) "Peach"
2) "Banana"

SUNION返回多个Set的并集,并集中没有重复值

127.0.0.1:6379> SUNION user:danny:favorite_fruits user:john:favorite_fruits
1) "Melon"
2) "Lemon"
3) "Apple"
4) "Peach"
5) "Banana"
127.0.0.1:6379> SUNION user:john:favorite_fruits user:danny:favorite_fruits user:rose:favorite_fruits
1) "Pear"
2) "Lemon"
3) "Apple"
4) "Peach"
5) "Banana"
6) "Melon"

SRANDMEMBER返回Set中的随机成员。

127.0.0.1:6379> SRANDMEMBER  user:john:favorite_fruits
"Banana"

SISMEMBER判断成员是否属于Set,1表示True,0表示False

127.0.0.1:6379> SISMEMBER user:john:favorite_fruits "Banana"
(integer) 1
127.0.0.1:6379> SISMEMBER user:john:favorite_fruits "banana"
(integer) 0

SREM从Set中删除成员,SCARD返回集合的成员数(cardinality)

127.0.0.1:6379> SREM user:john:favorite_fruits "Banana"
(integer) 1
127.0.0.1:6379> SISMEMBER user:john:favorite_fruits "Banana"
(integer) 0
127.0.0.1:6379> SCARD user:john:favorite_fruits
(integer) 3

SMEMBERS返回集合的所有成员

127.0.0.1:6379> SMEMBERS user:john:favorite_fruits
1) "Peach"
2) "Lemon"
3) "Apple"

Building a deal tracking system

Yipit, Groupon, 和 LivingSocial每天都给用户发送邮件,其中包括折扣券等用户感兴趣的内容,发送的内容(deal)与用户的地区和偏好相关。
在本例中, 有两个Set,key是dealID,values是Sets,包括需要发送此DealID的userID列表。

Sorted Sets

Sorted Set与Set类似, 只不过每个成员多了个score,并根据其排序。和Set一样,成员不允许重复,但score可以一样。

Sorted Set 操作不如Set快,因为需要比较score, Sorted Set成员的增删改运行时间为O(log(N)), N为Sorted Set的成员数。Sorted Set的内部实现为:

A skip list with a hash table. A skip list is a data structure that allows fast search within an ordered sequence of elements.
A ziplist, based on the zset-max-ziplist-entries and zset-max-ziplist-value configurations.

Sorted Sets可用于:
* 构建实时客服等待队列
* 在线游戏积分榜,显示排名靠前的选手和积分,或你的队友的积分
* 单词拼写自动完成系统

Sorted Set examples with redis-cli ###

ZADD:增加一个或多个成员到Sorted Set, 如果field相同,score不同,则更新score

$ redis-cli
127.0.0.1:6379> ZADD math 99 john
(integer) 1
127.0.0.1:6379> ZADD math 100 john
(integer) 0127.0.0.1:6379> ZADD math 99 rose
(integer) 1
127.0.0.1:6379> ZADD math 98 richard 96 tom
(integer) 2
127.0.0.1:6379> ZADD math 99 jean
(integer) 1

由于每一个成员由一个String和一个score组成,排序也有两个标准,如果score相同,则按字符顺序排序String.

从Sorted Set中取某区间的数据并排序的命令包括:ZRANGE, ZRANGEBYLEX, ZRANGEBYSCORE, ZREVRANGE, ZREVRANGEBYLEX, and ZREVRANGEBYSCORE。
这些命令都需要指定起始index, key,唯一的区别是排序结果不同。

127.0.0.1:6379> ZRANGE math 0 -1
1) "tom"
2) "richard"
3) "jean"
4) "rose"
5) "john"
127.0.0.1:6379> ZREVRANGE math 0 -1
1) "john"
2) "rose"
3) "jean"
4) "richard"
5) "tom"

也可以指定WITHSCORES参数,让结果显示score

127.0.0.1:6379> ZREVRANGE math 0 -1 WITHSCORES1) "john"2) "100"3) "rose"4) "99"5) "jean"6) "99"7) "richard"8) "98"9) "tom"
10) "96"

ZREM删除Sorted Set成员:

127.0.0.1:6379> ZREM math richard
(integer) 1
127.0.0.1:6379> ZREVRANGE math 0 -1
1) "john"
2) "rose"
3) "jean"
4) "tom"

可使用ZSCORE和ZRANK/ZREVRANK得到某成员在Sorted Set中的排名和积分, 排名最低的返回0

127.0.0.1:6379> ZSCORE math rose
"99"
127.0.0.1:6379> ZRANK math rose
(integer) 2
127.0.0.1:6379> ZREVRANK math rose
(integer) 1

查看key的类型和Sorted Set相关命令

127.0.0.1:6379> TYPE math
zset
127.0.0.1:6379> help @sorted_set

Building a leaderboard system for an online game

此例较简单,略过。

Bitmaps

Bitmap (bit arrays 或 bitsets)实际上针对String的一些bit操作,本质上不算数据类型。Redis提供了一系列Bitmap操作。

127.0.0.1:6379> type visits:2015-01-01
string
127.0.0.1:6379> help setbitSETBIT key offset valuesummary: Sets or clears the bit at offset in the string value stored at keysince: 2.2.0group: string

Bitmap是存储了0或1的bit序列,存储效率很高(因其紧凑,占用空间少),支持快速查询。可存储232 bit。

Bitmap非常适合于实时分析,例如某用户是否做了某操作,多少用户做了这些操作等。

Bitmap examples with redis-cli

SETBIT设置offset为index的值为0或1。index从0开始。

127.0.0.1:6379> SETBIT visits:2015-01-01 10 1
(integer) 0
127.0.0.1:6379> SETBIT visits:2015-01-01 15 1
(integer) 0
127.0.0.1:6379> SETBIT visits:2015-01-02 10 1
(integer) 0
127.0.0.1:6379> SETBIT visits:2015-01-02 11 1
(integer) 0

GETBIT获取某一位置的值。

127.0.0.1:6379> GETBIT visits:2015-01-01 10
(integer) 1
127.0.0.1:6379> GETBIT visits:2015-01-02 15
(integer) 0

BITCOUNT计算Bitmap中1的个数。

127.0.0.1:6379> BITCOUNT visits:2015-01-01
(integer) 2
127.0.0.1:6379> BITCOUNT visits:2015-01-02
(integer) 2

BITOP执行bitmap操作,如OR, AND, XOR, NOT,并将结果存入目标key中。

127.0.0.1:6379> BITOP OR total_users visits:2015-01-01 visits:2015-01-02
(integer) 2
127.0.0.1:6379> BITCOUNT total_users
(integer) 3

彩票的场景也比较适合使用Bitmap,例如对于六合彩,可以统计你的选择和开奖结果中相同的号码个数

127.0.0.1:6379> SETBIT lottery:select 10 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:select 12 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:select 14 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:select 19 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:select 22 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:select 26 1
(integer) 0
127.0.0.1:6379> bitcount lottery:select
(integer) 6127.0.0.1:6379> SETBIT lottery:result 6 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:result 7 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:result 10 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:result 14 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:result 22 1
(integer) 0
127.0.0.1:6379> SETBIT lottery:result 24 1
(integer) 0127.0.0.1:6379> BITOP AND lottery:same lottery:result lottery:select
(integer) 4
127.0.0.1:6379> bitcount lottery:same
(integer) 3

Building web analytics

本例计算网站在某时间段内的访问量,并列举出这些用户。此应用只能显示是否访问,但访问了多少次无法得到。
本例中key为’visits:daily:’ + date,userID为数字型,表示index
计算访问用户的总数比较简单,但列举哪些用户进行了访问比较麻烦,需要遍历整个Bitmap。

HyperLogLogs

严格来说,HyperLogLog不是一种数据类型,而是一种算法,可以估算出Set中的唯一元素的数量,计算速度快,消耗内存少,但注意是估算,尽管准确率较高,还是会有误差。

关于HyperLogLog算法的描述参见: The analysis of a near-optimal cardinality estimation algorithm (Philippe Flajolet, Éric Fusy, Olivier Gandouet, and Frédéric Meunier)

HyperLogLogs就三个命令: PFADD, PFCOUNT, and PFMERGE。前缀PF表示算法的发明人Philippe Flajolet,他于2011年3月去世。

HyperLogLogs的优缺点如下:

Usually, to perform unique counting, you need an amount of memory proportional to the number of items in the set that you are counting. HyperLogLogs solve these kinds of problems with great performance, low computation cost, and a small amount of memory. However, it is important to remember that HyperLogLogs are not 100 percent accurate. Nonetheless, in some cases, 99.19 percent is good enough.

HyperLogLogs可用于:
* 访问网站的用户数
* 在特定时间段在网站搜索的不同的词语数。
* 用户使用的不同的hashtags数
* 书中出现的不同的单词数

Counting unique users – HyperLogLog versus Set

场景为统计每小时内访问某网页的不同用户数,我们用HyperLogLog和Set做个比较。
用户ID用32字节的UUID表示,每一个小时需要一个key,例如visit:date:hour,每天需要24个key,每月720个key。
假设每小时有100000个不同用户的访问,则对于Set,每天需要100000 × 32 × 24 = 76.8MB,每月需要2.25GB。
而HyperLogLog存100000个用户的访问最多需要12kB,这样对应的每天需要288KB,每月需要8.4MB。但问题是:为何HyperLogLog只用12k? 难道是12K=12 x 1024 x 8 约等于100000?

HyperLogLogs examples with redis-cli

PFADD可以添加一个或多个唯一值到HyperLogLog。

$ redis-cli
127.0.0.1:6379> PFADD visits:2015-01-01 "carl" "max" "hugo" "arthur"
(integer) 1
127.0.0.1:6379> PFADD visits:2015-01-01 "max" "hugo"
(integer) 0
127.0.0.1:6379> PFADD visits:2015-01-02 "max" "kc" "hugo" "renata"
(integer) 1

PFCOUNT可以计算单个集合中唯一值的个数,也可以对多个集合进行UNION操作:

127.0.0.1:6379> PFCOUNT visits:2015-01-01
(integer) 4
127.0.0.1:6379> PFCOUNT visits:2015-01-02
(integer) 4
127.0.0.1:6379> PFCOUNT visits:2015-01-01 visits:2015-01-02
(integer) 6
127.0.0.1:6379> type visits:2015-01-01
string

PFMERGE将集合合并的结果存入目标key。

127.0.0.1:6379> PFMERGE visits:total visits:2015-01-01 visits:2015-01-02
OK
127.0.0.1:6379> PFCOUNT visits:total
(integer) 6

Counting and retrieving unique website visits

Summary

本章讲述了高级数据类型: Sets, Sorted Sets, Bitmaps, 和 HyperLogLogs. 并演示了它们在实际场景中的运用,这些都是Redis的优势,但选择合适的数据类型对应具体的问题也很重要。

Redis Essentials 读书笔记 - 第二章: Advanced Data Types (Earning a Black Belt)相关推荐

  1. Redis Essentials 读书笔记 - 第九章: Redis Cluster and Redis Sentinel (Collective Intelligence)

    Chapter 9. Redis Cluster and Redis Sentinel (Collective Intelligence) 上一章介绍了复制,一个master可以对应一个或多个slav ...

  2. Redis Essentials 读书笔记 - 第一章: Getting Started (The Baby Steps)

    Chapter 1. Getting Started (The Baby Steps) Redis是在内存中运行的NoSQL key-value数据库. Redis的优势除了内存的高性能外,还有其支持 ...

  3. 《计算传播学导论》读书笔记——第二章文本分析简介

    <计算传播学导论>读书笔记--第二章文本分析简介 第一节 文本分析研究现状 常用文本挖掘技术 第二节 文本分析与传播学研究 (一)为什么文本挖掘技术逐渐受到传播学者的关注 (二)不同文本分 ...

  4. In-memory Computing with SAP HANA读书笔记 - 第二章:SAP HANA overview

    本文为In-memory Computing with SAP HANA on Lenovo X6 Systems第二章SAP HANA overview的读书笔记. 本章最重要的部分是SAP HAN ...

  5. C++ Primer Plus读书笔记第二章

    自学了一段时间的C++打算还是要系统的整理一下一些知识点,让学习思路更清晰,不然老是学一点忘一点,这个读书笔记用来记录这段时间对C++ Primer Plus一书中知识点的记录,尽量会写的详细一点.直 ...

  6. 《软件测试经验与教训》读书笔记---第二章

    <软件测试经验与教训>读书笔记--目录 第一章 测试员的角色 第二章 按测试员的方式思考 第三章 测试手段 第四章 程序错误分析 第五章 测试自动化 第六章 测试文档 第七章 与程序员交互 ...

  7. 计算机网络(第五版 作者:AndrewS.Tanenbaum David J.Wetherall 清华大学出版社)读书笔记----第二章的学习

    计算机网络第二章--物理层读书笔记 1.物理层是网络的技术设置,物理层的材质和带宽决定了最大的传输速率. 2.传输介质的分类:引导性(有线介质)和非引导性(无线介质). (1)有线介质:磁介质.双绞线 ...

  8. PHP核心技术与最佳实践 读书笔记 第二章 面向对象的设计原则

    2019独角兽企业重金招聘Python工程师标准>>> 第二章 面向对象的设计原则 2.1 面向对象设计的五大原则 单一职责原则 接口隔离原则 开放-封闭原则 替换原则 依赖倒置原则 ...

  9. 《辛雷学习方法》读书笔记——第二章 心态

    第二章 心态   (1)保持良好心态:学习时保持良好心态,你才能比较容易入门.深入掌握知识.灵活运用知识.学习时始终保持着轻松愉悦振奋的心情,你就容易产生学习心得,更容易灵活运用. (2)爱情对心态影 ...

最新文章

  1. 电网机巡智能管控平台渗透测试经历
  2. winfrom里面使用类似于table的合并的控件_Flink集成iceberg数据湖之合并小文件
  3. python 向量元素判断_python;计算向量的元素
  4. python numpy.random模块中提供啦大量的随机数相关的函数
  5. 最全面试考点与面试技巧,大厂面经合集
  6. [css] 你有使用过字体图标吗?它有什么好处?
  7. mysql time 5分钟_MySQL 使用 PV 和 PVC 每天5分钟玩转 Docker 容器技术(154)
  8. 【转】TranslateAnimation详解
  9. 软件绿色联盟开发者大会惊喜不断,今日还有重磅议程!
  10. 视频教程-思科入门CCNA初级网络工程师视频课程-网络技术
  11. 技术开发、产品开发和平台开发的区别
  12. 计算机标准差平方差怎么按,数学标准差公式
  13. Python爬取并分析IMDB电影
  14. GOD IS A GIRL 创作背后感人的故事
  15. 详述支付网关的设计原则
  16. 互联网日报 | 阿里拟280亿港元控股高鑫零售;小米首发80瓦无线秒充;国产特斯拉整车出口海外...
  17. javascript解数独LeetCod-37
  18. 网页游戏如何发送数据封包的思路和核心代码具体实现
  19. <Rasa实战> 内容摘要(四)
  20. eclipse java jde,Eclipse平台入门之一:什么是Eclipse,我们将开始介绍Java 开发环境(JDE)。...

热门文章

  1. excel如何当计算机使用方法,如何让你的excel文件只准允一台电脑使用,重要资料相当需要...
  2. java二维码生成导出成压缩包
  3. 2005年最过耳难忘的好声音-才女肖燕《女儿香》
  4. sessionFactory理解
  5. JDBC连接MSQ乱码的问题
  6. mysql--使用full join报错
  7. jenkins构建权限问题
  8. 音频之Android NDK读写声卡
  9. 微型计算机原理实验,微机原理实验总结(共5篇)
  10. maven依赖找不到问题