Redis - 功能特点、应用场景、单线程模型详解(附:与Memcache对比)
作者:hangge | 2024-11-27 08:43
一、基本介绍
1,什么是 Redis
Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、向键值对(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)做数据过期处理
- 我们可以将 sorted set 的 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)Redis 6.0 之前,Redis 是一个完全的单线程服务。即 Redis 在处理客户端的请求时,包括读 socket、解析、执行、写 socket 等都由一个顺序串行的主线程处理。作者之所以这么设计,主要是为了保证 Redis 的快速和高效。如果涉及多线程,则需要使用锁机制来解决并发问题,这样执行效率反而会打折扣。(2)Redis 6.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 线程就负责读写数据。
注意:默认情况下,多线程模式是被禁用了的,需要显式地开启。
- 当客户端发出请求的时候,主线程会收到一个可读的事件,于是它把对应的客户端丢到可读的客户端列表。
- 一个 IO 线程会被安排读写这个客户端发过来的命令,并且解析好。
- 紧接着主线程会执行 IO 线程解析好的命令,并且把响应放回到可写客户端列表里面。
- IO 线程负责写回响应。整个过程就结束了。
(3)所以整个 Redis 在多线程模式下,可以看作是单线程 Reactor、单线程 Acceptor 和多线程 Handler 的 Reactor 模式。只不过 Redis 的主线程同时扮演了 Reactor 中分发事件的角色,也扮演了接收请求的角色。同时多线程 Handler 在 Redis 里面仅仅是读写数据,命令的执行还是依赖于主线程来进行的。
提示:虽然说现在 Redis 的 IO 改成多线程之后能够有效利用多核性能,但是大部分情况下都是不推荐使用多线程模式的。道理很简单,Redis 在单线程模式下的性能就足以满足绝大多数使用场景了,那么用不用多线程已经无所谓了。
全部评论(0)