目录
以下总结了关于Redis比较全面的知识笔记以及面试题,方便自己复习的同时希望对大家有所帮助。
简述
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
它支持多种类型的数据结构,如 字符串(strings),散列(hashes), 列表(lists),集合(sets),有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
Redis的英文官网
Redis的中文官网
Redis的中文命令手册
本文主要介绍Redis五种常用的数据类型、三种特殊的数据类型的使用、应用场景。
Redis五种基本数据类型
String字符串
Redis 字符串是字节序列。Redis 字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,所以你可以存储任何东西,512 M为上限,主要的还是操作键值对。
String的数据结构是简单的Key-Value模型,Value可以是字符串,也可以是数字。
常用命令
- 添加元素(SET命令)
格式:
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
ex:秒级过期时间,nx:键不存在时才能设置成功,xx键存在时才能设置成功
(1)普通添加:set key value
(2)设置过期时间 setex:setex key seconds value
上图中:设置一个键为key1,值为hello,并且30秒后过期。
使用ttl命令
可以查看该键还有多少时间过期。
(3)不存在设置 setnx(set if not exist):setnx key value
如果key不存在,则创建一个key,如果key存在,则创建失败并返回0。
上图中,执行第一条命令时,由于key2不存在,所以就创建一个key2,执行第二句命令时,由于key2前面已经创建了,已经存在了,所以就创建失败,并且返回0。
setnx在分布式锁中经常使用到
- 获取值 (GET命令)
127.0.0.1:6379> set name oldou
OK
127.0.0.1:6379> get name #获取name中的值
"oldou"
- mset/mget命令
同时设置/获取一个或者多个键值对,主要就是批量设置和获取键值对。
-
msetnx key1 value1 key2 value2…
同时设置多个值,如果其中有一个存在,那么就都创建失败。
要么一起成功,要么一起失败,这是一个原子性操作。 -
incr命令
让当前键值以 1 的数量递增,并返回递增后的值。相当于Java中的自增,每次使用改命令都能让变量自增1。
应用场景:(一般可用于设置浏览量、阅读量)
- incrby命令
可以指定参数一次增加的数值,并返回递增后的值,(原来的num是2,加10之后变为12)
- decr命令
可以指定参数一次递减的数值,并返回递减后的值,每次使用该命令都自减1,相当于Java中的自减。
- decrby
可以指定参数一次递减的数值,并返回递减后的值
- append命令
向键值的末尾追加 value。如果键不存在则将该键的值设置为 value。
返回值是追加后字符串的总长度。
- strlen命令 :获取字符串长度
- **getrange命令:**截去指定索引的字符串
- setrange命令:从指定索引开始替换字符串
以上表示从索引为0开始的元素替换为QQQQQ
- del命令: 根据key删除一个或者多个元素
- 设置对象
set user:1 {name:zhangsan,age:3}
设置一个user:1对象,值为json字符串来保存一个对象。
这是一个巧妙的设计:user:{id}:{field}
如此设置在Redis中是完全OK的。
- getset命令:先get然后再set
如果设置的键不存在值,则设置值,并且返回nil
如果设置的键存在值,则返回该值,并设置新的值
应用场景
- 计数器—点赞,视频播放量,每播放一次就+1
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
Hash散列表
Redis 的哈希是键值对的集合。
Redis 的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象,还有用户信息之类的,经常变动的信息。
Hash更适合用于对象的存储,String更适合字符串存储。
常用命令
- (1)
hset命令
:存储一个哈希键值对的集合
格式为:hset key field value -----表示的是在key的field下设置一个为“value”的值。 - (2)
hget命令
:获取一个哈希键的值
格式为:hget key field
- (3)
hmset
: 存储一个或多个哈希是键值对的集合
格式为:hmset key field1 value1 …fieldN keyN - (4)
hmget
: 获取多个指定的键的值
格式为:hmget key field1 … fieldN
- (5)
hexists
: 判断哈希表中的字段名是否存在 如果存在返回 1 否则返回 0
格式为:hexists key field
- (6)
hdel
: 删除一个或多个字段
格式为:hdel key field
- (7)
hgetall
: 获取一个哈希是键值对的集合
格式为:hgetall key
- (8)
hvals
: 只返回字段值
格式为:hvals key
- (9)
hkeys
: 只返回字段名
格式为:hkeys key
- (10)
hlen
: 返回 key 的 hash 的元素个数
格式为:hlen key
这里是因为user下有两个属性 username和userage
- (11)
hincrby key field value
: 指定增量value
- (12)
hsetnx key field value
: 如果该键不存在就创建,如果该键存在就创建失败。
使用场景
主要用于存储部分变更数据,比如存储用户信息等等
List链表
Redis 的链表是简单的字符串列表,排序插入顺序。您可以添加元素到 Redis 的列表的头部或尾部
Lpush:表示的是向链表的左添加,也就是向链表的头添加;
Rpush:表示的是向链表的右添加,也就是向链表的尾添加;
常用命令
-
(1)
lpush key value
: 向链表左侧添加—头插法
-
(2)
rpush key value
: 向链表右侧添加–尾插法
-
(3)
lpop key
: 从左边移出一个元素,就是从最左边的那个节点剔除掉。
-
(4)
rpop key
: 从右边移出一个元素,就是从最右边的那个节点剔除掉。
-
(5)
lrange key start end lrange
: 命令将返回索引从 start 到 stop 之间的所有元素。Redis 的列表起始索引为 0。
如果是要获取全部的元素:lrange key 0 -1
-
(6)
llen key
: 返回链表中元素的个数 相当于关系型数据库中 select count(*)
-
(7)
lindex key indexnumber
:lindex 命令用来返回指定索引的元素,索引从 0 开始,如果是负数表示从右边开始计算的索引,最右边元素的索引是-1。如果要将列表类型当做数组来用,lindex 命令是必不可少的。
-
(8)
lset key indexnumber value
: 是另一个通过索引操作列表的命令,它会将索引为 index的元素赋值为 value,原来的值会被覆盖。如果该列表不存在就会报错。所以使用这个命令之前先使用exists判断一下。
-
(9)
lrem key count value
: 移除key链表中count个元素的value值,精确匹配,如果链表中可以有多个重复的值,这里的count指的是可以删除多个相同key的值。
-
(10)
ltrim list startIndex endIndex
:通过下标截取指定的长度,这个时候list已经改变了,只剩下截断的元素。
-
(11)
rpoplpush source destination
:移除当前的source链表中的最后一个元素,并且将该元素移动到destination链表当中。
-
(12)
linsert key BEFORE|AFTER pivot value
:在key列表的pivot元素的前/后面插入元素value。
小结: -
实际上list是一个链表,before node after,left ,right都可以插入值;
-
如果key不存在,就创建新的链表;
-
如果key存在就创建新的值;
-
如果移除了所有的值,空链表,也就代表不存在;
-
在两边插入或者改动值,效率最高,中间元素相对来说效率会低一点。
应用场景
- 消息队列:利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。
- 消息排队,消息队列(Lpush、Rpop)、栈(Lpush、Lpop)
- 使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。
Set( 集合)
Redis 的集合是字符串的无序集合。
在Set集合当中,是不允许有重复的。
set是通过hash table实现的,可以进行添加、删除和查找。对集合我们可以取并集,交集,差集.
常用命令
- (1)
sadd key value
:添加一个 string 元素到,key 对应的 set 集合中, 成功返回 1,如果元素已经在集合中返回 0
- (2)
scard key
: 返回 set 的元素个数,如果 set 是空或者 key 不存在返回 0
- (3)
smembers key
: 返回 key 对应 set 的所有元素,结果是无序的
- (4)
sismember key value
: 判断 value 是否在 set 中,存在返回 1,0 表示不存在或者 key 不存在
- (5)
srem key value
: 从 key 对应 set 中移除给定元素,成功返回 1,如果 value 在集合中不存在或者 key 不存在返回 0
- (6)
srandmember key nums
: 从key集合中随机抽取nums个元素。
- (7)
spop key
:随机删除一些key集合中的元素。
- (8)
smove source destination member
:将原集合source中的member元素移动到destination集合中。
- (9)
sdiff key1 key2
:取出key1中与key2集合的不同元素,差集
- (10)
sinter key1 key2
:取key1与key2两个集合中相同的元素,交集
- (11)
sunion key1 key2
:将key1与key2两个集合中的元素合在一起,并集
应用场景
- 微博、用户将所有关注的人都放入到一个set集合当中,将它的粉丝也放在一个集合中。
- 共同关注、共同爱好、二度好友、QQ的好友推荐(六度分割理论)
SortedSet( 有序集合) zset
Redis 的有序集合类似于 Redis 的集合,字符串不重复的集合。
常用命令介绍
-
(1)
zadd key score value
: 将一个或多个 value 及其 socre 加入到 set 中
-
(2)
zrange key start end
:0 和-1 表示从索引为 0 的元素到最后一个元素(同 LRANGE 命令相似)
-
(3)
zrange key 0 -1 withscores
也可以连同 score 一块输出,使用 WITHSCORES 参数
-
(4)
zremrangebyscore key start end
:可用于范围删除操作
-
(5)
zrangebyscore key min max
: 升序排序操作,将key按最小值到最大值进行输出。
zrevrange salary 0 -1
:这个是倒序全部输出
以上是从小到大排序,也就是升序排序。 -
(6)
zrevrangebyscore key max min
:倒序排序操作,将key按照从大到小排序输出
-
(6)
zrem key value
: 删除指定的元素
-
(7)
zcard key
:获取有序集合中的个数
-
(8)
zcount key min max
: 获取指定区间的成员数量
应用场景
- 存储班级成绩表、工资表排序
Redis三种特殊的数据类型
本次介绍的三种特殊类型为:
- Geospatial 地理位置
- Hyperloglog 基数统计
- Bitmap位图场景
Geospatial 地理位置
我们日常生活中的定位、查看附近的人、朋友的定位、打车距离计算等等,基本上都是使用Geospatial 。
Redis的Geo在Redis3.2版本就推出了这个功能,这个功能可以推算出地理位置的信息,两地之间的距离,方圆几里的人。
链接地址点这里
它主要有以下六个命令:
命令介绍
- (1)
geoadd命令
:将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
格式:geoadd key 经度 纬度 地理位置
例如:geoadd china:city 116.40 39.90 beijing
有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回下述错误。
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
同时,这个命令还可以添加多个元素,例如:
geoadd china:city 116.40 39.90 beijing 121.47 32.23 shanghai
规则:两级(南极北极)是无法直接添加的,一般会下载城市数据,直接通过Java程序一次性导入。
-
(2)
geopos命令
:从key里返回所有给定位置元素的位置(经度和纬度)。
格式:geopos key member
例如:geopos china:city beijing
返回值:GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成: 第一个元素为给定位置元素的经度, 而第二个元素则为给定位置元素的纬度。
当给定的元素不存在时,对应的数组项为空值。
-
(3)
geodist命令
:返回两个给定位置之间的直线距离,如果两个位置之间的其中一个不存在,那么命令就返回空值。
格式:geodist key member1 member2 [unit]
指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。 -
(4)
georadius命令
:以给定的经纬度为中心,找出某一半径内的元素。
以给定的经纬度为中心,返回键包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
格式:georadius key longitude latitude radius m|km|ft|mi
例如: georadius china:city 110 23 1000 km
key为键,longitude、latitude表示当前位置的经纬度,radius表示搜索半径,m|km|ft|mi表示单位。
上图的代码解释:
以110 23为经纬度查询方圆1000km内的城市。前提是所有的数据录入了china:city中。
其余参数解释:
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE k]
在给定以下可选项时, 命令会返回额外的信息:
WITHDIST
: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。显示到中间距离的位置。
WITHCOORD
: 将位置元素的经度和维度也一并返回。显示他人的定位信息。
WITHHASH
: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
ASC
: 根据中心的位置, 按照从近到远的方式返回位置元素。
DESC
: 根据中心的位置, 按照从远到近的方式返回位置元素。
在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用COUNT <count>
选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素, 命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的。
获得指定的人数:2人
应用场景:附近的人。。
- (5)
georadiusbymember命令
:找出位于指定范围内的元素,中心点是由给定的位置元素决定。
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
格式:georadiusbymember key member radius m|km|ft|mi
例如:georadiusbymember china:city shanghai 1000 km
后面的参数,可以参考GEORADIUS命令。
- (6)
geohash命令
:返回一个或多个位置元素的Geohash字符串。
该命令返回11个字符的Geohash字符串,命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。
将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近。
底层实现原理
GEO底层的实现原理其实就是Zset,我们可以使用Zset命令来操作GEO。
Hyperloglog 基数统计
-
什么是基数?
一个可重复集合内不重复元素的个数就是基数。 -
简介
Redis 2.8.9版本就更新了Hyperloglog数据结构。
Redis Hyperloglog基数统计的算法。 -
优点:占用的内存是固定的,2^64不同元素的技术,只需要费12KB内存。如果要从内存角度来比较的话,Hyperloglog是首选。
-
举例:网页的UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式:使用set保护用户的ID,然后就可以统计set中的元素数量作为标准判断,这个方式如果保存大量的用户id,就会比较麻烦。
我们的目的是为了计数,而不是存储用户id。
命令介绍
- (1)
pfadd key member1 member2 ....
:添加一个或者多个元素 - (2)
pfcount key
:统计key集合中基数的个数
- (3)
pfmerge newKey key1 key2 ...
:合并key1和key2中的元素,并且剔除其中重复的元素,产生新的集合newKey。
key1中的元素有:a b c d e f g
key2中的元素有:h i j a e q n m c
重复的元素有:a e c
产生的newKey中的元素有:a b c d e f g h i j q n m
应用场景:适合做页面统计。
如果允许容错,那么一定要使用Hyperloglog;
如果不允许容错,就使用set或者自己的数据类型即可。
(容错就是当由于种种原因在系统中出现了数据、文件损坏或丢失时,系统能够自动将这些损坏或丢失的文件和数据恢复到发生事故以前的状态,使系统能够连续正常运行的一种技术,很简单的意思)
Bitmap位图场景
- 概括
Bitmap是位图,数据结构,都是操作二进制位来进行记录,就只有0和1两个状态。
365天 = 365bit 1字节 = 8bit 大约45个字节左右
基本命令
-
(1)
setbit key offset value
:value只能是0或者1,如下例子所示:
使用bitmap来记录周一到周日的打卡。
上图中的offeset表示周几,value的0表示未打卡,1表示打开成功。
周一0:1,周二1:0,周三2:1,周四3:1,… -
(2)
getbit key offset
:获取key中的offset值,查看某一天是否打卡
以上表示的是查看周四是否打卡,周日是否打卡。 -
(3)
bitcount key
:统计操作,统计打开的天数,相当于统计sign中value为1的元素个数
应用场景
统计用户信息、活跃、不活跃、登录、未登录、打卡等等… 两个状态的都可以使用Bitmap。
在生活或者开发中都有十分多的应用场景。
面试题常问的数据类型
1、Redis 的数据类型有哪些?
-
Redis支持五种数据类型:String(字符串)、hash(哈希)、list(列表)、set(集合)以及zsetsorted set(有序集合)。
-
我们实际项目中比较常用的是String和hash,如果你是Redis的中高级用户,还需要加上以下几种数据类型,分别是:HyperLogLog、Geo、Pub/Sub。
-
如果你玩过Redis Module,像BloomFilter、RedisSearch、Redis-ML,,等等,是加分项。
2、一个字符串类型的值能存储最大容量是多少?
一个字符串类型的值能存储的最大容量为512M。
3、Redis key 的过期时间和永久有效分别怎么设置?
-
使用expire命令对key的过期时间进行设置;
-
使用persist命令对key永久有效进行设置;
4、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set他们最多能存放多少元素?
理论上Redis可以处理多达232个keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。
任何list、set和sorted set都可以放232个元素,换句话说,Redis的存储极限是系统中的可用内存值。
5、Redis 最适合的场景?
- (1)会话缓存(最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其他存储(如 Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台Magento 也提供 Redis 的插件。
- (2)全页缓存(FPC)
除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。 再次以 Magento 为例,Magento提供一个插件来使用 Redis 作为全页缓存后端。 此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。 - (3)队列
Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop 操作。 如果你快速的在 Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一个后台就是使用 Redis 作为 broker,你可以从这里去查看。 - (4)排行榜/计数器
Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的 10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的,你可以在这里看到。 - (5)发布/订阅
最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用 Redis 的发布/订阅功能来建立聊天系统!)
6、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。
7、如果这个Redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
这个时候就要回答:Redis是单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,知道指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取指令模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
8、如果有大量的 key 需要设置同一时间过期,一般需要注意什么?
如果大量的key过期时间设置过于集中,那么到过期的那个时间点,Redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一点。
9、使用过 Redis 做异步队列么,你是怎么用的?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。
当lpop没有消息的时候,要适当sleep一会儿再重试。
追问:可不可以不使用sleep呢?
list还有个指令叫blpop,在没有消息的时候,它会阻塞住,直到消息到来。
再追问:能不能生产一次,消费多次呢?
使用pub/sub主题订阅者模式,可以使用1:N的消息队列。
再问:pub/sub有什么缺点?
在消费者下线的情况下,生产的消息会丢失,解决这样的问题得使用专业的消息队列,如RabbitMQ等。
Redis如何实现延时队列呢?
使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangbyscore指令获取N秒之前的数据轮询进行处理。
10、使用过 Redis 分布式锁么,它是什么回事?
先拿setnx来争抢锁,抢到之后再用expire给锁加一个过期时间防止锁忘记了释放。
问:如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
这个时候确实锁会永远得不到释放了,但是set指令有个非常复杂的参数是可以同时把setnx和expire合成一条指令来用的。
寄言
学习Redis建议看官网的文档进行学习,Redis还有许多的命令未介绍出来,如果在工作中使用到了,可以去Redis官方文档中查看,这里介绍一个Redis的中文命令手册点击即达。
创作不易,如果觉得对你有帮助,可以点个赞支持一下哈。