目录
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 字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,所以你可以存储任何东西,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
统计多单位的数量
粉丝数
对象缓存存储
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
: 如果该键不存在就创建,如果该键存在就创建失败。
主要用于存储部分变更数据,比如存储用户信息等等
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甚至可以构建有优先级的队列系统。
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的好友推荐(六度分割理论)
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
: 获取指定区间的成员数量
存储班级成绩表、工资表排序
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合成一条指令来用的。