前言
相关系列
- 《Redis & 目录》(持续更新)
- 《Redis & 基础 & 源码》(学习过程/多有漏误/仅作参考/不再更新)
- 《Redis & 基础 & 总结》(学习总结/最新最准/持续更新)
- 《Redis & 基础 & 问题》(学习解答/持续更新)
Redis是什么?有哪些应用场景?
Redis的全称为Remote Dictionary Server(远程字典服务),是一个基于C语言开发的开源/高性能/非关系型的数据库。与传统数据库不同的是:Redis会直接将数据保存在内存中,因此其读/写速度非常之快。据估算Redis每秒可以处理10/8万次以上的读/写,是目前已知性能最好的键/值型数据库,因此被广泛应用于缓存方向,并在分布式锁/消息通知等方面也有不小的运用…常用应用场景如下:
- 热点缓存:将热点数据写入内存中读取可有效减少磁盘I/O并提升并发量,从而有效增强读取性能;
- 会话缓存:Redis可储存全局性的会话信息(Token & 令牌)以支持对整个系统的全局访问,并有效提高系统整体的可用性/伸缩性;
- 全页缓存:Redis可缓存整个页面的输出以减少接口调用/页面渲染的时间;
- 计数器/排行榜:Redis可通过多种方式对整数进行快速增/减以实现计数器功能。并且由于Redis的读/写性能很高,因此很适合存储频繁读/写的计数量。此外计数器功能还可配合有序集合类以进一步实现实时排行榜功能;
- 数据共享:Redis可作为共享数据的媒介以在多服务之间传递共享数据;
- 消息队列(发布/订阅功能):Redis列表类型的底层结构为双向链表,因此可通过首/尾两端的插入/移除实现轻量级的消息队列功能以处理异步任务,不过该需求更推荐使用Kafka/RabbitMQ等专业的消息中间件实现;
- 分布式锁 :作为多服务共享的中间件,Redis可使用自带的SETNX命令/官方提供的API以提供分布式锁功能,从而确保在多服务之间共享资源的一致性;
- 限流:Redis可通过增/减整数,结合Lua脚本等方式统计总数以判断是否到达阈值以实现限流功能。
Redis与Memcached有什么区别?
- Redis的读/写速度比Memcached快很多;
- Redis支持数据持久化;
- Redis支持更多的数据类型,而Memcached只支持最基本的键/值。
为什么要用缓存?
这主要是出于性能方面的考量。内存的数据读取速度/并发承受量都远高于磁盘,因此将热点数据直接存于内存中将有效避免大量低效的数据库/磁盘I/O,并提升读取的最大并发量,而这两点都能为性能带来极大的收益。
为什么更推荐Redis,而不是使用Map/Guava做缓存?
缓存分为本地缓存和分布式缓存。本地缓存的优点是轻量/快速,但缺点是生命周期与服务绑定,并且由于在多实例(分布式/集群)情况下每个实例都拥有各自的缓存,因此很容易在开发/使用时出现缓存不一致问题,Map/Guava就属于该类型的缓存。而Redis作为分布式缓存其优点是生命周期与服务无关,并且多实例(表面上)使用的缓存都相同,因此便保证了数据的访问性/一致性。这种特性明显更加符合当今服务的大规模分布式应用场景,因此相比Map/Guava而言更推荐使用Redis做缓存。当然Redis也不是毫无缺点,在保证高可用的情况下,其整体架构设计/实现都较复杂。
Redis有哪些优缺点?
优点
- 读/写性能优异:Redis的读/写速度为110000/81000次/s;
- 使用便利:Redis支持五/三大基础/特殊数据类型,这使得其可以快速支持绝大多数的数据操作;
- 支持事务/批处理:Redis的所有基础操作都是原子的,并支持将多个基础操作打包执行;
- 支持数据持久化:Redis支持RDB/AOF两种数据持久化机制,其本质分别是数据快照/指令日志;
- 支持主从备份:Redis支持一主多从/级联等多种集群部署方案,主机会自动将数据同步到从机,从而实现读写分离。
缺点
- 容量受内存限制:受限于物理内存昂贵而偏小的原因,Redis难以用于海量数据的存储,仅适用于微量数据的高性能读/写/运算;
- 事务缺乏ACID属性:Redis事务只支持“伪”原子性/“读已递交”级别的隔离性,不支持数据回滚,并且不具备一致性/持久性;
- 数据丢失:即使是在启动了持久化机制及主从同步的情况下,数据也无法完全避免丢失的情况发生;
- 集群难以在线扩容:集群在内存耗尽时想要对进行在线扩容会变得很复杂,因为内存难以像磁盘一样动态增/删,因此运维人员在系统上线时必须确保有足够的内存来避免该问题,但这又会对资源造成了较大浪费。
Redis为什么这么快?
基础数据类型
- String & 字符串
字符串类型是Redis最基本的数据类型,一个键对应一个值,但值最大不能超过512M;
字符串类型不仅可以保存字符串,还可以保存数字,并支持递增/递减等操作;
字符串类型是二进制安全的,这意味着它可以保存任何数据,比如JPEG图像或者序列化对象。 - List & 列表
列表类型是字符串类型的列表,会按元素的插入顺序进行排序;
列表类型的底层实现是双向链表,因此支持首/尾两端的插入/移除/获取,并且时间复杂度都为O(1)。 - Set & 集合
集合类型是无序的字符串类型集合,会自动对插入的元素去重。 - Zset & 有序集合
有序集合类型是有序的字符串类型集合,会自动对插入的元素去重;
有序集合类型与集合类型类似,但每个元素都会关联一个double类型的分数,并按分数进行排序;
有序集合类型是唯一既可以进行范围查询又能够保持元素唯一性的数据结构。 - Hash & 哈希
哈希类型的本质是键值对,并且其键/值都是字符串类型;
哈希类型非常适合存储对象,因为其键/值结构与类的字段/值结构高度相似。
特殊数据类型
- Bitmaps & 位图
位图类型不是独立数据类型,而是基于字符串类型实现;
位图类型的底层结构是位数组,其会将元素代表的唯一整数ID作为索引,用于在位数组中定位以记录元素是否存在而不记录元素本身。因此位图类型非常节省空间,也可以高效统计大量数据,如统计在线用户数等。 - HyperLogLog & 基数
基数类型可用于统计集合中不重复元素的数量;
基数类型非常节省空间,但是它只能给出近似值而非精确值。 - Geo & 地理
地理类型用于存储经纬度等地理信息。 - Streams & 流
流类型提供了消息持久化/消费者组/消息确认等机制,是Redis官方推荐的消息队列解决方案。
Redis有哪些数据类型?
- 硬件上基于内存实现,这是Redis快速的核心原因;
- 算法上基于哈希实现,并在此基础上提供/优化了哈希/列表/集合等高效的数据结构,使得读/写可以在时间复杂度为O(1)的操作中完成;
- 指令采用单线程执行,避免了线程/上下文切换的开销,也无需考虑锁相关的影响,并简化了数据处理的逻辑;
- 采用非阻塞的多路I/O复用模型,使得单线程能够并发/高效地处理多客户端连接,减少了Redis在网络I/O上的等待,并且在Redis 6.0版本中引入了多线程机制进一步提升了网络I/O的处理性能。
如何最小化使用Redis内存?
尽可能多的将相同对象的多项数据以哈希类型紧凑保存,这将大幅减少内存的消耗量