探索Java与Redis的完美结合

发表时间: 2022-06-29 13:07

1.简单介绍一下Redis

Redis是一个使用C语言开发的数据库,不过与传统的数据库不同的是Reids的数据库是存在内存中的,也就是它是内存数据库,所以读写速度非常快,因此Redis被广泛应用于缓存方向。

Redis除了做缓存之外,Redis也经常用来做分布式锁,甚至是消息队列。

Redis提供了多种数据类型来支持不同的业务场景。Redis还支持事务、持久化、Lua脚本、多种集群方案。

优点:

读写性能极高

支持数据持久化

支持事务

数据结构丰富

支持主从复制

丰富的特性

缺点:

数据库容量收到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

2.缓存数据的处理流程

如果用户请求的数据在缓存中就直接返回

缓存中不存在的话就看数据中是否存在

数据库中不存在的话就更新缓存中的数据

数据库中不存在的话就返回空数据。

3.Rsdis常见的数据库

A.字符串类型 String

String数据结构是简单的key-value类型,可以存储任何类型的数据,包括二进制数据,序列化后的数据,JSON化后的数据,甚至是一张图片。最大为512M

常用命令:set、get、strlen、exists、dect、incr、setex

应用场景:计数器:记录播放量,记录访问数(Redis记总数,MySQL记录日志)、登录验证码

B.列表类型 list

是简单的字符串列表,按照插入顺序排序,元素可以重复。可以添加一个元素到列表的头部或者尾部,底层是个链表结构。实现的是双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

常用命令:rpush、lpop、lpush、rpop、lrange、llen

应用场景:消息队列,慢查询。

C. 集合类型 set

是String类型的无序无重复集合

常用命令:sadd、spop、smembers、scard

应用场景:需要存放的数据不能重复,以及需要获取多个数据源交集和并集等场景

D. 哈希类型hash

hash是一个string类型的field和value的映射表,hash特别适合于存储对象

常用命令:hset、hget、hgetall

应用场景:系统中对象的存储

E. 有序集合类型zset(sorted set)

有序集合zset和set集合一样也是string类型元素的集合,且不允许重复的成员。不同的是zset每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序。

常用命令:zadd、zscore、zcard、zrange

应用场景:需要对数据根据某个权重进行排序的场景。

4.Redis单线程模型

redis中的文件事件处理器,文件事件处理器是单线程模式运行的。

文件事件处理器的四个部分:

多个socket(客户端连接)

IO多路复用程序(支持多个客户端连接的关键)

文件事件分派器(将socket关联到相应的事件处理器)

事件处理器(连接 应答处理器、命令请求处理器、命令恢复处理器)

单线程为什么执行那么快

纯内存操作

避免了不必要的上下文切换

避免了多线程之间的竞争和切换而消耗CPU,避免了各种锁操作

采用了非阻塞I/O多路复用技术:这个技术让redis不需要额外创建多余的线程来监听客户端的大量连接,降低了资源消耗。

5. Redis6.0之前,为什么不使用多线程

单线程编程容易并且更容易维护

Redis的性能瓶颈不在CPU,主要在内存和网络;

多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能

6.0后为何引入了多线程?

Redis引入多线程主要是为了提高IO读写性能,这个也算是Redis中的一个性能瓶颈。

Redis6.0多线程的实现机制?

主线程负责接收建立连接请求,获取socket放入全局等待读处理队列

主线程处理完读事件后,通过RR(Round Robin)将这些连接分配给这些IO线程。

主线程阻塞等待IO线程读取socket完毕

主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行

主线程阻塞等待IO线程将数据回写Socket完毕。

6.缓存

6.1缓存淘汰

先进先出(FIFO)

近期较少使用(LFU)

最长时间未被使用哪个(LRU)

6.2 key过期删除策略

惰性删除:当使用的时候,如果过期就删除,如果没过期就使用

定时删除:key过期后设置一个定时器

定期删除:一段时间对key进行随机检查,删除。控制删除的时长和频率。

6.3 缓存穿透

当获取一个数据但是缓存中没有,数据库中也没有,多次查询使数据库宕机

解决方案;

布隆过滤器:将mysql中的key加入布隆过滤器,查询之前,查看数据库是否有这个key

在redis存在不存在的key设置value为null

6.4 缓存击穿

当获取一个数据但是缓存中没有(过期时间到,宕机),数据库中有,多次查询使数据库宕机

解决:redis集群。redis数据库中,添加缓存层或限流

6.5 缓存雪崩

同一时刻,大量缓存失效,直接请求数据库

解决:

设置不同失效时间

双层缓存

定期更新

增加过期标志

6.6 缓存阻塞

数据结构不合理

CPU饱和

持久化阻塞

6.7 热点key缓存倾斜

使用本地缓存

利用分片算法,对key加前缀,把一个热点key分散成多个key

7. 持久化

Redis的一种持久化方式叫快照(snapshotting RDB),另一种方式只追加文件(append-only file AOF).

快照持久化(RDB):定期的全盘缓存

在指定的时间间隔内将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存里。

优势:适合大规模的数据恢复;对数据完整性和一致性要求不高

劣势:在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。

AOF持久化:日志文件; 触发机制,每次记录

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以写文件,Redis启动之初会读取该文件重新构建数据。

避免文件越来越大,新增了重写机制,当AOF文件的大小超过所设定的阈值的时候,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。

优势:同步持久化;每秒同步;不同步

劣势:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb

aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

两者的比较:

AOF文件比RDB更新效率高

优先使用AOF

AOF更安全

RDB比AOF性能好

7.1Redis持久化数据和缓存怎么做扩容?

如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩写。

如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化,否则的话必须使用可以在运行时进行数据再平衡一套系统

8.Redis事务

Redis可以通过MULTI . EXEC , DISCARD, WATCH等命令来实现事务(transcation)功能

Multi开启事务

Exec执行事务代码块

Discard取消事务

Watch监制一个或多个key,如果事务执行前key被改动,事务将打断

8.1 redis事务的实现特征

所有的命令都将被串行化的顺序执行,事务执行期间,Redis不会再为其他客户提供要求

Redis事务中如果有一条命令执行失败,之后的命令仍然会继续执行

在事务开始之前,如果客户端和服务器之间出现通信故障并导致网络断开,之后的所有待执行语句都不会执行。如果exec之后网络中断后,事务中的所有命令都会被服务器执行。

当使用Append-Only模式的时候,Redis通常会调用write全部写入磁盘。当写入断电的时候,会发生数据不一致的信息,使用redis-check-aof对数据进行回滚

9.Redis集群

官方cluster集群3主3从

客户端只需要连接其中的任何一个节点

使用哈希槽(hash slot) 的方式来分配16384个slot,当新节点加入,取各个槽的前部门分给新节点。

9.1 容错性

投票:集群中所有的master节点投票,半数以上的master挂掉集群不可用

节点分配:新增节点时,从每个节点的前半部分分配给新的节点

9.2 主从复制

从机连通会给主机发送sync指令

主机进行存盘操作,发送RDB文件给从机

从机收到后,进行全盘加载

每次主机写操作,都会立刻发送给从机,从机执行相同的命令

9.3 性能优化

主机不进行持久化,从机AOF备份数据

主从位于同一局域网

避免在压力大的主库上增加从库

使用链式结构增加从机

9.3 集群不可用

主机挂掉,当前主机没有从机

半数以上的主机挂掉。

分布式问题

10.什么是分布式锁?为什么用分布式锁?

锁在程序中的作用就是同步工具,保证共享资源在同一时刻只能被一个线程访问,Java中的锁我们很熟悉,像synchronized、lock都是常见的,但是Java的锁只能保证单机的时候有效,分布式集群环境就无能无力了,这个时候我们就需要用到分布式锁。

分布式锁,就是分布式项目开发中用到的锁,可以用来控制分布式系统之间同步访问共享资源

需要满足的特点:

互斥性:在任何时刻,对于同一数据,只有一台应用可以获取到分布式锁

高可用性:在分布式场景下,一小部分服务器宕机不影响正常使用,这种情况就需要将提供分布式锁的服务以集群的方式部署

防止锁超时:如果客户端没有主动释放锁,服务器会在一段时间之后自动释放锁,防止客户端宕机或者网络不可达时产生死锁

独占性:加锁解锁必须由同一台服务器进行,也就是锁的持有者才可以释放锁。

11.常见的分布式锁有哪些解决方案?

三种解决方案:关系型数据库、Redis、ZooKeeper

12.Redis实现分布式锁

分布式锁的三大核心要素:加锁、解锁、锁超时

13.了解RedLock吗?

Redlock是一种算法,Redlock也就是Redis Distributed Lock,可以实现多节点的分布式锁

这种方法的特性:

互斥访问:即永远只有一个client能拿到锁

避免死锁:最终client都可能拿到锁,不会出现死锁的情况,即使锁定资源的服务崩溃或者分区,仍然能释放锁

容错性:只要大部分Redis节点存活(一半以上),就可以正常提供服务。

14.Redis如何做内存优化?

控制key的数量:当时用Redis存储大量数据的时候,通常会存在大量键,过多的键同样会消耗大量内存。

缩减键值对象:降低Redis内存使用最直接的方式就是缩减key和value的长度

编码优化:Redis对外提供了string,list,hash,zet,set等类型,但是Redis内部针对不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结果来实现。编码不同将直接影响数据的内存占用和读写效率

15.如果现在有个读超高并发的系统,用Redis来抗住大部分读请求,你会怎么设计?

如果是读高并发的话,先看并发的数量级是多少,因为Redis单机的读QPS在万级,每秒几万没问题,使用一主多从+哨兵集群的缓存架构来承载每秒10W的读并发,主从复制,读写分离,

使用哨兵集群主要是提高缓存架构的可用性,解决单点故障问题。主库负责写,多个从库负责读,支持水平扩容,根据读请求的QPS来决定加多少个Redis从实例。如果读并发继续增加的话,只需要增加Redis实例就行了。

如果需要缓存1T+的数据,选择Redis cluster模式,每个主节点存一部分数据,假设一个master存32G,那只需要n*32G>=1T,n个这样的master节点就可以支持1T+的海量数据的存储。