为什么Redis是单线程的
首先对于Redis的网络通信模块,它是基于I/O多路复用模型实现的,也就是说只使用一个线程就可以处理多个连接,避免了传统方案使用多线程处理多条连接时的上下文切换开销。而对于命令的执行,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,单线程的处理效率就是最高的,使用多线程反而会增大切换开销,并且引入线程安全的问题,因此Redis使用单线程的方案。
那什么时候应该使用多线程呢?其实对于类似磁盘I/O读写的操作,如果使用单线程执行,当耗时较长时,后面的所有其它请求都会被阻塞在这,这时候就可以使用新的线程去异步的处理之后的一些请求,提高系统的性能。
为什么Redis这么快
- Redis是基于内存的操作
- Redis底层使用C语言实现,效率更高
- Redis使用I/O多路复用,不像传统方案开多个线程处理多个I/O,大大降低了线程切换的开销
字符串
字符串是最基础的类型,字符串长度不能超过512MB。常用的API有如下几个:
get key
:获取一个key
的value
set key value
:设置一个key
的value
del key
:删除一个key
incr key
:key
自增1,如果key
不存在,自增后key
的value
为1decr key
:key
自减1,如果key
不存在,自减后key
的value
为-1incrby key k
:key
自增k,如果key
不存在,自增后key
的value
为kset key value
:不管key
是否存在,都设置value
setnx key value
:当key
不存在时才设置value
mget key1 key2 key3...
:批量获取key
,原子操作mset key1 value1 key2 value2 key3 value3
:批量设置key
的value
适用场景:
- 记录某个用户的页面访问量:
incr userid:pageview
- 实现分布式锁
哈希
哈希可以理解为Map中的Map,因为它每个key对应的value都是一个Map结构,每个field不能相同,但value可以相同。常用的API有如下几个:
hget key field
:获取key
对应的field
的value
hgetall key
:返回key
对应所有的field
和value
hset key field value
:设置key
对应的field
的value
hdel key field
:删除key
对应的field
的value
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时,从左到右删除最多count
个value
相等的元素;当count
< 0时,从右到左删除最多Math.abs(count)
个value
相等的元素;当count
= 0时,删除所有value
相等的元素。ltrim key start end
:按照索引范围修剪列表,即删除索引范围之外的元素lrange key start end
:获取列表索引范围内的所有元素,包含end
blpop key timeout
:lpop
阻塞版本,timeout
是阻塞超时时间,timeout
为0表示永久阻塞brpop key timeout
:rpop
阻塞版本,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
:添加score
和element
,score
是可以重复的,但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
进行元素的排序