Redis 达成队列原理的实例详细解释

Redis 实现队列原理的实例详解

Redis

一、初始Redis

场景说明:

简介

redis是一种高级的key:value存储系统,其中value支持五种数据类型:

  1. 字符串(String)
  1. 字符串列表(Lists)
  2. 字符串集合(Sets)
  3. 有序字符串集合(Sorted Sets)
  4. 哈希(Hashes)

Key不要太长,尽量不超过1024字节,不仅消耗内存,而且会降低查找效率;

Key也不要太短,太短的话,会降低可读性

在一个项目中,key最好使用统一的命名格式。uid:111:pwd:abc

1、Redis特性与优点

  • 速度快。redis所有数据都存放于内存;是用C语言实现,更加贴近硬件;使用了单线程架构,避免了多线程竞争问题
  • 基于键值对的数据结构,支持的数据结构丰富。它主要提供了5种数据结构:
    字符串、 哈希、 列表、 集合、 有序集合,
    同时在字符串的基础之上演变出了位图(Bitmaps)
    和HyperLogLog两种神奇的“数据结构”, 并且随着LBS(Location Based
    Service, 基于位置服务) 的不断发展,
    Redis3.2版本中加入有关GEO(地理信息定位) 的功能
  • 功能丰富。键过期功能,用来实现缓存;提供了发布订阅功能,实现消息系统;支持Lua脚本;提供简单的事务功能;提供了Pipeline,一次性将一批命令传给redis服务器,减小了网络开销
  • 简单,稳定。源码少,3.0版本源代码只有5w行左右
  • 客户端语言多。
  • 持久化。两种持久化方式: RDB和AOF

  • 主从复制。复制功能是分布式Redis的基础

  • 高可用和分布式。2.8版本提供了高可用实现Redis Sentinel,
    它能够保证Redis节点的故障发现和故障自动转移。
    Redis从3.0版本正式提供了分布式实现Redis Cluster,
    它是Redis真正的分布式实现, 提供了高用、 读写和容量的扩展性。

·用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时

String

String是一个很基础的数据类型,也是任何存储系统都必备的数据类型。

set mystr "hello world"
get mystr

字符串类型的用法,非常简单,因为二进制安全的,所以可以把一个图片文件的内容作为字符串来存储。

另外,还可以通过字符串类型进行数值操作:

set mynum "2"
get mynum
incr mynum
get mynum  
"3"

在数值操作时,redis会将字符串类型转换为数值。

由于INCR等指令本身就具有原子操作的特性,所以完全可以利用redis的incr、incrby、decr、decrby等指令来实现原子计数的效果。

假设某个场景下,有3个客户端同时读取mynum的值,并且給其+1,那么最后mynum的值一定是5。很多网站用redis来进行业务上的统计技术需求。

其他命令:
SETNX :SET if not exists(如果不存在,则SET),
Setnx可以用作加锁原语。

比如说,要对关键字(key)foo加锁,可以尝试一下方式:

setnx lock.foo <current Unix time + lock timeout +1>

可以通过del lock.foo来释放锁。

处理死锁:如果因为客户端失败、崩溃或其他原因没有释放锁的话,怎么办?
这种状况可以通过检测发现,因为上锁的key保存的是Unix时间戳,加入key值的时间戳小于当前时间戳,表示锁已经不再生效。
如果有多个客户端同时竞争的时候,就不能通过删除锁来进行解决了。

C1和C2读取lock.foo并检查时间戳,setnx都返回0,因为他已经被C3锁上了,但C3在上锁后就崩溃了。

  1. C1向lock.foo发送Del命令。
  2. C1向lock.foo发送setnx命令并成功。
  3. C2向lock.foo发送Del命令。
  4. C2向lock.foo发送setnx命令并成功。
  5. C1和C2都获得了锁。
    但是以下算法可以避免以上的问题。
    1.C4向lock.foo发送setnx命令。
    2.因为C3崩溃,还锁着lock.foo,所以redis向C4返回0.
    3.C4向lock.foo发送GET命令,查看lock.foo的锁是否过期。如果不,休眠,重试。
    4.如果lock.foo内的unix时间戳比当前时间戳老,则C4执行一下命令:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>

因为getset的作用,C4可以查看GETSET的返回值,确定lock.foo之前存储的旧值仍是哪个过期的时间戳,如果是的话,那么C4获得锁。

SETEX :
将值value关联到key,并将key的生存时间设为seconds(以秒为单位)。如果key已经存在,setex命令将会覆写旧值。
这个命令类似于一下的两个命令:

set key value
expire key seconds

setex
是一个原子性的操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在redis用作缓存的时候,非常实用。

Mset
同时设置一个或者多个key-value对。当发现同名的key存在时,mset会用新值覆盖旧值,如果不希望覆盖同名的key,请使用msetnx

MsetNx 类似于setnx,不做赘述。

Append
如果key存在,并且是一个字符串,append会将value追加到key原来的值之后。如果不存在,则append就简单的将给定的key设为value,就像执行set
key value一样。

Mget 返回所有(一个或者多个)给定的key值。

GetRange: getrange key start
end.返回key中字符串值的子字符串,字符串截取的范围由start和end两个偏移量决定。负数表示从最后开始计数。

GetSet: getset key value
将给定的key值设为value,并且返回key的旧值。

getset可以和incr组合使用,实现一个有原子性复位操作的计数器。
举例来说,每当某个时间发生时,进程可能对一个名为
mycount的key调用incr操作,通常我们还要在一个原子时间内完成获得计数器的值和将计数器复位为0两个操作。
可以用命令 getset mycounter 0来实现这一目标。

incr mycount
(integer) 11
getset mycount 0
"11"
get mycount
"0"

Decr decr key.将key中存储的数字值减一。
Decrby decrby key decrement
将key所存储的值减去减量decrement。如果key不存在,以0为初始值,然后执行decrby操作。

Incr incr by 将key中存储的数字值加一
Incrby incrby key increment 将key所存储的值加上增量increment

2、应用场景

  • 缓存。
  • 排行榜系统。redis提供了列表和有序集合支持。
  • 计数器应用。
  • 社交网络。
  • 消息队列系统。redis提供了订阅发布和阻塞队列功能,虽然和专业的消息队列软件比不够强大,但是可以满足一般的消息队列需求

·高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求

Lists

redis的另外一个重要的数据结构是lists(列表)。

redis的lists底层实现不是数组,而是链表。也就是说无论是10个元素的链表,还是1百万个元素的链表,从头尾插入一个元素的时间复杂度是常数级别的,也就是Lpush|Rpush插入新元素的时间是相同的。

缺点为:定位某个元素的速度很慢。

常用的操作包括: Lpsuh,Rpush,Lrange,Lpop

lpush mylist "1"
(integer)1
rpush mylist "2"
(integer)2
lpush mylist "0"
(integer)3
lrange mylist 0 1
1) "0"
2) "1"
lrange mylist 0 -1
1) "0"
2) "1"
3) "2"

Lists的应用场景非常广泛:
1.可以利用lists来实现消息队列,而且可以确保先后顺序。
2.利用Lrange可以方便实现分页的功能
3.在Blog中,每篇Blog的评论可以存入一个单独的list中。

lpush 将一个或多个值插入到列表表头

lpushx 将value插入列表key的表头,当且仅当key存在并且是一个列表

lpop lpop key,移除并返回列表key的头元素。

rpop rpop key,移除并返回列表key的尾元素。

blpop
是列表的阻塞式(blocking)弹出原语。如果阻塞,也就是给定的所有key都不存在或者包含空列表,那么blpop命令将阻塞连接,直到等待超时,或另一个客户端对给定的key执行lpush或rpush为止。超时参数timeout接受一个以秒为单位的数字作为值。超时参数设为0表示阻塞时间可以无限期延长。

相同的key可以被多个客户端同时阻塞,不同的客户端放进一个队列里,按先阻塞先服务的顺序为key进行blpop命令。
Blpop可以用在pipeline(批量的发送多个命令并读入多个回复)[不是很理解]

llen 返回key的长度

lrange 返回列表key中指定区间内的元素。

lset lset key index value 将列表key下标为index的元素的值改为value

ltrim ltrim key start stop
对一个列表进行修剪,让列表只保留指定区间内的元素,不在指定区间内的元素都将被删除。

lindex lindex key index 返回列表key中,下标为index的元素。

linsert linsert key before | after pivot
value,将value插入到列表key当中,位于值pivot之后,当pivot不存在于列表key时,不执行任何操作。当key不存在时,key被视为空列表,不执行任何操作。如果key不是列表类型,返回一个错误。

rpoplpush rpoplpush source destination
一个原子时间内执行两个操作。将列表source中的最后一个元素弹出,返回客户端,将source弹出的元素插入到列表destination,作为destination列表的头元素

一个安全的队列。redis经常被用作队列,用户在不同程序之间有效的交换信息。一个程序(producer)通过lpush命令将消息放入队列,而另一个程序(consumer)通过rpop取出队列中等待时间最长的消息。
不幸的是,在这个过程中,一个消费者可能在获取一个消息之后崩溃,而未执行完成的消息也会因此丢失。
使用rpoplpush命令可以解决这个问题,因为它在返回一个消息之余,还将该消息添加到另一个列表中,另外的这个列表可以用作消息的备份表:如果一起正常,当消费者完成该消息的处理之后,可以用lrem命令将该罅隙从备份表中删除。
另一方面,助手程序可以通过监视备份表,将超过一定处理时限的消息重新放入队列中去(负责处理该消息的消费者可能已经崩溃),这样就不会丢失任何消息了。

BRPOPLPUSH brpoplpush source destination timeout
是上一个命令的阻塞版本。

3、redis常用操作

  • 服务端启动。配置文件启动,redis-server  /opt/redis/redis.conf
  • 客户端启动。redis-cli -h 127.0.0.1  -p 6379
  • 停止redis服务。redis-cli shutdown nosave|save 
    关闭过程:断开与客户端的连接;持久化文件生成。不建议kill -9
    暴力杀进程,极端下会造成丢失数据的情况

·抢购场景,先入先出的模式

SETS

redis中的集合,是一种无序的集合,集合中的元素没有先后顺序

集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集。

对于集合的使用,也有一些常见的场景。如QQ的好友标签,就是把每一个用户的标签储存在一个集合之中。

SADD sadd key member
将一个或者多个member元素加入到集合key中,已经存在集合的member元素将被忽略。

SREM srem key member
移除集中key中的一个或多个member元素,不存在的member元素会被忽略。

smembers smembers key 返回集合key中的所有成员。

sismember 判断member元素是否是key的成员。

scard scard key 返回集合key的基数

smove smove source destination member
将member元素从source集合移动到destination集合。
如果source集合不存在或不包含指定的member元素,则smove命令不执行任何操作,仅返回0.否则,member元素从source集合中被移除,并添加到destination集合中去。
当destination集合已经包含member元素时,smove命令只是简单地将source集合中的member元素删除。

Spop spop key 移除并返回集合中的一个随机元素。
返回被移除的随机元素,当key不存在或key是空集时,返回nil

srandmember srandmember key 返回集合中的一个随机元素

sinter sinter key 返回一个集合的全部成员,该集合是所有给定集合的交集

sinterstore sinterstore destination key
它与sinter相同,但它将结果保存到destination集合,而不是简单地返回结果集。如果destination可以是key本身。

sunion 返回一个集合的全部成员,该集合时所有给定集合的并集。

sunionstore sunionstore destination key
此命令等同于sunion,但它将保存到destination集合,而不是简单地返回结果集。
如果destination已经存在,则将其覆盖。destination可以是key本身。

sdiff sdiff key 返回一个集合的全部成员,该集合是所有给定集合的差集。

sdiffstore
此命令等同于sdiff,但它将结果保存到destination集合中,而不是简单地返回结果集。

二、API的理解和使用

命令:

有序集合

redis不仅提供了无序集合(sets), 还提供了有序集合(sorted
sets)。有序集合中的每个元素都关联一个序号(score),这便是排序的一句。

将redis中的有序集合称为zsets,这是因为有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等

Zadd将一个zadd key score member
将一个或多个member元素机器score值加入到有序集key当中

Zrem 移除有序集key中的一个或多个成员,不存在的成员将被忽略。

Zcard 返回有序集key的基数

Zcount 返回有序集key中,score值在min和max之间的成员

Zscore 返回有序集key中,成员member的score值

Zincrby 为有序集key的成员member的score值加上增量increment。

Zrange 返回有序集key中,指定区间内的成员,按score值递增排序

Zrevrange
返回有序集中,指定区间内的成员。其中成员的位置按score值递减来排列。具有相同score值的成员按字典序反序排列

Zrangebyscore
返回有序集key中,所有score值介于min和max之间的成员。有序集成员按score值递增次序排列

Zrank
返回有序集key中成员member的排名。其中有序集成员按score值递增顺序排列

Zrevrank
返回有序集key中成员member的排名。其中有序集成员按score值递减排序。

Zremrangebyrank 移除有序集key中,指定排名区间内所有成员。

Zinterstore
计算给定的一个或多个有序集的交集,其中给定key的数量必须以numkeys参数指定,并将该交集存储到destination。

Zunionstore
计算给定的一个或多个有序集的并集,其中给定key的数量必须以numkeys参数指定,并将该并集存储到destination

1、全局命令

  • 查看所有键。【keys *】会遍历所有键,禁用
  • 键总数。【dbsize】不会遍历所有键,直接获取redis内置的键总数
  • 检查键是否存在。【exists key】存在返回1,不存在返回0
  • 删除键。【del key】返回结果为成功删除的键的个数
  • 设置键过期。【expire key seconds】秒级别;【expireat key
    timestamp 】秒级时间戳;【pexpire
    key milliseconds】毫秒级过期 ;【pexpireat
    key milliseconds-timestamp 】毫秒级时间戳

  • 检查键过期。【ttl key】返回键的剩余过期时间,秒级;【pttl
    key】毫秒级;-1,键没设置过期时间;-2 键不存在;

  • 清除过期时间。【persist】;对于字符串类型键,执行set命令也会去掉过期时间;setex命令作为set+expire的组合,
    不但是原子执行, 同时减少了一次网络通讯的时间
  • 查看键的类型。【type key】键不存在,返回none
  • 键重命名。rename key newkey。如果在rename
    newkey之前,newkey已经存在,newkey的值会被覆盖。防止被覆盖,提供了renamenx命令。重命名期间会删除原来的key,如果key对应的值过大,存在阻塞redis的可能。
  • 随机返回一个键。randomkey。
rpush + blpop 或 lpush + brpop

Hash

hash存的是字符串和字符串之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合用哈西。

Hset 将哈希表key中的域field的值设为value

Hsetnx 将哈希表key中的域field的值设为value,当且仅当域field不存在

Hmset 同时将多个field-value对设置到哈希表key中

Hget 返回哈希表key中给定域field的值

Hmget 返回哈希表key中给定的一个或多个域的值

Hgetall 返回哈希表中,所有的域和值

Hdel 删除哈希表key中一个或多个域,不存在的域将被忽略

Hlen 返回哈希表key中域的数量

Hexists 查看哈希表key中,给定域field是否存在

Hincrby 为哈希表key中的域field的值加上增量increment

Hkeys 返回哈希表key中的所有域

Hvals 返回哈希表key中的所有值

2、数据结构和内部编码

每种数据结构都有底层的内部编码实现,而且是多种实现,redis会在合适的场景下选择合适的内部编码。

rpush : 往列表右侧推入数据

3、单线程架构

  • Redis使用了单线程架构和IO多路复用模型(epoll作为多路复用技术的实现,非阻塞IO)来实现。
  • 每次客户端的请求都会经过发送命令、执行命令、返回结果三个阶段。
  • 所有客户端命令都会放入到同一个队列中,然后逐个被执行。
  • 单线程避免了线程切换和竞态产生的消耗

blpop : 客户端阻塞直到队列有值输出

4、字符串,最大不能超过512M

(1)命令

  • 设置值。set key value [ex seconds] [px
    milliseconds] [nx|xx]。其中setnx可以作为分布式锁的实现
  • 获取值。 get key。不存在返回
    nil(空)
  • 批量设置值,mset
    key value [key value …] ;批量获取值,mget key [key …]
    。一次请求的网络时间大于命令处理时间。学会使用批量操作,能减少大量的网络消耗。提高业务处理效率。但是一次批量操作过多,有可能导致Redis阻塞或者网络拥堵

  • 计数。incr、decr、incrby、decrby、incrbyfloat。很多其他的语言和存储系统通过cas实现计数,会有一定的cpu开销。redis单线程模型,完全不存在这个问题。

  • 追加值。append key
    value。
  • 字符串长度。strlen
    key。每个中文占用三个字节,也就是三个长度。
  • 设置并返回原值。getset
    key value
  • 设置指定位置的字符。setrange key offset
    value
  • 获取部分字符串。getrange
    key start end。时间复杂度O(n)

(2)内部编码,有三种

  • int。8个字节的长整型
  • embstr。小于等于39个字节的字符串
  • raw。大于39个字节的字符串

(3)应用场景

  • 缓存
  • 计数
  • 共享session
  • 限速。每个用户的请求频率,每个ip的请求频率

简单队列:

5、哈希

(1)命令

  • 设置field值。hset key
    field
    value。hsetnx,和setnx作用相同,只不过作用域由键变为field
  • 获取field值。hget key
    field。如果field不存在,返回nil
  • 删除field。hdel key
    field [field
    …] 
  • 计算field个数。hlen
    key
  • 批量设置和获取field-value。hmget key field [field …] ;hmset key field value [field value …]

  • 判断field是否存在。hexists key
    field。存在返回1,不存在返回0

  • 获取所有field。hkeys
    key
  • 获取所有value。hvals
    key
  • 获取所有field-value。hgetall
    key。如果获取的元素比较多,可能会阻塞Redis。

  • hincrby key field;hincrbyfloat key field

  • 计算value的字符串长度。hstrlen key
    field

(2)内部编码

  • ziplist(压缩列表)。当哈希类型元素个数小于hash-max-ziplist-entries
    配置(默认512个) 、 同时所有值都小于hash-max-ziplist-value配置(默认64
    字节) 时, Redis会使用ziplist作为哈希的内部实现, ziplist使用更加紧凑的
    结构实现多个元素的连续存储, 所以在节省内存方面比hashtable更加优秀 
  • hashtable(哈希表)。当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现
simple.php$stmt = $pdo-prepare('select id, cid, name from zc_goods limit 200000');$stmt-execute();while ($row = $stmt-fetch(PDO::FETCH_ASSOC)) { $redis-rPush('goods:task', json_encode($row));} $redis-close();

6、列表。列表中的每个字符串称为元素(element),一个列表最多存储2^32-1个元素。可以充当栈和队列,比较灵活。列表中的元素是有序的且可重复的,可以通过下标获取某个元素

(1)命令

  • 添加操作。从右边插入数据 rpush key value
    [value …];从左边插入数据  lpush key
    value [value …];向某个元素前或后插入数据 linsert key
    before|after pivot value

  • 查找。获取指定范围的元素列表 lrange key start
    end(包含end);获取指定下标的元素 lindex key
    index(-1为最后一个元素);获取列表长度 llen key

  • 删除。从左侧弹出元素 lpop key;从右侧弹出元素
    rpop key;删除指定元素 lrem key count value(count>0
    从左到右,删除最多count个元素;count<0
    从右到左;count=0,删除所有);按照索引范围修剪列表 ltrim key start
    end
  • 修改。修改执行索引下标的元素 lset key index
    newValue;
  • 阻塞操作。blpop key [key …] timeout ;brpop key [key …] timeout
    。需要注意两点:第一点,如果blpop多个key,一旦有一个键能弹出元素,立刻给客户端返回;第二点,如果多个客户端对同一个key执行blpop,那么最先执行blpop的客户端可以获取到弹出的值。

(2)内部编码

  • ziplist(压缩列表)。当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,
    Redis会使用linkedlist作为列表的内部实现。
  • Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现

(3)应用场景

  • 消息队列。Redis的lpush+brpop命令组合即可实现阻塞队列,
    生产者客户端使用lrpush从列表左侧插入元素,
    多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,
    多个客户端保证了消费的负载均衡和高可用性。
  • 文章列表。哈希存储每篇文章的详细信息。列表存储每个人的文章列表。支持分页获取,如果列表较大,获取列表中间元素的性能会变差,可以使用redis3.2版本的quicklist内部编码实现。
  • lpush+lpop=Stack(栈) ;lpush+rpop=Queue(队列)
    ;lpush+brpop=Message Queue(消息队列,阻塞队列); lpush+ltrim=Capped Collection(有限集合)

获取20000万个商品,并把json化后的数据推入goods:task队列

7、集合(set)。不允许有重复元素,无序,不能通过索引下标获取元素。一个集合最多可以存储2^32-1个元素。支持集合内的增删改查,集合的交集、并集、差集。

(1)命令。集合内操作。

  • 添加元素。sadd key element [element
    …]
  • 删除元素。srem key
    element [element …]

  • 计算元素个数。scard key 。时间复杂度O(1),不会遍历集合。

  • 判断元素是否在集合中。sismember key element
    。在集合内返回1,不在返回0

  • 随机从集合中返回指定个数元素。srandmember key [count]

  • 从集合随机弹出指定个数的元素。spop key  [count] 

  • 获取所有元素。smembers key
    。如果元素过多,存在阻塞redis的可能。可以用sscan来完成。

(2)命令,集合间操作。

  • 求多个集合的交集。sinter
    key [key …]

  • 求多个集合的并集。suinon key [key …]

  • 求多个集合的差集。sdiff
    key [key
    …] 

  • 将集合操作的结果保存。sinterstore/suionstore/sdiffstore  destination key [key
    …] 

(3)内部编码

  • intset(整数集合) :
    当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个)时,
    Redis会选用intset来作为集合的内部实现,从而减少内存的使用。
  • hashtable(哈希表) :
    当集合类型无法满足intset的条件时,
    Redis会使用hashtable作为集合的内部实现。

(4)使用场景

  • 标签(tag)。使用sinter命令计算用户的共同兴趣。
  • 生成随机数,抽奖
queueBlpop.php// 出队while (true) { // 阻塞设置超时时间为3秒 $task = $redis-blPop(array('goods:task'), 3); if ($task) { $redis-rPush('goods:success:task', $task[1]); $task = json_decode($task[1], true); echo $task['id'] . ':' . $task['cid'] . ':' . 'handle success'; echo PHP_EOL; } else { echo 'nothing' . PHP_EOL; sleep(5); }}

8、有序集合。元素可以排序,每个元素设置一个分数(score)作为排序的依据。集合成员不能重复,但是每个元素的score可以重复

(1)命令。集合内的命令。

  • 添加成员。zadd key score member [score member
    …] 

    ·Redis3.2为zadd命令添加了nx、 xx、 ch、 incr四个选项:
    ·nx: member必须不存在, 才可以设置成功, 用于添加。
    ·xx: member必须存在, 才可以设置成功, 用于更新。
    ·ch: 返回此次操作后, 有序集合元素和分数发生变化的个数
    ·incr: 对score做增加, 相当于后面介绍的zincrby。

  • 计算成员个数。zcard key

  • 计算某个成员的分数。zscore key member

  • 计算成员排名。zrank key
    member (分数由低到高);zrevrank key
    member (分数由高到低)

  • 删除成员。zrem key member [member …]

  • 增加成员分数。zincrby
    key increment member

  • 返回指定排名范围的成员。zrange key start end [withscores]
    (排名由低到高);zrevrange key start end
    [withscores] (排名由高到低)

  • 返回指定分数范围的成员。zrangebyscore key min max [withscores] [limit
    offset count] 。其中zrangebyscore按照分数从低到高返回, zrevrangebyscore反之。
    例如
    下面操作从低到高返回200到221分的成员, withscores选项会同时返回每个
    成员的分数。 [limit offset count]选项可以限制输出的起始位置和个数 。同时min和max还支持开区间(小括号) 和闭区间(中括号) ,
    -inf和
    +inf分别代表无限小和无限大。

  • 返回指定分数范围成员个数。zcount key min max

  • 删除指定排名内的升序元素。zremrangebyrank key start end

  • 删除指定分数范围的成员。zremrangebyscore key min
    max 

(2)命令,集合间的操作

  • 交集。zinterstore
    destination numkeys key [key …] [weights weight [weight
    …]][aggregate
    sum|min|max]
  • destination:
    交集计算结果保存到这个键。
    ·numkeys:
    需要做交集计算键的个数。
    ·key[key…]:
    需要做交集计算的键。
    weights weight[weight…]: 每个键的权重, 在做交集计算时, 每个键中
    的每个member会将自己分数乘以这个权重,
    每个键的权重默认是1。
    ·aggregate sum|min|max: 计算成员交集后, 分值可以按照sum(和) 、
    min(最小值) 、
    max(最大值)
    做汇总, 默认值是sum 
  • 并集。zunionstore
    destination numkeys key [key …] [weights weight [weight
    …]][aggregate
    sum|min|max]

(3)内部编码

  • ziplist(压缩列表) : 当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个) , 同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节) 时, Redis会用ziplist来作为有序集合的内部实现, ziplist
    可以有效减少内存的使用 
  • skiplist(跳跃表) :
    当ziplist条件不满足时, 有序集合会使用skiplist作为内部实现,
    因为此时ziplist的读写效率会下降。 

(4)使用场景

  • 排行榜

设置blpop阻塞时间为3秒,当有数据出队时保存到goods:success:task表示执行成功,当队列没有数据时,程序睡眠10秒重新检查goods:task是否有数据出队

9、键管理

(1)键迁移

  • dump+restore可以实现在不同的Redis实例之间进行数据迁移的功能,
    整个迁移的过程分为两步:
    1) 在源Redis上, dump命令会将键值序列化, 格式采用的是RDB格式。
    2) 在目标Redis上, restore命令将上面序列化的值进行复原,
    其中ttl参数代表过期时间, 如果ttl=0代表没有过期时间。
    第一, 整个迁移过程并非原子性的,而是通过客户端分步完成的。 第二,
    迁移过程是开启了两个客户端连接,
    所以dump的结果不是在源Redis和目标Redis之间进行传输

  • migrate命令也是用于在Redis实例间进行数据迁移的,
    实际上migrate命令就是将dump、 restore、 del三个命令进行组合,
    从而简化了操作流程。
    migrate命令具有原子性,
    而且从Redis3.0.6版本以后已经支持迁移多个键的功能,
    有效地提高了迁移效率, migrate在10.4节水平扩容中起到重要作用。
    第一, 整个过程是原子执行的, 不需要在多个Redis实例上开启客户端的,
    只需要在源Redis上执行migrate命令即可。
    第二, migrate命令的数据传输直接在源Redis和目标Redis上完成的。
    第三, 目标Redis完成restore后会发送OK给源Redis,
    源Redis接收后会根据migrate对应的选项来决定是否在源Redis上删除对应的键。
    migrate host port key|”” destination-db timeout [copy] [replace]
    keys key1 key2 key3
    key|””。要迁移的键,多个键此处为””。
    keys key1 key2 key3。迁移多个键

(2)键遍历

  • 全量遍历键。keys
    pattern。支持pattern匹配。如果非要使用,在一个不对外提供服务的Redis从节点上执行,
    这样不会阻塞到客户端的请求, 但是会影响到主从复制
  • 渐进式遍历。每次执行scan, 可以想象成只扫描一个字典中的一部分键,
    直到将字典中的所有键遍历完毕。
    scan cursor [match pattern] [count number]
    ·cursor是必需参数,
    实际上cursor是一个游标,第一次遍历从0开始,每次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束。
    ·match
    pattern是可选参数,它的作用的是做模式的匹配,这点和keys的模式匹配很像。
    ·count
    number是可选参数,它的作用是表明每次要遍历的键个数,默认值是10,此参数可以适当增大。
    除了scan以外,Redis提供了面向哈希类型、集合类型、有序集合的扫描遍历命令,解决诸如hgetall、
    smembers、 zrange可能产生的阻塞问题,对应的命令分别是hscan、 sscan、
    zscan,它们的用法和scan基本类似
    渐进式遍历可以有效的解决keys命令可能产生的阻塞问题,但是scan并非完美无瑕,如果在scan的过程中如果有键的变化(增加、
    删除、 修改),scan并不能保证完整的遍历出来所有的键

(3)数据库管理

  • 切换数据库。select
    db。各个数据库之间没有联系,可以存在相同的键。redis是单线程的,多个数据库使用同一个cpu,彼此至今还是会受影响。
  • 清除数据库。flushdb/flushall。如果当前数据库键值数量比较多,flushdb/flushall存在阻塞Redis的可能性

cli 模式执行命令:

三、小功能,大用处

php simple.phpphp queueBlpop.php

1、慢查询分析

(1)慢查询只是统计的命令执行的时间,不包括客户端发送命令、命令排队、返回结果的时间。

  • slowlog-log-slower-than预设阀值,它的单位是微秒(1秒=1000毫秒=1000000微秒),默认值是10000。slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slowerthan<0对于任何命令都不会进行记录。
  • 实际上Redis使用了一个列表来存储慢查询日志,slowlog-max-len就是列表的最大长度
  • 在Redis中有两种修改配置的方法, 一种是修改配置文件,
    另一种是使用config
    set命令动态修改。如果要Redis将配置持久化到本地配置文件,需要执行config
    rewrite命令
  • 获取慢查询日志。slowlog get [n]
    ,n可以指定条数。每个慢查询日志有4个属性组成,
    分别是慢查询日志的标识id、 发生时间戳、
    命令耗时、 执行命令和参数 
  • 获取慢查询日志当前的列表长度,slowlog len。

  • 慢查询日志列表清空。slowlog
    reset。

(2)最佳实践

  • slowlog-max-len配置建议: 线上建议调大慢查询列表,
    记录慢查询时Redis会对长命令做截断操作, 并不会占用大量内存。
    增大慢查询列表可以
    减缓慢查询被剔除的可能, 例如线上可设置为1000以上。
  • slowlog-log-slower-than配置建议:
    默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。
    由于Redis采用单线程响应命令, 对于高流量的场景,
    如果命令执行时间在1毫秒以上, 那么Redis最多可支撑OPS不到1000。
    因此对于高OPS场景的Redis建议设置为1毫秒。 
  • 慢查询只记录命令执行时间,
    并不包括命令排队和网络传输时间。
    因此客户端执行命令的时间会大于命令实际执行时间。 
  • 定期执行slow
    get命令将慢查询日志持久化到其他存储中(例如MySQL) ,
    然后可以制作可视化界面进行查询

优先级队列

2、Redis  Shell

(1)Redis-cli

  • -h
    服务端ip
  • -p
    端口
  • -r
    (repeat)将命令执行多次。redis-cli -r 3
    ping

  • -i
    (interval)每个几秒执行几次。redis-cli -r
    5 -i 1
    ping 

  • -a
    (auth)密码
  • –slave。将当前客户端模拟成服务端的从节点。
  • –rdb。生成RDB持久化文件,保存到本地。可用来做持久化文件的定期备份
  • –eval。执行lua脚本
  • –latency。测试客户端到目标redis服务的网络延迟;–latency-history,每隔多久输出一次网络延迟;–latency-dist,使用统计图表的形式从控制台输出延迟统计信息

  • –stat。实时获取redis的重要统计信息。key的数量、内存占用量、客户端数量、请求数量、连接数量

(2)redis-server

  • redis-server –test-memory
    1024。检测当前操作系统能否稳定地分配指定容量的内存给Redis。整个内存检测的时间比较长。该功能更偏向于调试和测试

(3)redis-benchmark。可以为Redis进行基准性能测试

  • -c。代表客户端的并发数量(默认是50) 
  • -n(num) 。代表客户端请求总量(默认是100000) 。redis-benchmark-c100-n20000代表100各个客户端同时请求Redis,
    一共执行20000次 
  • -r(random) 选项,
    可以向Redis插入更多随机的键
  • –csv选项会将结果按照csv格式输出, 便于后续处理, 如导出到Excel等