2. 数据结构2.1 Key
2.2 String最普通的key-value类型,说是String,其实是任意的byte[],比如图片,最大512M。 所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存Session,为了简化架构甚至可以替换掉Memcached。 Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用来做计数器,做自增序列。key不存在时会创建并贴心的设原值为0。IncrByFloat专门针对float,没有对应的decrByFloat版本?用负数啊。 SetNx, 仅当key不存在时才Set。可以用来选举Master或做分布式锁:所有Client不断尝试使用SetNx master myName抢注Master,成功的那位不断使用Expire刷新它的过期时间。如果Master倒掉了key就会失效,剩下的节点又会发生新一轮抢 夺。 其他Set指令:
GetBit/SetBit/BitOp,与或非/BitCount, BitMap的玩法,比如统计今天的独立访问用户数时,每个注册用户都有一个offset,他今天进来的话就把他那个位设为1,用BitCount就可以得出今天的总人数。 Append/SetRange/GetRange/StrLen,对文本进行扩展、替换、截取和求长度,只对特定数据格式如字段定长的有用,json就没什么用。 2.3 HashKey-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。 另一个用法是土法建索引。比如User对象,除了id有时还要按name来查询。可以有如下的数据记录:
底层实现是hash table,一般操作复杂度是O(1),要同时操作多个field时就是O(N),N是field的数量。 2.4 ListList是一个双向链表,支持双向的Pop/Push,江湖规矩一般从左端Push,右端Pop——LPush/RPop,而且还有Blocking的版本BLPop/BRPop,客户端可以阻塞在那直到有消息到来,所有操作都是O(1)的好孩子,可以当Message Queue来用。当多个Client并发阻塞等待,有消息入列时谁先被阻塞谁先被服务。任务队列系统Resque是其典型应用。 还有RPopLPush/ BRPopLPush,弹出来返回给client的同时,把自己又推入另一个list,LLen获取列表的长度。 还有按值进行的操作:LRem(按值删除元素)、LInsert(插在某个值的元素的前后),复杂度是O(N),N是List长度,因为List的值不唯一,所以要遍历全部元素,而Set只要O(log(N))。 按下标进行的操作:下标从0开始,队列从左到右算,下标为负数时则从右到左。
复杂度也是O(N),其中LSet的N是List长度,LIndex的N是下标的值,LRange的N是start的值+列出元素的个数,因为是链表而不是数组,所以按下标访问其实要遍历链表,除非下标正好是队头和队尾。LTrim的N是移除元素的个数。 在消息队列中,并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办?
|