Redis 6总结(1)
1. NoSQL 数据库简介
1.1 技术发展
技术的分类:
- 解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN
- 解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis
- 解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch
1.1.1 问题
1.1.2 解决CPU及内存压力
使用负载均衡减少服务器压力,但存在登录验证问题
1.1.3 解决IO压力
1.2 NoSQL 数据库
1.2.1 NoSQL 数据库概述
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库
NoSQL 不依赖业务逻辑方式存储,而以简单的 key-value 模式存储。因此大大的增加了数据库的扩展能力
- 不遵循 SQL 标准。
- 不支持 ACID。
- 远超于 SQL 的性能。
1.2.2 NoSQL 适用场景
- 对数据高并发的读写
- 海量数据的读写
- 对数据高可扩展性的
1.2.3 NoSQL不适用场景
- 需要事务支持
- 基于 SQL 的结构化查询存储,处理复杂的关系,需要即时查询
- (用不着 SQL 的和用了 SQL 也不行的情况,请考虑用 NoSQL)
1.2.4 常用的 NoSQL 数据库
1.2.4.1 Memcache
1.2.4.2 Redis
1.2.4.3 MongoDB
1.3 行式存储数据库(大数据时代)
1.3.1 行式数据库
1.3.2 列式数据库
1.3.2.1 HBase
HBase 是 Hadoop 项目中的数据库。它用于需要对大量的数据进行随机、实时的读写操作的场景中
HBase 的目标就是处理数据量非常庞大的表,可以用普通计算机处理超过10亿行数据,还可处理有数百万列元素的数据表
1.3.2.2 Cassandra
Apache Cassandra 是一款免费的开源 NoSQL 数据库,其设计目的在于管理由大量商用服务器构建起来的庞大集群上的海量数据集(数据量通常达到PB级别)。在众多显著特性当中,Cassandra 最为卓越的长处是对写入及读取操作进行规模调整,而且其不强调主集群的设计思路能够以相对直观的方式简化各集群的创建与扩展流程
计算机存储单位 计算机存储单位一般用B,KB,MB,GB,TB,EB,ZB,YB,BB来表示,它们之间的关系是:
- 位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位
- 字节 byte:8个二进制位为一个字节(B),最常用的单位
- 1KB (Kilobyte 千字节)=1024B
- 1MB (Megabyte 兆字节 简称“兆”)=1024KB
- 1GB (Gigabyte 吉字节 又称“千兆”)=1024MB
- 1TB (Trillionbyte 万亿字节 太字节)=1024GB,其中1024=2^10 ( 2 的10次方)
- 1PB(Petabyte 千万亿字节 拍字节)=1024TB
- 1EB(Exabyte 百亿亿字节 艾字节)=1024PB
- 1ZB (Zettabyte 十万亿亿字节 泽字节)= 1024 EB
- 1YB (Jottabyte 一亿亿亿字节 尧字节)= 1024 ZB
- 1BB (Brontobyte 一千亿亿亿字节)= 1024 YB.
注:“兆”为百万级数量单位
1.4 图关系型数据库
主要应用:社会关系,公共交通网络,地图及网络拓扑(n *(n-1)/ 2)
2. Redis 概述安装
- Redis 是一个开源的 key-value 存储系统
- 和 Memcached 类似,它支持存储的 value 类型相对更多,包括 string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和 hash(哈希类型)
- 这些数据类型都支持 push/pop、add/remove 以及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的
- 在此基础上,Redis 支持各种不同方式的排序
- 与 memcached 一样,为了保证效率,数据都是缓存在内存中
- 区别的是 Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件
- 并且在此基础上实现了 master-slave(主从)同步
2.1 应用场景
2.1.1 配合关系型数据库做高速缓存
- 高频次,热门访问的数据,降低数据库IO
- 分布式架构,做session共享
2.1.2 多样的数据结构存储持久化数据
2.2 Redis 安装
2.3 Redis 介绍相关知识
Redis是单线程+多路IO复用技术
多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
串行 vs 多线程+锁(memcached) vs 单线程+多路IO复用(Redis)
(与Memcache三点不同: 支持多数据类型,支持持久化,单线程+多路IO复用)
3. 常用五大数据类型
String、List、Set、Hash、Zset
对象 | 编码 | 数据结构 |
---|---|---|
string(字符串对象) | int | 可以存储 long 类型的整数 |
embstr | embstr 编码的简单动态字符串 | |
row | 简单动态字符串(SDS) | |
list(列表对象) | ziplist | 压缩列表 |
linkedisk | 双端链表 | |
hash(哈希对象) | ziplist | 压缩列表 |
hashtable | 字典 | |
set(集合对象) | intset | 整数集合 |
hashtable | 字典 | |
zset(有序集合对象) | ziplist | 压缩列表 |
skiplist | 字典 + 跳跃表 |
3.1. Redis键(key)
- keys * :查看当前库所有 key (匹配:keys *1)
- exists key :判断某个 key 是否存在
- type key :查看你的 key 是什么类型
- del key :删除指定的 key 数据
- unlink key :根据 value 选择非阻塞删除,仅将 keys 从 keyspace 元数据中删除,真正的删除会在后续异步操作
- expire key 10 :10秒钟:为给定的 key 设置过期时间
- ttl key :查看还有多少秒过期,-1 表示永不过期,-2 表示已过期
- select :命令切换数据库
- dbsize :查看当前数据库的 key 的数量
- flushdb :清空当前库
- flushall :通杀全部库
3.2 Redis字符串(String)
3.2.1 简介
- String 是 Redis 最基本的数据类型,可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value
- String 类型是二进制安全的。意味着 Redis 的string 可以包含任何数据。比如 JPG 图片或者序列化的对象
- 一个 Redis 中字符串 value 最多可以是 512M
3.2.2 常用命令
添加键值对
set <key> <value>
- NX:当数据库中key不存在时,可以将key-value添加数据库
- XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
- EX:key的超时秒数
- PX:key的超时毫秒数,与EX互斥
- get key
查询对应键值- append key value
将给定的 value 追加到原值的末尾- strlen key
获得值的长度- setnx key value
只有在 key 不存在时 设置 key 的值- incr key
将 key 中储存的数字值增1
只能对数字值操作,如果为空,新增值为1- decr key
将 key 中储存的数字值减1
只能对数字值操作,如果为空,新增值为-1- incrby / decrby key 步长
将 key 中储存的数字值增减。自定义步长。原子性
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作
Redis 单命令的原子性主要得益于 Redis 的单线程- mset key1 value1 key2 value2 .....
同时设置一个或多个 key-value 对- mget key1 key2 key3 .....
同时获取一个或多个 value- msetnx key1 value1 key2 value2 .....
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
原子性,有一个失败则都失败- getrange key 起始位置 结束位置
获得值的范围,类似java中的substring,前包,后包- setrange key 起始位置 value
用 value 覆写 key 所储存的字符串值,从 起始位置 开始(索引从0开始)- setex key 过期时间 value
设置键值的同时,设置过期时间,单位秒- getset key value
以新换旧,设置了新值同时获得旧值
3.2.3 数据结构
String 的数据结构为简单动态字符串(Simple Dynamic String,缩写 SDS)。是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList ,采用预分配冗余空间的方式来减少内存的频繁分配
内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M
3.3 Redis 列表(List)
3.3.1 简介
单键多值
- Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
- 它的底层实际是个 双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。即 查询性能较差
3.3.2 常用命令
- lpush/rpush key value1 value2 value3 ....
从左边 / 右边插入一个或多个值 - lpop/rpop key
从左边 / 右边吐出一个值。值在键在,值光键亡。 - rpoplpush key1 key2
从 key1 列表右边吐出一个值,插到 key2 列表左边。 - lrange key start stop
按照索引下标获得元素(从左到右)
lrange mylist 0 -1
0 左边第一个,-1 右边第一个,(0-1表示获取所有) - lindex key index
按照索引下标获得元素(从左到右) - llen key
获得列表长度 - linsert key before/after value newvalue
在 value 的前面 / 后面插入 newvalue 插入值 - lrem key n value
从左边删除 n 个value(从左到右) - lset key index value
将列表 key 下标为 index 的值替换成 value
3.2.3 数据结构
List 的数据结构为快速链表 quickList
- 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表
- 它将所有的元素紧挨着一起存储,分配的是一块连续的内存
- 当数据量比较多的时候才会改成 quicklist
- 因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如列表里存的只是int类型的数据,结构上还需要两个额外的指针 prev 和 next
Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余
3.4 Redis 集合(Set)
3.4.1 简介
Redis Set 对外提供的功能与 List 类似是一个列表的功能,特殊之处在于 Set 是可以 自动排重 的,当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个成员是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的
Redis 的 Se t是 String 类型的 无序集合 。它底层其实是一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是O(1)
一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
3.4.2 常用命令
- sadd key value1 value2 .....
将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略 - smembers key
取出该集合的所有值。 - sismember key value
判断集合 key 是否为含有该 value 值,有1,没有0 - scard key
返回该集合的元素个数。 - srem key value1 value2 ....
删除集合中的某个元素。 - spop key
随机从该集合中吐出一个值。 - srandmember key n
随机从该集合中取出n个值。不会从集合中删除 。 - smove source target value
把集合中一个值从一个集合移动到另一个集合 - sinter key1 key2
返回两个集合的交集元素。 - sunion key1 key2
返回两个集合的并集元素。 - sdiff key1 key2
返回两个集合的差集元素(key1中的,不包含key2中的)
3.4.3 数据结构
Set 数据结构是 dict 字典,字典是用哈希表实现的
Java 中 HashSet 的内部实现使用的是 HashMap,只不过所有的 value 都指向同一个对象。Redis 的 Set 结构也是一样,它的内部也使用 Hash 结构,所有的 value 都指向同一个内部值。
3.5 Redis哈希(Hash)
3.5.1 简介
Redis hash 是一个键值对集合。Redis hash 是一个 String 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。类似 Java 里面的 Map<String, Object>
**
用户 ID 为查找的 key,存储的 value 用户对象包含姓名,年龄,生日等信息,如果用普通的 key/value 结构来存储,主要有以下2种存储方式:
3.5.2 常用命令
- hset key field value
给 key 集合中的 field 键赋值 value - hget key1 field
从 key1 集合 field 取出 value - hmset key1 field1 value1 field2 value2...
批量设置 hash 的值 - hexists key1 field
查看哈希表 key 中,给定域 field 是否存在。 - hkeys key
列出该 hash 集合的所有 field - hvals key
列出该 hash 集合的所有 value - hincrby key field increment
为哈希表 key 中的域 field 的值加上增量 1 -1 - hsetnx key field value
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
3.5.3 数据结构
Hash 类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当 field-value 长度较短且个数较少时,使用 ziplist,否则使用 hashtable
3.6 Redis 有序集合 Zset(sorted set)
3.6.1 简介
Redis 有序集合 Zset 与普通集合 Set 非常相似,是一个 没有重复元素 的字符串集合。不同之处是有序集合的每个成员都关联了一个 评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的
因为元素是有序的,所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表
3.6.2 常用命令
- zadd key score1 value1 score2 value2…
将一个或多个 member 元素及其 score 值加入到有序集 key 当中 - zrange key start stop [WITHSCORES]返回有序集 key 中,下标在 start stop之间的元素
带WITHSCORES,可以让分数一起和值返回到结果集 - zrangebyscore key minmax [withscores] [limit offset count]
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列 - zrevrangebyscore key maxmin [withscores] [limit offset count]同上,改为从大到小排列
- zincrby key increment value
为元素的score加上增量 - zrem key value
删除该集合下,指定值的元素 - zcount key min max
统计该集合,分数区间内的元素个数 - zrank key value
返回该值在集合中的排名,从0开始
3.6.3 数据结构
SortedSet(Zset)是Redis提供的一个非常特别的数据结构,一方面它等价于 Java 的数据结构 Map<String, Double>
,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表
Zset 底层使用了两个数据结构
- hash,hash的作用就是关联元素 value 和权重 score,保障元素 value 的唯一性,可以通过元素 value 找到相应的 score 值
- 跳跃表,跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表
3.6.4 跳跃表
1、简介
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis 采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单
2、实例
对比有序链表和跳跃表,从链表中查询出51
- 有序链表
要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较 - 跳跃表
- 从第2层开始,1节点比51节点小,向后比较
- 21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
- 在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
- 在第0层,51节点为要查找的节点,节点被找到,共查找4次
从此可以看出跳跃表比有序链表效率要高
4. Redis 配置文件
4.1 ###Units 单位###
配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感
4.2 ###INCLUDES 包含###
类似 JSP 中的 include,多实例的情况可以把公用的配置文件提取出来
4.3 ###NETWORK 网络相关配置###
4.3.1 bind
默认情况 bind=127.0.0.1 只能接受本机的访问请求。不写的情况下,无限制接受任何 ip 地址的访问。生产环境应该要写应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉
4.3.2 protected-mode
将本机访问保护模式设置 no
4.3.3 Port
默认端口为 6379
4.3.4 tcp-backlog
设置 tcp 的 backlog,backlog 其实是一个连接队列,backlog 队列总和 = 未完成三次握手队列 + 已经完成三次握手队列。在高并发环境下你需要一个高 backlog 值来避免慢客户端连接问题
注意 Linux 内核会将这个值减小到 /proc/sys/net/core/somaxconn 的值(128),所以需要确认增大 /proc/sys/net/core/somaxconn 和 /proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果
4.3.5 timeout
一个空闲的客户端维持多少秒会关闭,0 表示关闭该功能。即永不关闭。
4.3.6 tcp-keepalive
对访问客户端的一种心跳检测,每隔 n 秒检测一次。单位为秒,如果设置为 0,则不会进行 Keepalive 检测,建议设置成 60 (默认 300)
4.4 ###GENERAL 通用###
4.4.1 daemonize
是否为后台进程,设置为 yes。守护进程,后台启动
4.4.2 pidfile
存放 pid 文件的位置,每个实例会产生一个不同的 pid 文件
4.4.3 loglevel
指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice。四个级别根据使用阶段来选择,生产环境选择 notice 或者 warning
4.4.4 logfile
日志文件名称
4.4.5 databases 16
设定库的数量默认16,默认数据库为 0,可以使用SELECT dbid 命令在连接上指定数据库 id
4.5 ###SECURITY 安全###
4.5.1 设置密码
在命令中设置密码,只是临时的。重启 redis 服务器,密码就还原了。永久设置,需要在配置文件中进行设置。
4.6 #### CLIENTS 客户端
4.6.1 maxclients
设置redis同时可以与多少个客户端进行连接。默认情况下为 10000 个客户端。如果达到了此限制,redis 则会拒绝新的连接请求,并且向这些连接请求方发出 “max number of clients reached” 以作回应
4.7 ####MEMORY MANAGEMENT 内存管理####
4.7.1 maxmemory
建议必须设置,否则,将内存占满,造成服务器宕机。设置 Redis 可以使用的内存量。一旦到达内存使用上限,Redis 将会试图移除内部数据,移除规则可以通过 maxmemory-policy 来指定
如果 Redis 无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么 Redis 则会针对那些需要申请内存的指令返回错误信息,比如 SET、LPUSH 等
但是对于无内存申请的指令,仍然会正常响应,比如 GET 等。如果你的 Redis 是主 Redis(说明你的 Redis有从 Redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素
4.7.2 maxmemory-policy
- volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键;(最近最少使用)
- allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key
- volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键
- allkeys-random:在所有集合 key 中,移除随机的 key
- volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key
- noeviction:不进行移除。针对写操作,只是返回错误信息
4.7.3 maxmemory-samples
- 设置样本数量,LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,Redis 默认会检查这么多个 key 并选择其中 LRU 的那个。
- 一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小。
5. Redis的发布和订阅
5.1 简介
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。Redis 客户端可以订阅任意数量的频道
5.2 Redis的发布和订阅
- 客户端订阅频道
- 当给这个频道发布消息后,消息就会发送给订阅的客户端
5.3 发布订阅命令行实现
1、打开一个客户端订阅 channel1
subscribe channel1
2、打开另一个客户端,给 channel1 发布消息 hello
publish channel1 hello
返回的1是订阅者数量
3、打开第一个客户端可以看到发送的消息
注:发布的消息没有持久化,如果在订阅的客户端收不到 hello,只能收到订阅后发布的消息
6. Redis新数据类型
6.1 Bitmaps
6.1.1 简介
现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位, 例如“abc”字符串是由3个字节组成,但实际在计算机存储时将其用二进制表示, “abc”分别对应的 ASCII 码分别是97、 98、 99, 对应的二进制分别是01100001、 01100010和01100011,如下图
合理地使用操作位能够有效地提高内存使用率和开发效率。Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:
- Bitmaps 本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作
- Bitmaps 单独提供了一套命令, 所以在 Redis 中使用 Bitmaps 和使用字符串的方法不太相同。 可以把 Bitmaps 想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在 Bitmaps 中叫做偏移量
6.1.2 命令
1、setbit
- 格式
setbit key offset value :设置 Bitmaps 中某个偏移量的值(0或1),offset:偏移量从0开始
- 示例
每个独立用户是否访问过网站存放在 Bitmaps 中, 将访问的用户记做1, 没有访问的用户记做0, 用偏移量作为用户的 id
设置键的第 offset 个位的值(从0算起) , 假设现在有20个用户,userid=1, 6, 11, 15, 19的用户对网站进行了访问, 那么当前Bitmaps初始化结果如图
users:20220224 表示 2022-02-24 这天独立访问用户的 Bitmaps
注:- 很多应用的用户 id 以一个指定数字(例如10000) 开头, 直接将用户 id 和 Bitmaps 的偏移量对应势必会造成一定的浪费, 通常的做法是每次做 setbit 操作时将用户 id 减去这个指定数字
- 在第一次初始化 Bitmaps 时, 假如偏移量非常大, 那么整个初始化过程执行会比较慢, 可能会造成 Redis 的阻塞
2、getbit
-
格式
getbit key offset :获取 Bitmaps 中某个偏移量的值,获取键的第 offset 位的值(从0开始算)
-
示例
获取 id=8 的用户是否在 2022-02-24 这天访问过, 返回0说明没有访问过,因为100根本不存在,所以也是返回0
3、bitcount
统计字符串被设置为 1 的 bit 数。一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。start 和 end 参数的设置,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,start、end 是指bit组的字节的下标数,二者皆包含
-
格式
bitcount key [start end] :统计字符串从start字节到end字节比特值为1的数量
-
示例
计算 2022-02-24 这天的独立访问用户数量
start 和 end 代表起始和结束字节数, 下面操作计算用户 id 在第1个字节到第3个字节之间的独立访问用户数, 对应的用户id是11, 15, 19。
举例: K1 【01000001 01000000 00000000 00100001】,对应【0,1,2,3】,四个字节组bitcount K1 1 2 : 统计下标1、2字节组中 bit=1 的个数,即 01000000 00000000
--》bitcount K1 1 2 --》1
bitcount K1 1 3 : 统计下标1、2字节组中 bit=1 的个数,即 01000000 00000000 00100001
--》bitcount K1 1 3 --》3
bitcount K1 0 -2 : 统计下标0到下标倒数第2,字节组中 bit=1 的个数,即 01000001 01000000 00000000
--》bitcount K1 0 -2 --》3注意:Redis 的 setbit 设置或清除的是 bit 位置,而 bitcount 计算的是 byte 位置
4、bitop
格式:bitop and(or/not/xor) destkey [key…]
bitop 是一个复合操作, 它可以做多个 Bitmaps 的 and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在 destkey 中
6.1.3 Bitmaps 与 Set 对比
设网站有 1 亿用户, 每天独立访问的用户有 5 千万, 如果每天用集合类型和 Bitmaps 分别存储活跃用户可以得到表
很明显, 这种情况下使用Bitmaps能节省很多的内存空间, 尤其是随着时间推移节省的内存还是非常可观的
但 Bitmaps 并不是万金油, 假如该网站每天的独立访问用户很少, 例如只有 10 万(大量的僵尸用户),那么两者的对比如下表所示, 很显然, 这时候使用Bitmaps 就不太合适了, 因为基本上大部分位都是0。
6.2 HyperLogLog
6.2.1 简介
在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站 PV(PageView页面访问量),可以使用 Redis 的 incr、incrby 轻松实现。但像 UV(UniqueVisitor,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。解决基数问题有很多种方案:
- 数据存储在MySQL表中,使用 distinct count 计算不重复个数
- 使用Redis提供的 hash、set、bitmaps 等数据结构来处理
以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。能否能够降低一定的精度来平衡存储空间?Redis 推出了 HyperLogLog。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数
6.2.2 命令
1、pfadd
- 格式
pfadd key element [element ...] :添加指定元素到 HyperLogLog 中
- 示例
将所有元素添加到指定 HyperLogLog 数据结构中。如果执行命令后 HLL 估计的近似基数发生变化,则返回1,否则返回0
2、pfcount
- 格式
pfcount key [key ...] :计算 HLL 的近似基数,可以计算多个 HLL,比如用HLL 存储每天的 UV,计算一周的 UV 可以使用7天的 UV 合并计算即可
- 示例
3、pfmerge
- 格式
pfmerge destkey sourcekey [sourcekey ...] :将一个或多个 HLL 合并后的结果存储在另一个 HLL 中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得
- 示例
6.3 Geospatial
6.3.1 简介
Redis 3.2 中增加了对 GEO 类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。Redis 基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度 Hash 等常见操作
6.3.2 命令
1、geoadd
-
格式
geoadd key longitude latitude member [longitude latitude member...] :添加地理位置(经度,纬度,名称)
-
示例
bash两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。当坐标位置超出指定范围时,该命令将会返回一个错误。已经添加的数据,是无法再次往里面添加的
2、geopos
- 格式
geopos key member [member...] :获得指定地区的坐标值
- 示例
3、geodist
-
格式
geodist key member1 member2 [m|km|ft|mi ] :获取两个位置之间的直线距离
-
示例
获取两个位置之间的直线距离
单位:m 表示单位为米[默认值]
km 表示单位为千米
mi 表示单位为英里
ft 表示单位为英尺如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位
4、georadius
- 格式
georadius key longitude latitude radius m|km|ft|mi :以给定的经纬度为中心,找出某一半径内的元素
- 示例