最近看了《Redis 设计与实现》,机械工业出版社,黄健宏著。
阅读之后,虽然没有很好的理解 Redis 底层数据结构的设计和实现。
但是,还是加深了对 Redis 性能高效方面的认识。
Redis 内部实现的主要数据结构有:简单动态字符串(SDS)、双端链表、字典、压缩列表、整数集合等等。
Redis 没有直接使用这些数据结构,来实现键值对数据库,而是基于这些数据结构,创建了一个对象系统。
包含:字符串对象(strings), 哈希对象(hashes), 列表对象(lists), 集合对象(sets) 和 有序集合对象(sorted sets)。
通过这五种不同类型的对象,Redis 在执行命令之前,根据对象的类型来判断一个对象,是否可以执行给定的命令。
另一个好处在于,使用者针对不同的使用场景,选用最佳的数据对象,从而优化系统的使用效率。
最后总结一下,目前在项目中使用到的 strings 和 sets 的特性和用法。
  1. String 的特性
  Redis 中一个字符串对象,包含两个对象:一个对象用作键值对的键,一个对象用作键值对的值。
  字符串对象值的编码可以是:int、raw 或者 embstr,因此,支持保存:整数、浮点数和字符串。
  如果,字符串对象保存的值为整数,则可以调用 加、减法的计数方法。
  2. String 的方法
  GET key
  返回 key 所关联的字符串值。
  如果 key 不存在则返回特殊值 nil。
  时间复杂度:O(1)
  SETEX key seconds value
  将值 value 关联到 key,并将 key 的生存时间设为 seconds (以秒为单位)。
  如果 key 已经存在,SETEX 命令将覆写旧值。
  这个命令类似于以下两个命令:
  
SET key value EXPIRE key seconds # 设置生存时间不同之处是,SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。
  3. String 的案例
  统计商品当日积分兑换的次数
  特点:该数值,只保存一天,默认值为 0;被兑换一次,则增一。
  
  # 设置 key
  def merchant_exchange_key(merchant_id)
    "merchant:exchange:count:#{merchant_id}:#{Date.today.strftime("%m%d")}"
  end
  # 读取商品当日兑换的次数
  def current_exchange_count(merchant_id)
    key = merchant_exchange_key(merchant_id)
    get_count(key)
  end
  # 设置商品当日兑换的次数
  def increase_exchange_count(merchant_id)
    key = merchant_exchange_key(merchant_id)
    increase_count(key)
  end
  def get_count(key)
    $redis.get(key).to_i
  end
  def set_count(key, count, ttl = 24*3600)
    $redis.setex(key, ttl, count)
  end
  def increase_count(key)
    num = $redis.get(key).to_i
    if num == 0
      set_count(key, 1)
    else
      $redis.incr(key)
    end
  end
  
  1. Set 的特性
  Set 是一组没有序列的字符串的集合体。
  同样包含两个对象:键对象用作键值对的键,值对象用作键值对的值。
  值的编码可以是:intset 或者 hashtable,因此,可以保存:整数和字典,而字典里存储的是字符串。
  集合对象的值是唯一的,重复的值将被覆盖。
  2. Set 的方法
  SADD key member [member ...]
  将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
  假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
  时间复杂度:O(N),N是被添加的元素的数量。
  SMEMBERS key
  返回集合 key 中的所有成员。
  不存在的 key 视为空集合,返回值为 0 。
  时间复杂度:O(N),N为集合的基数。
  SREM key member [member ...]
  移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
  当 key 不是集合类型,返回一个错误。
  时间复杂度:O(N),N为给定member元素的数量。
  3. Set 的案例
  统计用户加入的结伴小组
  特点:默认值为 0,即没有加入任何小组;成功加入小组,则将小组 ID 加入 Set;若用户退出小组,则将小组 ID 移除 Set。
  
  def user_members_key(user_id)
    "user:members:#{user_id}"
  end
  # 读取用户当前加入的小组
  def get_active_members(user_id)
    smembers(user_members_key(user_id)).map{|t| t.to_i }
  end
  # 添加用户当前加入的小组
  def add_active_member(member)
    sadd(user_members_key(member.user_id), member.companion_id)
  end
  # 移除用户当前加入的小组
  def remove_active_member(member)
    srem(user_members_key(member.user_id), member.companion_id)
  end
  def sadd(key, value)
    $redis.sadd(key, value)
  end
  def smembers(key)
    $redis.smembers(key)
  end
  def srem(key, value)
    $redis.srem(key, value)
  end
  
  参考链接
  Redis 官网
  Redis命令参考简体中文版
2015-04-02