相关演示代码如下:
// 执行 SORT 命令 func executeSortOperation(conn redis.Conn) { // 删除原有值 handleResult(redis.Int(conn.Do("DEL", "id", "age", "name", "destination"))) // 初始化 handleResult(redis.Int(conn.Do("RPUSH", "id", 1, 4, 3, 2, 5))) handleResult(redis.String(conn.Do("SET", "age_1", 15))) handleResult(redis.String(conn.Do("SET", "age_2", 14))) handleResult(redis.String(conn.Do("SET", "age_3", 11))) handleResult(redis.String(conn.Do("SET", "age_4", 12))) handleResult(redis.String(conn.Do("SET", "age_5", 10))) handleResult(redis.String(conn.Do("SET", "name_1", "tom"))) handleResult(redis.String(conn.Do("SET", "name_2", "jerry"))) handleResult(redis.String(conn.Do("SET", "name_3", "bob"))) handleResult(redis.String(conn.Do("SET", "name_4", "mary"))) handleResult(redis.String(conn.Do("SET", "name_5", "jack"))) // 根据 id 降序排序,跳过第一个元素,获取接下来的两个元素,输出 -> [4 3] handleResult(redis.Ints(conn.Do("SORT", "id", "LIMIT", "1", "2", "DESC"))) // 根据 age_{id} 升序排序,按照 id age_{id} name_{id} 顺序返回结果,输出 -> [5 10 jack 3 11 bob 4 12 mary 2 14 jerry 1 15 tom] handleResult(redis.Strings(conn.Do("SORT", "id", "BY", "age_*", "GET", "#", "GET", "age_*", "GET", "name_*", "ALPHA"))) // 根据 name_{id} 字典序降序排序,按照 id age_{id} name_{id} 顺序返回结果,存储到 destination 中 // 输出 -> 15 handleResult(redis.Int(conn.Do("SORT", "id", "BY", "name_*", "GET", "#", "GET", "age_*", "GET", "name_*", "ALPHA", "DESC", "STORE", "destination"))) // 输出 列表 结果,输出 -> [1 15 tom 4 12 mary 2 14 jerry 5 10 jack 3 11 bob] handleResult(redis.Strings(conn.Do("LRANGE", "destination", 0, -1))) } 基本的 Redis 事务Redis 有 5 个命令可以让用户在不被打断的情况下对多个键执行操作,它们分别是: WATCH 、 MULTI 、 EXEC 、UNWATCH 和 DISCART 。基本的 Redis 事务只用 MULTI 和 EXEC 即可,使用多个命令的事务将在以后进行介绍。 P56
Redis 的基本事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令。当一个事务执行完毕之后, Redis 才会处理其他客户端的命令。 P56
假如某个(或某些) key 正处于 WATCH 命令的监视之下,且事务块中有和这个(或这些) key 相关的命令,那么 EXEC 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效,否则该事务被打断(abort)。
命令 格式 描述MULTI MULTI 标记一个事务块的开始,总是返回 OK
EXEC EXEC 执行所有事务块内的命令,按顺序返回命令的执行结果。当操作被打断时,返回 nil
相关演示代码如下:
// 执行事务命令 func executeTransactionOperation(conn redis.Conn) { // 删除原有值 handleResult(redis.Int(conn.Do("DEL", "counter"))) // 开启事务(采用流水线方式,降低通信开销) handleResult(nil, conn.Send("MULTI")) // 事务中执行自增操作(采用流水线方式,降低通信开销) handleResult(nil, conn.Send("INCR", "counter")) handleResult(nil, conn.Send("INCR", "counter")) handleResult(nil, conn.Send("INCR", "counter")) // 执行命令,依次执行自增操作,分别返回操作结果,输出 -> [1 2 3] handleResult(redis.Ints(conn.Do("EXEC"))) } 练习题:移除竞争条件 P58简单实践 - 文章投票 中 VoteArticle 函数内曾说明没有事务控制,会存在并发问题。该函数包含一个竞争条件以及一个因为竞争条件而出现的 bug 。函数的竞争条件可能会造成内存泄漏,而函数的 bug 则可能会导致不正确的投票结果出现。你能想办法修复它们吗?
提示:如果你觉得很难理解竞争条件为什么会导致内存泄漏,那么可以在分析 简单实践 - 文章投票 中的 PostArticle 的函数的同时,阅读一下 6.2.5 节。
感觉还是无法理解为什么会有这种情况,强行猜测以下可能性(虽然都不是竞争条件造成的):
PostArticle 函数中,在将作者加入到投票用户集合中后,给其设定过期时间。如果设定过期时间之前由于某些原有异常导致没有进行相关操作,那么这个集合将一直在内存中,不会过期,从而造成内存泄漏。