zoukankan      html  css  js  c++  java
  • redis 的简单命令

    以下实例讲解了如何启动 redis 客户端:

    启动 redis 客户端,打开终端并输入命令 redis-cli。该命令会连接本地的 redis 服务。

    $redis-cli
    redis 127.0.0.1:6379>
    redis 127.0.0.1:6379> PING
    
    PONG


    在远程服务上执行命令

    如果需要在远程 redis 服务上执行命令,同样我们使用的也是 redis-cli 命令。

    语法

    $ redis-cli -h host -p port -a password

    实例

    以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。

    $redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
    redis 127.0.0.1:6379>
    redis 127.0.0.1:6379> PING
    
    PONG

    有时候会有中文乱码。

    要在 redis-cli 后面加上 --raw

    redis-cli --raw

    Redis Expire 命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。

    设置成功返回 1 。 当 key 不存在或者不能为 key 设置过期时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的过期时间)返回 0 。

    redis> SET mykey "Hello"
    OK
    
    redis> EXPIRE mykey 10  # 为 key 设置生存时间
    (integer) 1
    
    redis> TTL mykey
    (integer) 10
    
    redis> PERSIST mykey    # 移除 key 的生存时间  
    (integer) 1
    
    redis> TTL mykey  以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
    (integer) -1

    返回 key 的数据类型,数据类型有:
    # 字符串 redis
    > SET weather "sunny" OK redis> TYPE weather string # 列表 redis> LPUSH book_list "programming in scala" (integer) 1 redis> TYPE book_list list # 集合 redis> SADD pat "dog" (integer) 1 redis> TYPE pat set

    Redis Getrange 命令用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。

    redis 127.0.0.1:6379> SET mykey "This is my test key"
    OK
    redis 127.0.0.1:6379> GETRANGE mykey 0 3
    "This"
    redis 127.0.0.1:6379> GETRANGE mykey 0 -1
    "This is my test key"

    Redis Getset 命令用于设置指定 key 的值,并返回 key 的旧值。

    返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时,返回 nil 。

    当 key 存在但不是字符串类型时,返回一个错误。

    redis> GETSET db mongodb    # 没有旧值,返回 nil
    (nil)
    
    redis> GET db
    "mongodb"
    
    redis> GETSET db redis      # 返回旧值 mongodb
    "mongodb"
    
    redis> GET db
    "redis"

    Redis Getset 命令用于设置指定 key 的值,并返回 key 的旧值。

    127.0.0.1:6379> get k3
    "redis"
    127.0.0.1:6379> getset k3 abc
    "redis"
    127.0.0.1:6379> get k3
    "abc"

    Redis Mget 命令返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。

    redis 127.0.0.1:6379> SET key1 "hello"
    OK
    redis 127.0.0.1:6379> SET key2 "world"
    OK
    redis 127.0.0.1:6379> MGET key1 key2 someOtherKey
    1) "Hello"
    2) "World"
    3) (nil)

    Redis Strlen 命令用于获取指定 key 所储存的字符串值的长度。当 key 储存的不是字符串值时,返回一个错误。

    127.0.0.1:6379> get k3
    "abc"
    127.0.0.1:6379> strlen k3
    (integer) 3
    127.0.0.1:6379> keys *
    1) "k3"
    2) "mong"
    127.0.0.1:6379> strlen k1
    (integer) 0

    Redis Incr 命令将 key 中储存的数字值增一。

    如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

    如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

    本操作的值限制在 64 位(bit)有符号数字表示之内。

    redis> SET page_view 20
    OK
    
    redis> INCR page_view
    (integer) 21
    
    redis> GET page_view    # 数字值在 Redis 中以字符串的形式保存
    "21"

    Redis Incrby 命令将 key 中储存的数字加上指定的增量值。

    如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。

    如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

    本操作的值限制在 64 位(bit)有符号数字表示之内。

    # key 存在且是数字值
    
    redis> SET rank 50
    OK
    
    redis> INCRBY rank 20
    (integer) 70
    
    redis> GET rank
    "70"
    
    
    # key 不存在时
    
    redis> EXISTS counter
    (integer) 0
    
    redis> INCRBY counter 30
    (integer) 30
    
    redis> GET counter
    "30"
    
    
    # key 不是数字值时
    
    redis> SET book "long long ago..."
    OK
    
    redis> INCRBY book 200
    (error) ERR value is not an integer or out of range

    Redis Hdel 命令用于删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略。

    redis 127.0.0.1:6379> HSET myhash field1 "foo"
    (integer) 1
    redis 127.0.0.1:6379> HDEL myhash field1
    (integer) 1
    redis 127.0.0.1:6379> HDEL myhash field2
    (integer) 0

    Redis Hexists 命令用于查看哈希表的指定字段是否存在。

    如果哈希表含有给定字段,返回 1 。 如果哈希表不含有给定字段,或 key 不存在,返回 0 。

    redis 127.0.0.1:6379> HSET myhash field1 "foo"
    (integer) 1
    redis 127.0.0.1:6379> HEXISTS myhash field1
    (integer) 1
    redis 127.0.0.1:6379> HEXISTS myhash field2
    (integer) 0

    Redis Hget 命令用于返回哈希表中指定字段的值。

    # 字段存在
    
    redis> HSET site redis redis.com
    (integer) 1
    
    redis> HGET site redis
    "redis.com"
    
    
    # 字段不存在
    
    redis> HGET site mysql
    (nil)
    redis> HSET myhash field1 "Hello"
    (integer) 1
    redis> HSET myhash field2 "World"
    (integer) 1
    redis> HGETALL myhash
    1) "field1"
    2) "Hello"
    3) "field2"
    4) "World"
    redis> 

    Redis Hincrby 命令用于为哈希表中的字段值加上指定增量值。

    增量也可以为负数,相当于对指定字段进行减法操作。

    如果哈希表的 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。

    如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 。

    对一个储存字符串值的字段执行 HINCRBY 命令将造成一个错误。

    本操作的值被限制在 64 位(bit)有符号数字表示之内。

    redis> HSET myhash field 5
    (integer) 1
    redis> HINCRBY myhash field 1
    (integer) 6
    redis> HINCRBY myhash field -1
    (integer) 5
    redis> HINCRBY myhash field -10
    (integer) -5
    redis> 

    Redis Hkeys 命令用于获取哈希表中的所有域(field)。

    redis 127.0.0.1:6379> HSET myhash field1 "foo"
    (integer) 1
    redis 127.0.0.1:6379> HSET myhash field2 "bar"
    (integer) 1
    redis 127.0.0.1:6379> HKEYS myhash
    1) "field1"
    2) "field2"

    Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。

    此命令会覆盖哈希表中已存在的字段。

    如果哈希表不存在,会创建一个空哈希表,并执行 HMSET 操作。

    redis 127.0.0.1:6379> HMSET myhash field1 "Hello" field2 "World"
    OK
    redis 127.0.0.1:6379> HGET myhash field1
    "Hello"
    redis 127.0.0.1:6379> HGET myhash field2
    "World"

    Redis Hlen 命令用于获取哈希表中字段的数量。

    哈希表中字段的数量。 当 key 不存在时,返回 0 。
    redis 127.0.0.1:6379> HSET myhash field1 "foo"
    (integer) 1
    redis 127.0.0.1:6379> HSET myhash field2 "bar"
    (integer) 1
    redis 127.0.0.1:6379> HLEN myhash
    (integer) 2

    Redis Hmget 命令用于返回哈希表中,一个或多个给定字段的值。

    如果指定的字段不存在于哈希表,那么返回一个 nil 值。

    一个包含多个给定字段关联值的表,表值的排列顺序和指定字段的请求顺序一样。

    redis 127.0.0.1:6379> HSET myhash field1 "foo"
    (integer) 1
    redis 127.0.0.1:6379> HSET myhash field2 "bar"
    (integer) 1
    redis 127.0.0.1:6379> HMGET myhash field1 field2 nofield
    1) "foo"
    2) "bar"
    3) (nil)

    Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。

    此命令会覆盖哈希表中已存在的字段。

    如果哈希表不存在,会创建一个空哈希表,并执行 HMSET 操作。

    如果命令执行成功,返回 OK 。

    redis 127.0.0.1:6379> HMSET myhash field1 "Hello" field2 "World"
    OK
    redis 127.0.0.1:6379> HGET myhash field1
    "Hello"
    redis 127.0.0.1:6379> HGET myhash field2
    "World"

    Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

    Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

    集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

    127.0.0.1:6379> sadd l1 k1
    (integer) 1
    127.0.0.1:6379> sadd l1 k2
    (integer) 1
    127.0.0.1:6379> sadd l1 k3
    (integer) 1
    127.0.0.1:6379> sget l1
    (error) ERR unknown command `sget`, with args beginning with: `l1`, 
    127.0.0.1:6379> smembers l1
    1) "k3"
    2) "k1"
    3) "k2"
     

    Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。

    不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

    有序集合的成员是唯一的,但分数(score)却可以重复。

    集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

    Redis Zadd 命令用于将一个或多个成员元素及其分数值加入到有序集当中。

    如果某个成员已经是有序集的成员,那么更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上。

    分数值可以是整数值或双精度浮点数。

    如果有序集合 key 不存在,则创建一个空的有序集并执行 ZADD 操作。

    当 key 存在但不是有序集类型时,返回一个错误。

    127.0.0.1:6379> zadd myzset 1 one
    (integer) 1
    127.0.0.1:6379> zadd myzset 1 two
    (integer) 1
    127.0.0.1:6379> zadd  myzset 2 three 3 four
    127.0.0.1:6379> zrange myzset 0 -1 withscores
    1) "one"
    2) "1"
    3) "two"
    4) "1"
    5) "three"
    6) "2"
    7) "four"
    8) "3"
    127.0.0.1:6379> zcard myzset
    (integer) 4

    Redis Zcard 命令用于计算集合中元素的数量。

    Redis Zcount 命令用于计算有序集合中指定分数区间的成员数量。

    分数值在 min 和 max 之间的成员的数量。

    127.0.0.1:6379> zcount myzset 1 3
    (integer) 4

    Redis Zincrby 命令对有序集合中指定成员的分数加上增量 increment

    可以通过传递一个负数值 increment ,让分数减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 。

    当 key 不存在,或分数不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member 。

    当 key 不是有序集类型时,返回一个错误。

    分数值可以是整数值或双精度浮点数。

    127.0.0.1:6379> zrange myzset 0 -1 withscores
    1) "one"
    2) "1"
    3) "two"
    4) "1"
    5) "three"
    6) "2"
    7) "four"
    8) "3"
    127.0.0.1:6379> zincrby myzset 3 one
    "4"

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

    默认情况下,结果集中某个成员的分数值是所有给定集下该成员分数值之和。

    返回值

    保存到目标结果集的的成员数量

    # 有序集 mid_test
    redis 127.0.0.1:6379> ZADD mid_test 70 "Li Lei"
    (integer) 1
    redis 127.0.0.1:6379> ZADD mid_test 70 "Han Meimei"
    (integer) 1
    redis 127.0.0.1:6379> ZADD mid_test 99.5 "Tom"
    (integer) 1
    
    # 另一个有序集 fin_test
    redis 127.0.0.1:6379> ZADD fin_test 88 "Li Lei"
    (integer) 1
    redis 127.0.0.1:6379> ZADD fin_test 75 "Han Meimei"
    (integer) 1
    redis 127.0.0.1:6379> ZADD fin_test 99.5 "Tom"
    (integer) 1
    
    # 交集
    redis 127.0.0.1:6379> ZINTERSTORE sum_point 2 mid_test fin_test
    (integer) 3
    
    # 显示有序集内所有成员及其分数值
    redis 127.0.0.1:6379> ZRANGE sum_point 0 -1 WITHSCORES     
    1) "Han Meimei"
    2) "145"
    3) "Li Lei"
    4) "158"
    5) "Tom"
    6) "199"

    Redis Zrangebylex 通过字典区间返回有序集合的成员。

    返回值

    指定区间内的元素列表。

    (integer) 7
    redis 127.0.0.1:6379> ZRANGEBYLEX myzset - [c
    1) "a"
    2) "b"
    3) "c"
    redis 127.0.0.1:6379> ZRANGEBYLEX myzset - (c
    1) "a"
    2) "b"
    redis 127.0.0.1:6379> ZRANGEBYLEX myzset [aaa (g
    1) "b"
    2) "c"
    3) "d"
    4) "e"
    5) "f"
    redis> 

    Redis Zrangebyscore 返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大)次序排列。

    具有相同分数值的成员按字典序来排列(该属性是有序集提供的,不需要额外的计算)。

    默认情况下,区间的取值使用闭区间 (小于等于或大于等于),你也可以通过给参数前增加 ( 符号来使用可选的开区间 (小于或大于)。

    举个例子:

    ZRANGEBYSCORE zset (1 5

    返回所有符合条件 1 < score <= 5 的成员,而

    ZRANGEBYSCORE zset (5 (10

    则返回所有符合条件 5 < score < 10 的成员。

    redis 127.0.0.1:6379> ZADD salary 2500 jack                        # 测试数据
    (integer) 0
    redis 127.0.0.1:6379> ZADD salary 5000 tom
    (integer) 0
    redis 127.0.0.1:6379> ZADD salary 12000 peter
    (integer) 0
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf               # 显示整个有序集
    1) "jack"
    2) "tom"
    3) "peter"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf WITHSCORES    # 显示整个有序集及成员的 score 值
    1) "jack"
    2) "2500"
    3) "tom"
    4) "5000"
    5) "peter"
    6) "12000"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf 5000 WITHSCORES    # 显示工资 <=5000 的所有成员
    1) "jack"
    2) "2500"
    3) "tom"
    4) "5000"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary (5000 400000            # 显示工资大于 5000 小于等于 400000 的成员
    1) "peter"

    Redis Zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。

    返回值

    如果成员是有序集 key 的成员,返回 member 的排名。 如果成员不是有序集 key 的成员,返回 nil 。

    redis 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES        # 显示所有成员及其 score 值
    1) "peter"
    2) "3500"
    3) "tom"
    4) "4000"
    5) "jack"
    6) "5000"
    
    redis 127.0.0.1:6379> ZRANK salary tom                     # 显示 tom 的薪水排名,第二
    (integer) 1
    redis 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES    # 测试数据
    1) "tom"
    2) "2000"
    3) "peter"
    4) "3500"
    5) "jack"
    6) "5000"
    
    redis 127.0.0.1:6379> ZSCORE salary peter 

     redis 的事务管理

    Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

    • 批量操作在发送 EXEC 命令前被放入队列缓存。
    • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
    • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

    一个事务从开始到执行会经历以下三个阶段:

    • 开始事务。
    • 命令入队。
    • 执行事务。
    • https://www.cnblogs.com/kyrin/p/5967620.html感谢博主我做个笔记
    • Redis会将一个事务中的所有命令序列化,然后按顺序执行。Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。 > 在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。
    • 因此,Redis事务能够保证原子性。EXEC命令会触发执行事务中的所有命令。因此,当某个客户端正在执行一次事务时,如果它在调用MULTI命令之前就从Redis服务端断开连接,那么就不会执行事务中的任何操作;相反,如果它在调用EXEC命令之后才从Redis服务端断开连接,那么就会执行事务中的所有操作。当Redis使用只增文件(AOF:Append-only File)时,Redis能够确保使用一个单独的write(2)系统调用,这样便能将事务写入磁盘。然而,如果Redis服务器宕机,或者系统管理员以某种方式停止Redis服务进程的运行,那么Redis很有可能只执行了事务中的一部分操作。Redis将会在重新启动时检查上述状态,然后退出运行,并且输出报错信息。使用redis-check-aof工具可以修复上述的只增文件,这个工具将会从上述文件中删除执行不完全的事务,这样Redis服务器才能再次启动。
    • 一、相关命令

      1. MULTI

      用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,然后才能使用EXEC命令原子化地执行这个命令序列。

      这个命令的运行格式如下所示:

      MULTI

      这个命令的返回值是一个简单的字符串,总是OK。

      2. EXEC

      在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。

      当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令,这种方式利用了检查再设置(CAS)的机制。

      这个命令的运行格式如下所示:

      EXEC

      这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。 当使用WATCH命令时,如果事务执行中止,那么EXEC命令就会返回一个Null值。

      3. DISCARD

      清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。

      如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。

      这个命令的运行格式如下所示:

      DISCARD
      

      这个命令的返回值是一个简单的字符串,总是OK。

      4. WATCH

      当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的。

      这个命令的运行格式如下所示:

      WATCH key [key ...]
      

      这个命令的返回值是一个简单的字符串,总是OK。

      对于每个键来说,时间复杂度总是O(1)。

      5. UNWATCH

      清除所有先前为一个事务监控的键。

      如果你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令。

      这个命令的运行格式如下所示:

      UNWATCH
      

      这个命令的返回值是一个简单的字符串,总是OK。

      时间复杂度总是O(1)。

      二、使用方法

      使用MULTI命令便可以进入一个Redis事务。这个命令的返回值总是OK。此时,用户可以发出多个Redis命令。Redis会将这些命令放入队列,而不是执行这些命令。一旦调用EXEC命令,那么Redis就会执行事务中的所有命令。

      相反,调用DISCARD命令将会清除事务队列,然后退出事务。

      以下示例会原子化地递增foo键和bar键的值:

      <img>

      正如从上面的会话所看到的一样,EXEC命令的返回值是一个数组,其中的每个元素都分别是事务中的每个命令的返回值,返回值的顺序和命令的发出顺序是相同的。

      当一个Redis连接正处于MULTI请求的上下文中时,通过这个连接发出的所有命令的返回值都是QUEUE字符串(从Redis协议的角度来看,返回值是作为状态回复(Status Reply)来发送的)。当调用EXEC命令时,Redis会简单地调度执行事务队列中的命令。

      三、事务内部的错误

      在一个事务的运行期间,可能会遇到两种类型的命令错误:

      一个命令可能会在被放入队列时失败。因此,事务有可能在调用EXEC命令之前就发生错误。例如,这个命令可能会有语法错误(参数的数量错误、命令名称错误,等等),或者可能会有某些临界条件(例如:如果使用maxmemory指令,为Redis服务器配置内存限制,那么就可能会有内存溢出条件)。 
      在调用EXEC命令之后,事务中的某个命令可能会执行失败。例如,我们对某个键执行了错误类型的操作(例如,对一个字符串(String)类型的键执行列表(List)类型的操作)。

      可以使用Redis客户端检测第一种类型的错误,在调用EXEC命令之前,这些客户端可以检查被放入队列的命令的返回值:如果命令的返回值是QUEUE字符串,那么就表示已经正确地将这个命令放入队列;否则,Redis将返回一个错误。如果将某个命令放入队列时发生错误,那么大多数客户端将会中止事务,并且丢弃这个事务。

      然而,从Redis 2.6.5版本开始,服务器会记住事务积累命令期间发生的错误。然后,Redis会拒绝执行这个事务,在运行EXEC命令之后,便会返回一个错误消息。最后,Redis会自动丢弃这个事务。

      在Redis 2.6.5版本之前,如果发生了上述的错误,那么在客户端调用了EXEC命令之后,Redis还是会运行这个出错的事务,执行已经成功放入事务队列的命令,而不会关心先前发生的错误。从2.6.5版本开始,Redis在遭遇上述错误时,会采用先前描述的新行为,这样便能轻松地混合使用事务和管道。在这种情况下,客户端可以一次性地将整个事务发送至Redis服务器,稍后再一次性地读取所有的返回值。

      相反,在调用EXEC命令之后发生的事务错误,Redis不会进行任何特殊处理:在事务运行期间,即使某个命令运行失败,所有其他的命令也将会继续执行。

      这种行为在协议层面上更加清晰。在以下示例中,当事务正在运行时,有一条命令将会执行失败,即使这条命令的语法是正确的:

      <img>

      上述示例的EXEC命令的返回值是批量的字符串,包含两个元素,一个是OK代码,另一个是-ERR错误消息。客户端会根据自身的程序库,选择一种合适的方式,将错误信息提供给用户

      需要注意的是,即使某个命令执行失败,事务队列中的所有其他命令仍然会执行 —— Redis不会停止执行事务中的命令。

      再看另一个示例,再次使用telnet通信协议,观察命令的语法错误是如何尽快报告给用户的:

      <img>

      这一次,由于INCR命令的语法错误,Redis根本就没有将这个命令放入事务队列。

      四、为什么Redis不支持回滚?

      如果你具备关系型数据库的知识背景,你就会发现一个事实:在事务运行期间,虽然Redis命令可能会执行失败,但是Redis仍然会执行事务中余下的其他命令,而不会执行回滚操作,你可能会觉得这种行为很奇怪。

      然而,这种行为也有其合理之处:

      只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。 
      Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力。

      对于Redis事务的这种行为,有一个普遍的反对观点,那就是程序有可能会有缺陷(bug)。但是,你应当注意到:事务回滚并不能解决任何程序错误。例如,如果某个查询会将一个键的值递增2,而不是1,或者递增错误的键,那么事务回滚机制是没有办法解决这些程序问题的。请注意,没有人能解决程序员自己的错误,这种错误可能会导致Redis命令执行失败。正因为这些程序错误不大可能会进入生产环境,所以我们在开发Redis时选用更加简单和快速的方法,没有实现错误回滚的功能。

      五、丢弃命令队列

      DISCARD命令可以用来中止事务运行。在这种情况下,不会执行事务中的任何命令,并且会将Redis连接恢复为正常状态。示例如下所示:

      <img>

      六、通过CAS操作实现乐观锁

      Redis使用WATCH命令实现事务的“检查再设置”(CAS)行为。

      作为WATCH命令的参数的键会受到Redis的监控,Redis能够检测到它们的变化。在执行EXEC命令之前,如果Redis检测到至少有一个键被修改了,那么整个事务便会中止运行,然后EXEC命令会返回一个Null值,提醒用户事务运行失败。

      例如,设想我们需要将某个键的值自动递增1(假设Redis没有INCR命令)。

      首次尝试的伪码可能如下所示:

      val = GET mykey
      val = val + 1
      SET mykey $val
      

      如果我们只有一个Redis客户端在一段指定的时间之内执行上述伪码的操作,那么这段伪码将能够可靠的工作。如果有多个客户端大约在同一时间尝试递增这个键的值,那么将会产生竞争状态。例如,客户端-A和客户端-B都会读取这个键的旧值(例如:10)。这两个客户端都会将这个键的值递增至11,最后使用SET命令将这个键的新值设置为11。因此,这个键的最终值是11,而不是12。

      现在,我们可以使用WATCH命令完美地解决上述的问题,伪码如下所示:

      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC
      

      由上述伪码可知,如果存在竞争状态,并且有另一个客户端在我们调用WATCH命令和EXEC命令之间的时间内修改了val变量的结果,那么事务将会运行失败。

      我们只需要重复执行上述伪码的操作,希望此次运行不会再出现竞争状态。这种形式的锁就被称为乐观锁,它是一种非常强大的锁。在许多用例中,多个客户端可能会访问不同的键,因此不太可能发生冲突 —— 也就是说,通常没有必要重复执行上述伪码的操作。

      七、WATCH命令详解

      那么WATCH命令实际做了些什么呢?这个命令会使得EXEC命令在满足某些条件时才会运行事务:我们要求Redis只有在所有受监控的键都没有被修改时,才会执行事务。(但是,相同的客户端可能会在事务内部修改这些键,此时这个事务不会中止运行。)否则,Redis根本就不会进入事务。(注意,如果你使用WATCH命令监控一个易失性的键,然后在你监控这个键之后,Redis再使这个键过期,那么EXEC命令仍然可以正常工作。)

      WATCH命令可以被调用多次。简单说来,所有的WATCH命令都会在被调用之时立刻对相应的键进行监控,直到EXEC命令被调用之时为止。你可以在单条的WATCH命令之中,使用任意数量的键作为命令参数。

      当调用EXEC命令时,所有的键都会变为未受监控的状态,Redis不会管事务是否被中止。当一个客户单连接被关闭时,所有的键也都会变为未受监控的状态。

      你还可以使用UNWATCH命令(不需要任何参数),这样便能清除所有的受监控键。当我们对某些键施加乐观锁之后,这个命令有时会非常有用。因为,我们可能需要运行一个用来修改这些键的事务,但是在读取这些键的当前内容之后,我们可能不打算继续进行操作,此时便可以使用UNWATCH命令,清除所有受监控的键。在运行UNWATCH命令之后,Redis连接便可以再次自由地用于运行新事务。

      如何使用WATCH命令实现ZPOP操作呢?

      本文将通过一个示例,说明如何使用WATCH命令创建一个新的原子化操作(Redis并不原生支持这个原子化操作),此处会以实现ZPOP操作为例。这个命令会以一种原子化的方式,从一个有序集合中弹出分数最低的元素。以下源码是最简单的实现方式:

      WATCH zset
      element = ZRANGE zset 0 0
      MULTI
      ZREM zset element
      EXEC
      

      如果伪码中的EXEC命令执行失败(例如,返回Null值),那么我们只需要重复运行这个操作即可。

      八、Redis脚本和事务

      根据定义,Redis脚本也是事务型的。因此,你可以通过Redis事务实现的功能,同样也可以通过Redis脚本来实现,而且通常脚本更简单、更快速。

      由于Redis从2.6版本才开始引入脚本特性,而事务特性是很久以前就已经存在的,所以目前的版本才有两个看起来重复的特性。但是,我们不太可能在短时间内移除对事务特性的支持。因为,即使不用求助于Redis脚本,用户仍然能够规避竞争状态,这从语义上来看是适宜的。还有另一个更重要的原因,Redis事务特性的实现复杂度是最小的。

      但是,在相当长的一段时间之内,我们不大可能看到整个用户群体都只使用Redis脚本。如果发生这种情况,那么我们可能会废弃,甚至最终移除Redis事务。

  • 相关阅读:
    第一阶段用户模板和用户场景
    团队开发冲刺第十二天
    第十周总结
    团队开发冲刺第十一天
    团队开发冲刺第十天(实现页面展示评论数与点赞数)
    团队开发冲刺第九天(实现收藏,点赞,关注功能)
    团队开发冲刺第八天(实现评论功能)
    软件用户场景分析
    第九周总结
    团队开发冲刺第六天(新闻详情页的展示)
  • 原文地址:https://www.cnblogs.com/zhulina-917/p/10471057.html
Copyright © 2011-2022 走看看