为什么Redis是单线程的

首先对于Redis的网络通信模块,它是基于I/O多路复用模型实现的,也就是说只使用一个线程就可以处理多个连接,避免了传统方案使用多线程处理多条连接时的上下文切换开销。而对于命令的执行,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,单线程的处理效率就是最高的,使用多线程反而会增大切换开销,并且引入线程安全的问题,因此Redis使用单线程的方案。

那什么时候应该使用多线程呢?其实对于类似磁盘I/O读写的操作,如果使用单线程执行,当耗时较长时,后面的所有其它请求都会被阻塞在这,这时候就可以使用新的线程去异步的处理之后的一些请求,提高系统的性能。

为什么Redis这么快

  • Redis是基于内存的操作
  • Redis底层使用C语言实现,效率更高
  • Redis使用I/O多路复用,不像传统方案开多个线程处理多个I/O,大大降低了线程切换的开销

字符串

字符串是最基础的类型,字符串长度不能超过512MB。常用的API有如下几个:

  • get key:获取一个keyvalue
  • set key value:设置一个keyvalue
  • del key:删除一个key
  • incr keykey自增1,如果key不存在,自增后keyvalue为1
  • decr keykey自减1,如果key不存在,自减后keyvalue为-1
  • incrby key kkey自增k,如果key不存在,自增后keyvalue为k
  • set key value:不管key是否存在,都设置value
  • setnx key value:当key不存在时才设置value
  • mget key1 key2 key3...:批量获取key,原子操作
  • mset key1 value1 key2 value2 key3 value3:批量设置keyvalue

适用场景:

  • 记录某个用户的页面访问量:incr userid:pageview
  • 实现分布式锁

哈希

哈希可以理解为Map中的Map,因为它每个key对应的value都是一个Map结构,每个field不能相同,但value可以相同。常用的API有如下几个:

  • hget key field:获取key对应的fieldvalue
  • hgetall key:返回key对应所有的fieldvalue
  • hset key field value:设置key对应的fieldvalue
  • hdel key field:删除key对应的fieldvalue
  • hmget key field1 field2 ... fieldN:批量获取key的一批field对应的value
  • hmset key field1 value1 field2 value2 ... fieldN valueN:批量设置key的一批field对应的value

要注意的是,哈希结构无法针对某个指定的field设置超时时间,TTL不好控制。

列表

列表(list)用来存储多个有序(插入顺序)的字符串,每个字符串称为元素,并且元素是可以重复的。常用的API有如下几个:

  • rpush key value1 value2 ... valuen:从列表右端插入元素
  • lpush key value1 value2 ... valuen:从列表左端插入元素
  • linsert key before|after value newValue:在某个key下的list指定的value前|后插入newValue
  • lpop key:从列表左侧弹出一个元素
  • rpop key:从列表右侧弹出一个元素
  • lrem key count value:根据count的值,从列表中删除所有value相等的元素。当count > 0时,从左到右删除最多countvalue相等的元素;当count < 0时,从右到左删除最多Math.abs(count)value相等的元素;当count = 0时,删除所有value相等的元素。
  • ltrim key start end:按照索引范围修剪列表,即删除索引范围之外的元素
  • lrange key start end:获取列表索引范围内的所有元素,包含end
  • blpop key timeoutlpop阻塞版本,timeout是阻塞超时时间,timeout为0表示永久阻塞
  • brpop key timeoutrpop阻塞版本,timeout是阻塞超时时间,timeout为0表示永不阻塞

适用场景:

  • 用户动态的时间轴:当用户更新动态的时候可以使用lpush命令加入列表,使用lrange命令展示一定数量的动态,并且使用ltrim限制动态的数量。
  • 使用lpush + lpop实现一个栈,使用lpush + rpop实现一个队列。
  • 使用lpush + brpop实现一个消息队列。

集合

集合(set)与列表类似,都是用来保存多个字符串,但集合中的元素是无序的,因此不能通过索引来操作元素,并且集合中的元素不能有重复。常用的API有如下几个:

  • sadd key element:向key添加element,如果element已经存在,则添加失败
  • srem key element:将key中的element移除掉
  • sinter key1 key2:取交集
  • sdiff key1 key2:取差集
  • sunion key1 key2:取并集
  • scard key:返回集合中元素的数量
  • sismember key member:判断集合key中是否存在member
  • spop key:从集合中随机弹出一个元素

适用场景:

  • 使用sadd命令给用户添加标签,或者给标签添加用户,通过sinter命令实现共同关注等功能。

有序集合

有序集合与集合一样,元素都不能重复,但有序集合中的元素是有顺序的,与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据。常用的API有如下几个:

  • zadd key score element:添加scoreelementscore是可以重复的,但element是不能重复的。
  • zrem key element:删除元素element
  • zscore key element:返回元素element的分数score
  • zincrby key increScore element:增加或减少元素element的分数score
  • zcard key:返回元素的总个数
  • zrange key start end [WITHSCORES]:返回指定索引范围内的升序元素(相当于排名)
  • zrangebyscore key minScore maxScore [WITHSCORES]:返回指定分数范围内的升序元素
  • zcount key minScore maxScore:返回有序集合内在指定分数范围内的个数

适用场景:

  • 排行榜:可以将销售量、关注人数、时间戳作为score进行元素的排序