java后端知识点梳理——Redis
redis都支持哪些数据类型?应用场景有哪些?
redis支持五种数据类型作为其Value,redis的Key都是字符串类型的。
- string:redis 中字符串 value 最大可为512M。可以用来做一些计数功能的缓存(也是实际工作中最常见的)。
- list:简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),其底层实现是一个链表。可以实现一个简单消息队列功能,做基于redis的分页功能等。
- set:是一个字符串类型的无序集合。可以用来进行全局去重等。
- sorted set:是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持顺序。可以用来做排行榜应用或者进行范围查找等。
- hash:键值对集合,是一个字符串类型的 Key和 Value 的映射表,也就是说其存储的Value是一个键值对(KeyValue)。可以用来存放一些具有特定结构的信息。
其实redis还支持三种特殊的数据类型,分别为BitMap、Geo和HyperLogLog
一般情况下,可以认为redis的支持的数据类型有上述五种,其底层数据结构包括:简单动态字符串,链表,字典,跳表,整数集合以及压缩列表。
redis是单线程的吗?为什么执行速度这么快?
redis是单线程的,redis的单线程是指网络请求模块使用了一个线程,所以不需考虑并发安全性。但是对于需要依赖多个操作的复合操作来说,还是需要锁的,而且有可能是分布式锁。
那么单线程的redis为什么执行速度如此之快?
- 基于内存实现,完全内存计算
- 单线程操作,避免了线程上下文切换操作
- 多路I/O复用的线程模型,实现了一个线程监控多个IO流,及时响应请求
- redis对外部的依赖比较少,属于轻量级内存数据库
redis的线程模型多路I/O复用机制是一个比较重要并且常见的考察点。目前支持I/O多路复用的系统调用有select,pselect,poll,epoll等函数。I/O多路复用就是通过一种机制一个进程可以监视多个描述符,一旦某个描述符读就绪或者写就绪,其能够通知应用程序进行相应的读写操作。
多路I/O复用机制与多进程和多线程技术相比系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
关于常见函数的特点如下所示:
- select函数:
- 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。
- 有最大监听连接数1024个的限制
- 如果任何一个sock(I/O stream)出现了数据,select没有返回具体是哪个返回了数据,需要采用轮询的方式去遍历获取
- 线程不安全(当你在一个线程中已经监听该socket,另一个线程想要将该socket关闭,则结果会不可预知)
- “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
- poll函数:
- 去掉了1024的限制(使用链表搞定)
- 不再修改传入的参数数组
- 依然是线程不安全的
- epoll函数:
- epoll 不仅返回socket组里面数据,还可以确定 具体哪个socket有数据
- 线程安全
缓存
缓存雪崩
对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
这就是缓存雪崩。
缓存雪崩的事前事中事后的解决方案如下。
事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。
限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。
好处:
- 数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
- 只要数据库不死,就是说,对用户来说,部分的请求都是可以被处理的。
- 只要有部分的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。
缓存穿透
对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是找不到的
比如发出的那 4000 个请求,缓存中查不到,每次你去数据库里查,也查不到。
举个栗子。数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
解决方式很简单,每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
缓存击穿
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。
数据库和缓存的双写一致性问题:
在高并发请求下很容易导致数据不一致的问题,如果你的业务需要保证数据的强一致性,那么建议不要使用缓存。在数据库中和缓存数据的删除或者写入过程中,如果有失败的情况,会导致数据的不一致。
解决办法:
双删延时的解决办法。可以先删除缓存数据,然后再更新数据库数据,最后再隔固定的时间再次删除缓存。
更新数据库产生的binlog订阅(使用canal)。将有变化的key记录下来,并且尝试去不断的去删除缓存(如果上次删除缓存失败)
redis的持久化方式有哪些?
RDB(快照方式 snapshotting)(全量持久化):
将当前内存中的数据集快照写入磁盘,实现数据的持久化,恢复时可以将快照重新载入内存。
触发方式:
- 自动触发:在配置文件中,可以配置执行了多少次save就自动触发自动持久化。
- 手动触发:通过bgsave命令,在后台异步进行生成快照的操作,同时还可以响应客户端的请求。通过redis进程fork操作创建子进程,生成的快照由子进程负责,客户端请求只会在fork阶段被阻塞。
快照恢复:
将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务,redis会自动加载快照文件数据到内存。但是,redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
优缺点分析:
- RDB持久化方式存在数据的丢失,因为其没有办法实现实时持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高,会影响系统性能。自动触发也存在丢失部分数据的情况。
- 在恢复大数据集时候,RDB方式相对于AOF要快。
AOF(append-only-file)(增量持久化):
在 redis配置文件的 APPEND ONLY MODE 中,可以设置AOF持久化。通过记录redis服务器所执行的写命令来记录数据库状态。恢复时可以将AOF文件载入内存,并且可以通过redis-check-aof --fix 进行修复AOF文件。
AOF日志重写:
- AOF文件会随着服务器运行的时间越来越大,可以通过AOF重写来控制AOF文件的大小。
- AOF重写会首先读取数据库中现有的键值对状态,然后根据类型使用一条命令来替代前面对键值对操作的多条命令。
- 使用命令 bgrewriteaof 来实现AOF重写
AOF重写缓存区:
redis 是单线程工作,当AOF文件较大时重写时间会比较长,在重写 AOF 期间,redis将长时间无法处理客户端请求。为了解决这个问题,可以将 AOF 重写程序放到子进程中执行,好处如下:
- 子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其它客户端请求。
- 子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
子进程中AOF重写导致的问题:
- 子进程在进行 AOF 重写期间,服务器进程依然可以处理其它客户端请求,这就会导致数据库状态已经发生了改变,使得当前数据库数据状态和重写后的 AOF 文件中的数据不一致。
- 也就是出现了AOF文件和数据库中数据不一致的问题。
数据状态不一致解决办法:
- redis 服务器设置了一个 AOF 重写缓冲区。这个缓冲区在创建子进程后开始使用,当redis服务器执行一个客户端的写请求命令,之后将这个写命令也发送到 AOF 重写缓冲区。
- 当子进程完成 AOF 日志重写之后,给父进程发送信号,父进程接收此信号后,将 AOF 重写缓冲区的内容写到新的 AOF 文件中,保持数据的一致性。
两者的优缺点分析:
- AOF文件可以做到秒级持久化,使用追加写的方式来写入,可读性强并且可以使用命令进行文件修复。
- 相比于RDB文件,同样数据下AOF文件体积要大。在redis负载较高时,秒级更新AOF文件会影响性能
持久化策略选择:
- AOF更安全,可将数据及时同步到文件中,但需要较多的磁盘IO,AOF文件尺寸较大,文件内容恢复相对较慢也更加完整。
- RDB持久化,安全性较差,它是正常时期数据备份及 master-slave数据同步的最佳手段,文件尺寸较小并且恢复速度较快。
redis数据的过期回收策略与内存淘汰机制
redis中的数据过期回收策略使用了定期删除和惰性删除相结合的方式。
定期删除:
redis会每隔一定的时间去抽查一定量的数据判断其是否过期,过期则进行删除。
惰性删除:
在获取一个key的时候,redis会检查这个key是否已经过期,若过期,则会进行删除操作。
内存淘汰机制:
在配置文件中,我们可以对内存淘汰机制进行配置。当内存使用达到最大值时,redis可以使用的清除策略如下:
- volatile-lru:利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )
- allkeys-lru:利用LRU算法移除任何key
- volatile-random:移除设置过过期时间的随机key
- allkeys-random:移除随机key
- volatile-ttl:移除即将过期的key(minor TTL)
- noeviction :不移除任何key,只是返回一个写错误 ,默认选项
主从复制简单介绍
当项目比较大的时候,我们可以使用主从架构Master/Slave机制,Master 以写为主,Slave 以读为主,Master 主节点更新后根据配置,自动同步到从机Slave 节点。
主从复制的原理包括旧版同步和命令传播,主从复制的代价就是系统复制较重的时候会导致主从延迟,并且根据CAP理论,无法同时保证服务可用性和数据一致性。
什么是CAP理论?
CAP理论是指 当网络分区发生时,一致性和可用性不可能同时保证。
- C:Consistent 一致性
- A:Availability 可用性
- P:Partition tolerance 分区容忍度
- 网络分区:分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,网络断开也就意味着发生了网络分区。
- 最终一致性:Redis可以保证最终一致性,从节点会努力追赶主节点,最终从节点的状态会和主节点的状态将保持一致。
redis对事务支持
redis对事务的支持主要可以概括如下:
- 隔离性:redis 是单进程的程序,保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。所以redis的事务支持隔离性。
- redis会将一个事务中的所有命令序列化,然后按顺序执行。redis不可能在一个事务的执行过程中插入执行另一个客户端发出的请求。可以保证Redis将这些命令作为一个单独的隔离操作执行。
redis操作事务的相关命令如下所示:
- MULTI:标记一个事务块的开始。
- EXEC:执行所有事务块内的命令。
- DISCARD:取消事务,放弃执行事务块内的所有命令。
- UNWATCH:取消 WATCH 命令对所有 key 的监视。
- WATCH key [key ...]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
需要注意的是redis的事务不支持回滚操作,redis以 MULTI 开始一个事务,然后将多个命令入队到事务中,最后由 EXEC 命令触发事务, 一并执行事务中的所有命令。只有当被调用的redis命令有语法错误时,这条命令才会执行失败,或者对某个键执行不符合其数据类型的操作,但是应该在将命令入队列的时候就应该并且能够发现这些问题,所以redis的事务不支持进行回滚操作。
后续更新
- 哨兵模式
- 分布式锁
- 关于分布式锁,为了使得加锁操作具有原子性,不可以使用多条命令来完成,我们可以使用带多个参数的set命令来完成,如下所示:jedis.set(String key, String value, String nxxx, String expx, int time)
- 第一个为key,我们使用key来当锁,因为key是唯一的。
- 第二个为value,我们传的是requestId,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。
- 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
- 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
- 第五个为time,与第四个参数相呼应,代表key的过期时间。
java后端知识点梳理——Redis的更多相关文章
- java后端知识点梳理——Spring
开篇:感谢我是祖国的花朵,java3y,三太子敖丙等优秀博主!他们的文章为我学习java提供了莫大的帮助,膜拜大神! Spring的优点有哪些呢? Spring的依赖注入将对象之间的依赖关系交给了框架 ...
- java后端知识点梳理——多线程与高并发
进程与线程 进程是一个"执行中的程序",是系统进行资源分配和调度的一个独立单位 线程是进程的一个实体,一个进程中一般拥有多个线程. 线程和进程的区别 进程是操作系统分配资源的最小单 ...
- java后端知识点梳理——web安全
跨域 当浏览器执行脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域. 这里的同源指访问的协议.域名.端口都相同. 同源策略是由 Netscape 提出的著名安全策略,是浏览器最核心.基 ...
- java后端知识点梳理——JVM
可以先看看我的深入理解java虚拟机笔记 深入理解java虚拟机笔记Chapter2 深入理解java虚拟机笔记Chapter3-垃圾收集器 深入理解java虚拟机笔记Chapter3-内存分配策略 ...
- java后端知识点梳理——java集合
集合概览 Java中的集合,从上层接口上看分为了两类,Map和Collection.Map是和Collection并列的集合上层接口,没有继承关系. Java中的常见集合可以概括如下. Map接口和C ...
- java后端知识点梳理——java基础
面向对象 java三大特性 封装: 将事务封装成一个类,达到解耦,隐藏细节的效果.通过get/set等方法,封装了内部逻辑,并保留了特定的接口与外界联系. 继承: 从一个已知的类中派生出一个新的类,新 ...
- java后端知识点梳理——MySQL
MySQL的索引 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息,就像一本书的目录一样,可以加快查询速度.InnoDB 存储引擎的索引模型底层实现数据结构为 ...
- Java编程知识点梳理
1. elementAt() temp.elementAt(0) 返回temp这个vector里面存放的第一个元素--->也是一个vector类型. 2. 字符串空格分割 String [] ...
- java基础知识点梳理
前言 在别人追问我以下几个问题,自己在问题回答上不够全面和准确,对此自己把专门针对这几个问题进行总结! java相关问题 1.Java中构造方法跟普通方法的区别? 构造方法与普通方法的调用时机不同. ...
随机推荐
- Social engineering tookit 钓鱼网站
目录 Set 钓鱼攻击 网站克隆 Set Set(Social engineering tookit)是一款社会工程学工具,该工具用的最多的就是用来制作钓鱼网站. Kali中自带了该工具. 钓鱼攻击 ...
- POJ1376简单广搜
题意: 给你一个n*m的矩阵,然后给你机器人的起点和终点,还有起点的方向,然后每次机器人有两种操作,左右旋转90度,或者是朝着原来的方向走1,2或者3步,机器人再走的过程中不能碰到格子,也 ...
- 影子卫士汉化语言包 res.ini
[translate];authorinfo=Simplified Chinese Translation 简体翻译 by: Clarence [common]0=Shadow Defender 10 ...
- android 资料
https://xfans.gitbooks.io/android-book/content/issue-39/Android%20dex%E5%88%86%E5%8C%85%E5%AF%BC%E8% ...
- angularjs中的常遇问题整理
1.页面中默认图片的显示 {{img || "/upload/img.png"}} 2.接口中的数据没有及时读取到,$apply $.post( url, {}, function ...
- 你管这破玩意叫CPU?
每次回家开灯时你有没有想过,用你按的开关实际上能打造出 复杂的 CPU来,只不过需要的数量可能比较多,也就几十亿个吧. 伟大的发明 过去200年人类最重要的发明是什么?蒸汽机?电灯?火箭?这些可能都不 ...
- java+selenium使用JS、键盘滑动滚动条
本篇文章介绍如何使用JS和键盘对象对页面进行滑动滚动条-------------主要针对java做自动化测试的同学 一:使用键盘对象操作滚动条 //导包 import org.openqa.selen ...
- Flutter 2.2 现已发布!
在本次 Google I/O 2021 大会 上,我们正式发布了 Flutter 2.2.Flutter 2.2 是我们最新版的开源工具包,可让开发者立足单个平台构建适合任何设备的精美应用.Flutt ...
- [刷题] 283 Move Zeros
要求 将所有的0,移动到vector的后面比如; [1,3,0,12,5] -> [1,3,12,5,0] 实现 第一版程序,时间.空间复杂度都是O(n) 1 #include<iostr ...
- 对Linux系统内核版本稳定性测试介绍
对Linux系统内核版本稳定性测试介绍 在对 Linux 内核版本稳定性的测试中,需要明确地声明并证明为什么版本是稳定的或者是不稳定的. 然而还没有被证明和证实当前现有的系统范围内的压力测试可以测试 ...