VoteArticle 函数中,如果将投票用户添加到投票用户集合中后,还没来得及给文章的相关信息进行设置,那么这个用户以后不能再投票,并且文章的投票信息不对。
不是太明白究竟在竞争什么,只能针对以上问题处理。用事务只能再添加一个集合在事务中标记事务是否执行成功,处理流程大致如下:
先将用户与文章作为值加入到这个集合
再将用户加入到投票集合中
然后开启事务,依次发送更新信息的命令和删除那个集合中的相关信息,并执行
最后有一个 worker 扫描这个集合,将其中值拿出来解析出用户和文章,再查改用户是否已在集合中,如果在集合中,则重新执行 步骤3,最后删除该值
练习题:提高性能 P58简单实践 - 文章投票 中 ListArticles 函数在获取整个页面的文章时,需要在 Redis 与客户端之间最多会进行 26 次通信往返,这种做法十分低效,你能否想个办法将 ListArticles 函数的往返次数降低为 2 次呢?
提示:使用流水线
获取文章列表时,先获取相应的 id 列表(最多 25 个),再循环获取每个 id 对应的文章,所以最多会进行 26 次通信往返
由于必须先获取 id 列表,再获取每个 id 对应的文章,所以只能将这两块分开,所以最低至少有 2 次通信往返。大致流程如下:
先获取 id 列表
使用流水线,依次将获取每个 id 的文章的命令发送至缓冲区,最后与服务端通信并执行命令(Go 中可以使用上述事务演示代码的方式进行操作 )
最后按照顺序解析结果
过期时间 P58只有少数几个命令可以原子地为键设置过期时间,并且对于列表、集合、哈希表和有序集合这样的容器来说,键过期命令只能为整个键设置过期时间,而没办法为键里面的单个元素设置过期时间(可以使用存储时间戳的有序集合来实现针对单个元素的过期时间;也可以以前缀的形式将容器中的单个元素变为字符串)。 P58
用于处理过期时间的 Redis 命令 P59 命令 格式 描述PERSIST PERSIST key 移除键的过期时间
TTL TTL key 查看键距离过期时间还有多少秒
EXPIRE EXPIRE key seconds 让键在指定的秒数之后过期
EXPIREAT EXPIREAT key timestamp 让键在指定的 UNIX 秒级时间戳过期
PTTL PTTL key 查看键距离过期时间还有多少毫秒
PEXPIRE PEXPIRE key milliseconds 让键在指定的毫秒数之后过期
PEXPIREAT PEXPIREAT key milliseconds-timestamp 让键在指定的 UNIX 毫秒级时间戳过期
相关演示代码如下:
// 指定过期时间相关的命令 func executeExpirationOperation(conn redis.Conn) { // 删除原有值 handleResult(redis.Int(conn.Do("DEL", "string"))) // 设置字符串的值为 value,输出 -> OK,string 变为 -> value handleResult(redis.String(conn.Do("SET", "string", "value"))) // 查看 string 的过期时间,输出 -> -1,表示不过期 handleResult(redis.Int(conn.Do("TTL", "string"))) // 设置 string 在 3 秒后过期,输出 -> 1 handleResult(redis.Int(conn.Do("EXPIRE", "string", 3))) time.Sleep(time.Second) // 查看 string 的过期时间,输出 -> 2 handleResult(redis.Int(conn.Do("TTL", "string"))) // 移除 string 的过期时间,输出 -> 1 handleResult(redis.Int(conn.Do("PERSIST", "string"))) // 查看 string 的过期时间,输出 -> -1,表示不过期 handleResult(redis.Int(conn.Do("TTL", "string"))) // 设置 string 在当前时间 2500 毫秒后过期,输出 -> 1 handleResult(redis.Int(conn.Do("PEXPIREAT", "string", time.Now().UnixNano() / 1e6 + 2500))) time.Sleep(time.Second) // 查看 string 的过期时间,输出 -> 1499,表示还有 1499 毫秒过期 handleResult(redis.Int(conn.Do("PTTL", "string"))) time.Sleep(2 * time.Second) // 查看 string 的过期时间,输出 -> -2,表示已过期 handleResult(redis.Int(conn.Do("PTTL", "string"))) } 练习题:使用 EXPIRE 命令代替时间戳有序集合 P59在简单实践 - Web应用中使用了一个根据时间戳排序、用于清除会话信息的有序集合,通过这个有序集合,程序可以在清理会话的时候,对用户浏览过的商品以及用户购物车里面的商品进行分析。但是,如果我们决定不对商品进行分析的话,那么就可以使用 Redis 提供的过期时间操作来自动清理过期的会话信息,而无须使用清理函数。那么,你能否修改简单实践 - Web应用中定义的 UpdateToken 函数和 UpdateCartItem 函数,让它们使用过期时间操作来删除会话信息,从而代替目前使用有序集合来记录并清除会话信息的做法呢?