Redis - 功能特点、应用场景、单线程模型详解(附:与Memcache对比)
Redis - 功能特点、应用场景、单线程模型详解(附:与Memcache对比)
Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、向键值对(Key-Value)数据类型的NoSQL数据库,可以满足对海量数据的快速读写需求,并提供多种语言的API。
一、基本介绍
1,什么是 Redis
Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、向键值对(Key-Value)数据类型的NoSQL数据库,可以满足对海量数据的快速读写需求,并提供多种语言的API。
2,Redis 的起源
- Redis是意大利人antirez发明的,起初是为了解决网站的负载问题。作者当初运营了一个访客信息网站,存储访客的浏览记录,通过列表的形式进行维护。
- 当时网站的数据存储使用的是MySQL,随着用户越来越多,需要维护的列表数量也越来越多,要执行的入栈和出栈操作也越来越多。使用MySQL执行入栈和出栈操作是需要硬盘读写操作的,所以程序的性能严重受制于硬盘的I/O。
- 作者希望在不改变硬件的基础上,通过提升列表的性能来解决负载问题。于是决定自己写一个具有列表结构的内存数据库原型,最重要的是将数据存储于内存而不是磁盘,这样程序的性能就不会受制于磁盘I/O。
- 当时发现这样确实解决了问题,所以作者使用C语言重写了这个内存数据库原型,并增加了持久化等功能。这样Redis就诞生了。
3,Redis 的特点
- 高性能:官方提供的数据,Redis读的速度是110000次/s,写的速度是81000次/s。
- 原子性:Redis支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 持久存储:支持RDB和AOF这两种方式的持久化,可以把内存中的数据持久化到磁盘中。
- 支持主从复制:主机会自动将数据同步到从机,可以进行读写分离,以实现负载均衡及高可用。
- 支持集群:从3.0版本开始支持。
- 数据结构丰富:支持string类型的value外还支持hash、set、zset、list等数据结构。
4,一个 Redis 实例最多能存放多少 key?
答案是没有限制。因为Redis本身是不会限制存储多少key的,但是Redis是基于内存的,它的存储极限是系统中的可用内存值,如果内存存满了,那就无法再存储key了。
5,Redis 的应用场景
(1)做缓存
- 这是Redis使用最多的场景,它不仅够替代Memcached。而且相比于memcached,Redis还提供了丰富的数据结构,并且提供RDB和AOF等持久化机制。
- 使用Redis,不需要每次都重新生成数据,而且它的缓存速度和查询速度比较快,使用也比较方便。比如,实现数据查询、缓存新闻消息内容、缓存商品内容或购物车等。
(2)做计数器应用
- Redis的命令具有原子性,它提供了INCR、DECR、GETSET、INCRBY等相关命令来构建计数器系统。
- 可以使用Redis来记录一个热门帖子的转发数、评论数。通过Redis的原子递增,可以实现在任何时候封锁一个IP地址等。
(3)实现消息队列系统
- Redis运行稳定,速度快,支持模式匹配,也可以实现消息订阅发布。
- Redis还有阻塞队列的命令,能够让一个程序在执行时被另一个程序添加到队列中。比如,实现秒杀、抢购等。
(4)做实时系统、消息系统
- 可以利用Redis的set功能做实时系统,来查看某个用户是否进行了某项操作,对其行为进行统计对比。
- 也可以利用Redis的Pub/Sub构建消息系统,如在线聊天系统。
(5)实现排行榜应用
- 排行榜的实现利用了Redis的有序集合。比如,对上百万个用户的排名,采用其他数据库来实现是非常困难的,而利用Redis的ZADD、ZREVRANGE、ZRANK等命令可以轻松实现排名并获取排名的用户。
(6)做数据过期处理
- 我们可以将sortedset的score值设置成过期时间的时间戳,然后通过过期时间排序,找出过期的数据进行删除。
- 可以采用过期属性来确认一个关键字在什么时候应该被删除。也可以利用UNIX时间作为关键字,将列表按时间排序。对currenttime和timeto_live进行检索,查询出过期的数据,进而删除。
(7)做大型社交网络
- 任何架构的系统或网站都可以与Redis很好地结合,同样,采用Redis可以很好地与社交网络相结合,如新浪微博、Twitter等。
- 比如,我们在使用QQ时,进行实时聊天就需要Redis的支持;又如,我们在浏览微博时,实现信息的刷新、浏览查看等也需要Redis的支持。
(8)分布式集群架构中的session分离
- 采用分布式集群部署,可以满足一个Web应用系统被大规模访问的需要。而要实现分布式集群部署,就要解决session统一的问题。通常可以采用Redis来实现session共享机制,以达到session统一的目的。
(9)分布式锁实现
- 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用Redis自带的SETNX命令实现分布式锁,除此之外,还可以使用官方提供的RedLock分布式锁实现。
(10)位操作
- reids位操作也叫位数组操作、bitmap,它提供了SETBIT、GETBIT、BITCOUNT、BITTOP四个命令用于操作二进制位数组。每个位可以存储0或1,因此Redis的位操作非常灵活,可以用于多种应用场景。
- 通过在Redis中使用位操作,可以实现一个非常节省内存的布隆过滤器,用于判断某个元素是否存在于集合中。
- 使用Redis的位操作来实现计数器,其中每个位代表一个计数值。这在一些场景下可以用于统计用户的活跃状态、点击次数等。
- 在某些场景下,可以使用位操作来构建位图索引,用于快速检索某些属性的相关数据。例如,可以用位图来表示用户的活跃状态,每个位表示一个小时,从而可以迅速了解用户的活跃时间。
6,Redis 线程模型
(1)Redis6.0之前,Redis是一个完全的单线程服务。即Redis在处理客户端的请求时,包括读socket、解析、执行、写socket等都由一个顺序串行的主线程处理。作者之所以这么设计,主要是为了保证Redis的快速和高效。如果涉及多线程,则需要使用锁机制来解决并发问题,这样执行效率反而会打折扣。
(2)Redis6.0之后改多线程,但要注意多线程并非是完全摒弃单线程,redis还是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程。这样做的目的是因为redis的性能瓶颈在于网络IO而非CPU,使用多线程能提升IO读写的效率,从而整体提高redis的性能。
提示:使用Redis时,几乎不存在CPU成为瓶颈的情况,Redis主要受限于内存和网络。例如在一个普通的Linux系统上,Redis通过使用pipelining每秒可以处理100万个请求,所以如果应用程序主要使用O(N)或O(log(N)) 的命令,它几乎不会占用太多CPU。
附一:Redis 和 Memcached 的对比
1,二者之间的共同点与区别
(1)Redis与Memcached共同点:
- 都是基于内存的数据库,一般都用来当做缓存使用。
- 都有过期策略。
- 两者的性能都非常高。
(2)Redis与Memcached区别:
- Redis支持的数据类型更丰富(String、Hash、List、Set、ZSet),而Memcached只支持最简单的key-value数据类型;
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memcached没有持久化功能,数据全部存在内存之中,Memcached重启或者挂掉后,数据就没了;
- Redis原生支持集群模式,Memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
- Redis支持发布订阅模型、Lua脚本、事务等功能,而Memcached不支持;
2,为什么 Memcache 使用多线程,而 Redis 使用单线程模式?
(1)Redis使用单线程模式的理由有很多。首先有两个显著的优点:
- 不会引入上下文切换的开销,也没有多线程访问资源的竞争问题。
- 其次Redis是一个内存数据库,操作很快,所以它的性能瓶颈只可能出现在网络IO和内存大小上,是不是多线程影响不大。
- 最后,单线程模式比较好理解,调试起来也容易。
(2)Memcache采用了多线程设计,那么带来的后果就是会有线程上下文切换的开支,并且多线程模式下需要引入锁来保护共享资源。优点则是Memcache会比Redis更充分地利用多核CPU的性能。
(3)当然,这就是一个设计者的偏好问题,没有说哪种设计一定更好。网上发布了Redis和Memcache的性能对比。基本上就是有些时候Redis好一点,有些时候Memcache好一点。
附二:Redis 为什么引入多线程?
(1)Redis在6.0引入多线程的原因只有一个,那就是性能。当Redis启用了多线程之后,里面的主线程就要负责接收事件、创建连接、执行命令。Redis的IO线程就负责读写数据。
注意:默认情况下,多线程模式是被禁用了的,需要显式地开启。
(2)整个请求的处理过程如下:
- 当客户端发出请求的时候,主线程会收到一个可读的事件,于是它把对应的客户端丢到可读的客户端列表。
- 一个IO线程会被安排读写这个客户端发过来的命令,并且解析好。
- 紧接着主线程会执行IO线程解析好的命令,并且把响应放回到可写客户端列表里面。
- IO线程负责写回响应。整个过程就结束了。
(3)所以整个Redis在多线程模式下,可以看作是单线程Reactor、单线程Acceptor和多线程Handler的Reactor模式。只不过Redis的主线程同时扮演了Reactor中分发事件的角色,也扮演了接收请求的角色。同时多线程Handler在Redis里面仅仅是读写数据,命令的执行还是依赖于主线程来进行的。
提示:虽然说现在Redis的IO改成多线程之后能够有效利用多核性能,但是大部分情况下都是不推荐使用多线程模式的。道理很简单,Redis在单线程模式下的性能就足以满足绝大多数使用场景了,那么用不用多线程已经无所谓了。