1-redis 基础
1.1- Redis 是什么?简述它的优缺点?
Redis 本质上是一个 Key-Value 类型的内存数据库,很像 Memcached,整个数据库加载在内存当中操作,定期通过异步操作把数据库中的数据 flush 到硬盘上进行保存。
因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value 数据库。
优点:
-
读写性能极高, Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s。
-
支持数据持久化,支持 AOF 和 RDB 两种持久化方式。
-
支持事务, Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
-
数据结构丰富,除了支持 string 类型的 value 外,还支持 hash、set、zset、list 等数据结构。
-
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
-
丰富的特性 – Redis 还支持 publish/subscribe, 通知, key 过期等特性。
缺点:
-
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。
-
主机宕机,宕机前有部分数据未能及时同步到从机,切换 IP 后还会引入数据不一致的问题,降低了系统的可用性。
1.2-Redis 为什么这么快?
-
内存存储:Redis是使用内存(in-memeroy)存储,没有磁盘 IO 上的开销。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1)。
-
单线程实现( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。注意:单线程是指的是在核心网络模型中,网络请求模块使用一个线程来处理,即一个线程处理所有网络请求。
-
非阻塞IO:Redis 使用多路复用 IO 技术,将 epoll 作为 I/O 多路复用技术的实现,再加上 Redis 自身的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。
-
优化的数据结构:Redis 有诸多可以直接应用的优化数据结构的实现,应用层可以直接使用原生的数据结构提升性能。
-
使用底层模型不同:Redis 直接自己构建了 VM (虚拟内存)机制 ,因为一般的系统调用系统函数的 话,会浪费一定的时间去移动和请求;
Redis 的 VM( 虚拟内存)机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过 VM 功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。 Redis 提高数据库容量的办法有两种:一种是可以将数据分割到多个 RedisServer上;另一种是使用虚拟内存把那些不经常访问的数据交换到磁盘上。需要特别注意的是 Redis 并没有使用 OS 提供的 Swap ,而是自己实现。
1.3-Redis 相比 Memcached 有哪些优势?
-
数据类型:Memcached 所有的值均是简单的字符串,Redis 支持更为丰富的数据类型,支持 string(字符串),list(列表),Set(集合)、Sorted Set(有序集合)、Hash(哈希)等。
-
持久化:Redis 支持数据落地持久化存储,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 memcache 不支持数据持久存储 。
-
集群模式:Redis 提供主从同步机制,以及 Cluster 集群部署能力,能够提供高可用服务。Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据
-
性能对比:Redis 的速度比Memcached快很多。
-
网络IO模型:Redis 使用单线程的多路 IO 复用模型,Memcached 使用多线程的非阻塞IO模式。
-
Redis 支持服务器端的数据操作:Redis 相比 Memcached 来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在 Memcached 里,你需要将数据拿到客户端来进行类似的修改再 set 回去。这大大增加了网络 IO 的次数和数据体积。在 Redis 中,这些复杂的操作通常和一般的 GET/SET 一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么 Redis 会是不错的选择。
1.4-为什么要用 Redis 做缓存?
从高并发上来说:
-
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
从高性能上来说:
-
用户第一次访问数据库中的某些数据。 因为是从硬盘上读取的所以这个过程会比较慢。将该用户访问的数据存在缓存中,下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据。
1.5-为什么要用 Redis 而不用 map/guava 做缓存?
缓存分为本地缓存和分布式缓存。以java为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
使用 Redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 Redis 或 memcached 服务的高可用,整个程序架构上较为复杂。
对比:
-
Redis 可以用几十 G 内存来做缓存,Map 不行,一般 JVM 也就分几个 G 数据就够大了;
-
Redis 的缓存可以持久化,Map 是内存对象,程序一重启数据就没了;
-
Redis 可以实现分布式的缓存,Map 只能存在创建它的程序里;
-
Redis 可以处理每秒百万级的并发,是专业的缓存服务,Map 只是一个普通的对象;
-
Redis 缓存有过期机制,Map 本身无此功能;Redis 有丰富的 API,Map 就简单太多了;
-
Redis可单独部署,多个项目之间可以空想,本地内存无法共享;
-
Redis有专门的管理工具可以查看缓存数据。
1.6-Redis 的常用场景有哪些?
1 、缓存
缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis 提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在 Redis 用在缓存的场合非常多。
2、排行榜
很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis 提供的有序集合数据类构能实现各种复杂的排行榜应用。
3 、计数器
什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis 提供的 incr 命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。
4、分布式会话
集群模式下,在应用不多的情况下一般使用容器自带的 session 复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以 Redis 等内存数据库为中心的 session 服务,session 不再由容器管理,而是由 session 服务及内存数据库管理。
5 、分布式锁
在很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局 ID、减库存、秒杀等场景,并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,大大影响了数据库的性能。可以利用 Redis 的 setnx 功能来编写分布式的锁,如果设置返回 1 说明获取锁成功,否则获取锁失败,实际应用中要考虑的细节要更多。
6 、 社交网络
点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据,Redis 提供的哈希、集合等数据结构能很方便的的实现这些功能。如在微博中的共同好友,通过 Redis 的 set 能够很方便得出。
7、最新列表
Redis 列表结构,LPUSH 可以在列表头部插入一个内容ID作为关键字,LTRIM 可用来限制列表的数量,这样列表永远为N个ID,无需查询最新的列表,直接根据 ID 去到对应的内容页即可。
8 、消息系统
消息队列是大型网站必用中间件,如 ActiveMQ、RabbitMQ、Kafka 等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis 提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
1.7-Redis 的数据类型有哪些?
有五种常用数据类型:String、Hash、Set、List、SortedSet。以及三种特殊的数据类型:Bitmap、HyperLogLog、Geospatial ,其中 HyperLogLog、Bitmap 的底层都是 String 数据类型,Geospatial 的底层是 Sorted Set 数据类型。
五种常用的数据类型:
-
String:String 是最常用的一种数据类型,普通的 key- value 存储都可以归为此类。其中 Value 既可以是数字也可以是字符串。使用场景:常规 key-value 缓存应用。常规计数: 微博数, 粉丝数。
-
Hash:Hash 是一个键值(key => value)对集合。Redishash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,并且可以像数据库中 update 一个属性一样只修改某一项属性值。
-
Set:Set 是一个无序的天然去重的集合,即 Key-Set。此外还提供了交集、并集等一系列直接操作集合的方法,对于求共同好友、共同关注什么的功能实现特别方便。
-
List:List 是一个有序可重复的集合,其遵循 FIFO 的原则,底层是依赖双向链表实现的,因此支持正向、反向双重查找。通过 List,我们可以很方面的获得类似于最新回复这类的功能实现。
-
SortedSet:类似于 java 中的 TreeSet,是 Set 的可排序版。此外还支持优先级排序,维护了一个 score 的参数来实现。适用于排行榜和带权重的消息队列等场景。
三种特殊的数据类型:
-
Bitmap:位图,Bitmap 想象成一个以位为单位数组,数组中的每个单元只能存 0 或者 1 ,数组的下标在 Bitmap中叫做偏移量。使用 Bitmap 实现统计功能,更省空间。如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1 。
-
Hyperloglog。HyperLogLog 是一种用于统计基数的数据集合类型,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的