Java面试高频知识点总结 Redis
Redis是一种内存型数据库。传统的数据库储存在硬盘中,而Redis数据库存在内存中,所以读写速度非常快。因此redis广泛用于缓存方向,除此之外也经常用于实现分布式锁。redis提供了多种数据类型来支持不同的业务场景。
除此之外,redis支持事务、持久化、LUA脚本、LRU驱动事件、多种集群方案。
为什么要用Redis/为什么要用缓存
高性能和高并发
高性能:从内存读取数据比从硬盘读取要快很多。如果数据库中对应的数据改变之后,同步改变缓存中相应的数据即可。
高并发:直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以可以考虑将数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
Redis常见数据结构
- String
- Hash
- List
- Set
- Sorted Set
Redis内存淘汰机制
redis提供6中数据淘汰策略:
-
volatile-lru
:从设置的过期时间数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。 -
volatile-ttl
:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。 -
volatile-random
:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。 -
allkeys-lru
:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。 -
allkeys-random
:从数据集(server.db[i].dict)中任意选择数据淘汰。 -
no-eviction
:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。
4.0版本后增加以下两种:
-
volatile-lfu
:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。 -
allkeys-lfu
:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用key。
总结:两种操作对象:已设置过期时间的数据集(server.db[i].expires)
和数据集(server.db[i].dict)
,四种机制lru
、lfu
、random
、ttl
(仅针对过期时间数据集)加no-eviction
。
Redis持久化机制 (重要)
持久化:将内存中的数据写入到硬盘里面。主要是为了之后重用数据(比如重启及其、机器故障之后恢复数据),或者为了防止系统故障而将数据备份到一个远程位置。
Redis
不同于memcache
很重要的一点在于Redis支持持久化,提供了两种不同的持久化方式快照(snapshotting, RDB)
,另一种方式是只追加文件(append-only file, AOF)
。
快照
Redis通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
AOF(append-only file)持久化
默认没有开启,可以通过下面的参数开启:
appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令。Redis就会将该命令写入硬盘中那个的AOF文件。AOF文件个RDV文件位置相同,可以通过dir参数设置。
appendfsync always # 每次有数据修稿发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec # 每秒钟同步一次,显式地将多个写命令同步到硬盘
appendfsync no # 让操作系统决定何时进行同步
为了兼顾数据和写入性能,用户可以考虑appendfsync everysec
选项,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只丢失一秒之内产生的数据。当硬盘忙于写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
Redis 4.0
开始支持RDB和AOF的混合持久化(默认关闭,可以通过配置项aof-use-rdb-preamble
开启)
开启混合持久化之后,AOF重写的时候就直接把RDB的内容写到AOF文件开头,这样做的好处是可以结合RDB和AOF的优点,快速加载的同时避免丢失过多的数据,当然缺点也是有的,AOF里面RDB部分的压缩格式不再是AOF格式,可读性较差。
Redis事务
Redis通过MULTI
、EXEC
、WATCH
等命令来实现事务(transaction)功能。事务提供了一种将多个请求打包,然后一次性的,按顺序的执行多个命令的机制,并且在事务执行期间,服务不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕。
Redis中,事务总是具有原子性(Atomicity)
、一致性(Consistency)
、隔离性(Isolation)
,并且当Redis运行在某种特定的持久化模式下时,事务也具有持久性(Durability)
。
缓存雪崩和缓存穿透
缓存雪崩
缓存同一时间大面积的失效,所以,后面的请求全都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方法:
- 事前:尽量保证redis集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
- 事中:本地ehcache缓存+hystrix限流&降级,避免MySQL崩掉。
- 事后:利用redis持久化机制保存的数据尽快恢复缓存。
缓存穿透
大量请求的key根本不在缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。
解决方法:
- 缓存无效key:如果缓存和数据库都查不到某个key就写一个到redis中并设置过期时间。并不能从根本上解决问题,尽量要将无效的key的过期时间设置短一点。
- 布隆过滤器:把所有可能请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来请求的值是否存在于布隆过滤器中,存在的话才会走正常流程,不存在的话直接放回请求错误信息。
如何解决Redis并发竞争key问题
并发竞争key的问题指的是多个系统同时对一个key进行操作,但最后执行的顺序与我们期望的不同,这样导致了结果的不同。
分布式锁(zookeeper和redis都可以实现分布式锁)
基于zookeeper临时有序节点可以实现分布式锁。大致思想为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点序号中的最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。
如何保证缓存与数据库双写时的数据一致性
读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况。
最好不要使用这个方案,串行之后系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
参考
来自Java Guide面试突击版,百度可得最新版本,这里有删减和修正。
- C# 6.0 功能预览 (一)
- [译]Asp.net MVC 之 Contorllers(二)
- [译]Asp.net MVC 之 Contorllers(一)
- Oracle 学习笔记
- [数据库基础]——索引详解
- [数据库基础]——快速浏览日期时间转换
- 【死磕Java并发】—- 深入分析CAS
- [SQLServer大对象]——FileTable从文件系统迁移文件
- [机器学习]-[数据预处理]-中心化 缩放 KNN(二)
- [数据清洗]-看上去一样的数字
- [数据清洗]- Pandas 清洗“脏”数据(三)
- [数据清洗]- Pandas 清洗“脏”数据(二)
- [数据清洗]-Pandas 清洗“脏”数据(一)
- [数据清洗]-混乱的邮编数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 基于React+Koa实现一个h5页面可视化编辑器-Dooring
- 使用Spring Boot DevTools优化你的开发体验
- 排序算法——一篇文章搞懂常用的排序算法
- 路径寻优
- Milvus 实战 | 基于 Milvus 的 CORD-19 论文检索引擎
- Laravel 8 新特性和功能优化速览
- Linux 搭建 我的世界(MC) 基岩版服务器
- 虚函数、析构函数、静态函数、多态
- 一日一技:pylint除了检查代码风格,还能做这件事情
- Manjaoro ifconfig问题
- 面对成百上千台服务器产生的日志,试试这款轻量级日志搬运神器!
- 内网渗透 | 内网中的信息收集
- 通过命令下载执行恶意代码的几种姿势
- CS如何配置通过CDN上线
- Height transition