写在前面

本文一起看下,redis的一些知识点。

1:pub/sub

redis的发布订阅功能,客户端可以订阅特定的频道,当有新消息发送到该频道时,订阅了该频道的客户端都能接收到发布的消息,如下图:

redis 的命中率 redis命中率多少合适_redis知识点

假设我们有一个频道nba news,用来你发布nba相关信息。

  • 客户端订阅

redis 的命中率 redis命中率多少合适_redis_02

  • 发布消息

redis 的命中率 redis命中率多少合适_redis 的命中率_03

2:缓存命中率

当将Redis作为缓存来使用时,缓存命中率是一个判断缓存是否表现优秀的重要指标,这部分一起看下如何判断缓存命中率,也比较简单,只需要使用info命令即可,如下:

127.0.0.1:6379> info Stats
# Stats
...
keyspace_hits:14
keyspace_misses:1
...

其中的keyspace_hits就是命中的个数,keyspace_misses就是没有命中的个数,则缓存命中率就是keyspace_hits/keyspace_hits+keyspace_misses,这里就是14/14+1=93.3%

3:lua脚本

redis支持执行lua脚本,可以用其来以原子的方式执行相对复杂的业务逻辑,接下来一起看下。首先我们定义一个lua脚本Redis_CompareAndSet.lua,如下:

local key = KEYS[1]
local val = redis.call("GET", key);
 
if val == ARGV[1]
then
        redis.call('SET', KEYS[1], ARGV[2])
        return 1
else
        return 0
end

逻辑也比较简单,看下就明白了。执行:

执行命令: redis-cli -a 密码 --eval Lua脚本路径 key [key …] ,  arg [arg …] 
如:redis-cli -a 123456 --eval ./Redis_CompareAndSet.lua userName , zhangsan lisi

注意:“–eval"而不是命令模式中的"eval”,一定要有前端的两个-,脚本路径后紧跟key [key …],相比命令行模式,少了numkeys这个key数量值。key [key …] 和 arg [arg …] 之间的“ , ”,英文逗号前后必须有空格,否则死活都报错。

运行如下:

d:\program_files\redis3.2>redis-cli -a 123456 --eval ./Redis_CompareAndSet.lua userName , zhangsan lisi
(integer) 0

此时返回0是因为我本地环境中并没有keyuserName,如下:

127.0.0.1:6379> EXISTS userName
(integer) 0

我们接着通过如下操作来设置userName的值为zhangsan,这样再次运行lua脚本后将会满足条件,最终return 1,如下:

127.0.0.1:6379> eval "return redis.call('set', 'userName', 'zhangsan')" 0   
OK

0,代表不设置key和arg,当然需要的话也可以设置。完成格式是eval script numkeys key [key ...] arg [arg ...]

再次执行lua脚本如下:

d:\program_files\redis3.2>redis-cli -a 123456 --eval ./Redis_CompareAndSet.lua userName , zhangsan lisi
(integer) 1

可以看到此时返回1,代表修改userName为lisi了,如下验证:

127.0.0.1:6379> get userName
"lisi"

此时再执行lua脚本,就会返回0了,如下:

d:\program_files\redis3.2>redis-cli -a 123456 --eval ./Redis_CompareAndSet.lua userName , zhangsan lisi
(integer) 0

4:设置过期时间

redis给数据设置过期时间提供了命令expire ,PEXPIRE ,EXPIREAT, PEXPIREAT ,如下图:

redis 的命中率 redis命中率多少合适_redis_04

然后分别来看下。

4.1:expire

设置过期的秒,即多少秒之后过期,命令格式为EXPIRE key seconds [NX | XX | GT | LT] ,参数说明如下:

NX -- Set expiry only when the key has no expiry
XX -- Set expiry only when the key has an existing expiry
GT -- Set expiry only when the new expiry is greater than current one
LT -- Set expiry only when the new expiry is less than current one

实例如下:

127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> expire name 60
(integer) 1
127.0.0.1:6379> ttl name
(integer) 56
127.0.0.1:6379> ttl name
(integer) 53

使用ttl 命令可以获取过期剩余的秒数。

4.2:pexpire

类似于expire,但是通过毫秒设置,时间更加精确,命令格式PEXPIRE key milliseconds [NX | XX | GT | LT],参数含义如下:

NX -- Set expiry only when the key has no expiry
XX -- Set expiry only when the key has an existing expiry
GT -- Set expiry only when the new expiry is greater than current one
LT -- Set expiry only when the new expiry is less than current one

实例如下:

127.0.0.1:6379> set name lucy
OK
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> pexpire name 10000
(integer) 1
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2

最后返回-2,说明已经过期删除了。

4.3:expireat

使用UNIX秒时间戳来设置过期时间,提供准确的时间,命令格式EXPIREAT key unix-time-seconds [NX | XX | GT | LT],实例如下:

127.0.0.1:6379> set name james
OK
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> expireat name 1670852794
(integer) 1
127.0.0.1:6379> ttl name
(integer) 23

1670852794设置的是70秒之后过期。

4.4:pexpireat

使用UNIX毫秒时间戳来设置过期时间,进行更加精确的设置,命令格式PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT],实例如下:

127.0.0.1:6379> set name lucy
OK
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> pexpireat name 1670857794000
(integer) 1
127.0.0.1:6379> ttl name
(integer) 4853
127.0.0.1:6379> ttl name
(integer) 4849

5:如何禁用命令

有些命令在正式环境使用的话,可能会引起线上环境的问题,比如keys,flushall,flushdb,那么有没有什么办法来禁用这些危险命令呢?有的,Redis提供了rename-command 配置项,该配置项可以将命令重命名为其他字符串,使原命令失效,也可以使用""彻底禁用命令,说明如下:

# Command renaming.
#
# It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# hard to guess so that it will still be available for internal-use tools
# but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG ""
#
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to slaves may cause problems.

因此我们就可以配置rename-command 源命令 其他的字符串来修改命令的名称,或者rename-command 源命令 ""来彻底禁用命令。

6:monitor

该命令用来实时监听客户端执行的命令,需要的时候,可以用来查看命令的执行情况,一般因为其对性能影响较大,所以不要使用,如下:

redis 的命中率 redis命中率多少合适_redis 的命中率_05

但是注意,以下命令,monitor并不会监听:

AUTH(>6.0)
EXEC
QUIT

接下来我们看下使用monitor和不使用monitor时性能的差异。

6.1:不使用monitor

d:\program_files\redis3.2>.\redis-benchmark.exe -c 10 -n 100000 -q -a 123456 -h 127.0.0.1 -p 6379
PING_INLINE: 50684.23 requests per second
PING_BULK: 53219.80 requests per second
SET: 49975.02 requests per second
GET: 49455.98 requests per second
INCR: 50025.02 requests per second

看下GETGET: 49455.98 requests per second,每秒处理49455.98个请求。

6.2:使用monitor

d:\program_files\redis3.2>.\redis-cli.exe -a 123456 monitor
OK
d:\program_files\redis3.2>.\redis-benchmark.exe -c 10 -n 100000 -q -a 123456 -h 127.0.0.1 -p 6379
PING_INLINE: 35855.14 requests per second
PING_BULK: 39904.23 requests per second
SET: 37147.11 requests per second
GET: 36101.08 requests per second
INCR: 30721.96 requests per second

看下GETGET: 36101.08 requests per second,每秒处理36101.08个请求。相比于不使用monitor的49455.98个请求,下降了49455.98-36101.08/49455.98=27%,可以看到降低了将近百分之三十的处理速度,实际场景中可能会更严重,这里导致性能降低的原因并不是实时获取每个执行命令动作本身,而是y因为将执行结果(不管这个结果是OK响应,还是具体的数据)发送给客户端时需要放到发送缓冲区中,而monitor命令也会将返回结果先放到发送缓存取,会导致发送缓冲区使用量增大,所以会导致发送缓冲区的阻塞,进而导致性能变慢。

写在后面