1. 安装Redis6.0.8
2. redis传统五大数据类型的落地应用
3. 你知道分布式锁吗?有哪些实现方案?谈谈redis分布式锁的理解,删key的时候有什么问题
4. redis缓存过期淘汰策略
5. redis的LRU算法简介
0 redis 简介1 Redis安装,推荐使用6.0.8以上
官网说6.0.8之前有BUG
安装成功后, 查看redis版本的方法
方法一:redis- server -v
方法二:redis info
2 Redis的五大数据类型
数据类型 | 使用场景 |
String | 比如:想知道什么时候封锁一个IP地址, Incrby命令 |
Hash | 存储用户信息【id,name,age】 Hset(key,filed,value) Hset(userKey,id,101) Hset(userKey,name,admin) Hset(userKey,age,25) ------修改案例如下------ Hget(userKey,id) Hset(userKey,id,102) 为什么不使用String类型来存储 Set(userKey,用信息的字符串) Get(userKey) 不建议使用String类型 |
List | 实现最新消息的排行,还可以利用List的push命令,将任务存在list集合中,同时使用另一个命令,将任务从集合中取出【pop】 Redis-list数据类型来模拟消息队列【电商秒杀可采用此方式完成秒杀活动】 |
Set | 特殊之处:可以自动排重,微博中将每个人的好友存在集合set中,可求两个人的共同好友,取交集即可 |
Zset | 以某个条件为权重进行排序。比如京东商品详情,有个综合排名,或者按照价格排名等 |
为什么不使用string记录数据信息
因为在修改数据的时候,仅调整某个字段,存取string方式会增加序列号及反序列化的IO次数,降低了性能
以前的SSM架构现在趋向于用SSR,Mysql被Redis代替。
2.1 除了5大数据类型的其他类型有哪些?
bitmap
HyperLogLog
GEO
Stream
3 五大数据结构都是如何使用的
官网:redis中文官方网站
官方命令查询:Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)
Redis大数据类型的落地应用
备注:
命令不区分大小写,而key是区分大小写的
help@类型名词 可查询使用命令。比如help@string, help@list
3.1 8大类型:
- String: 字符串类型
- Hash: 散列类型
- List: 列表类型
- Set: 集合类型
- Sorted Set: 有序集合类型,简称zset
- Bitmap: 位图
- HyperLogLog: 统计
- GEO: 地理
3.2 String
3.4 hash
redis中的hash如果对应java中的数据结构是哪个?
应用场景:
比如购物车
3.5 list
3.6 set
应用场景:
微信小程序抽奖
应用场景:
例如 大数据推送
3.7 zset
应用场景:
场景2 抖音热搜
4 Redis分布式锁
有哪些实现方案?删key会有什么影响
首先要明白锁JVM层面的加锁和分布式锁不是一回事
分布式微服务架构,拆分后 各个服务之间为了避免冲突和数据故障而加入的一种锁,分布式锁。
4.1 面试题:
一般不会部署单机版Redis,一般是一主二从,无人值守再加个哨兵,一般会用Redis cluster集群,最经典的就是三主三从。在这种集群下会有什么问题?
Redlock:Redis分布式锁的简写
4.2 DEMO
4.2.1 建立Module
boot_redis01
boot_redis02
4.2.2 pom
4.2.3 yml
4.2.4 主启动
4.2.5 业务类
4.2.5.1 config
4.2.5.2 Controller
4.2.5.3 运行测试
2号服务器代码同上述的一样,端口号2222
以上DEMO代码在单机版没问题,在高并发的情况下,会出现什么问题?
5. 问题-1 单机版没加锁:
5.1 问题
没有加锁,并发下数字不对,出现超卖现象。
5.2 思考
加synchronized,ReentrantLock,是都可以吗?具体分析?
5.3 解决
5.3.1 synchronized 会造成线程挤压拥堵,只有锁释放了,下一个线程才可以执行
5.3.2 时间太久,放弃等待;或规定时间拿不到锁就放弃
6 问题-2 nginx分布式微服务架构
6.1 问题
6.1.1 分布式部署后,单机锁还是出现超卖现象,需要分布式锁
6.1.2 nginx配置负载均衡
nginx的配置查看 vim nginx.conf
6.1.3 启动两个微服务
6.1.4 上面手点,下面高并发模拟
使用ApacheJMeter.jar测试高并发
6.2 解决
6.2.1 上Redis分布式锁setnx
6.2.2 官网
https://redis.io/commands/set
官网加锁命令:
官网显示,解锁需要lua脚本:
思考问题:不适用LUA脚本怎么解决问题
6.2.3 修改DEMO
7 问题-3 出现异常的话,可能无法释放锁,必须要在代码层面finally释放
7.2 解决方案
加锁解锁,lock/unlock必须同时出现并保证调用
8 问题-4 机器宕机
8.1 问题
8.2 解决
增加过期时间限定
9 问题-5 未保证原子性
9.1 问题
9.2 解决
10 问题-6 删除了其他线程的锁
10.1 问题现象
即A线程在执行过程中,由于一些原因超时完成(比如中间调用了其他服务,其他服务超时),超时锁被删除,线程B执行,在B执行的过程中线程A执行完成,A删除锁,此时删除的是线程B的锁,也就是A自己的锁因为超时被删除,现在又删了B的锁
10.2 解决
只能删除自己的锁,不能删除别人的。
11 问题-7 删除锁非原子
11.1 解决方案1:用Redis事务
11.1.1 案例一(顺利情况):
11.1.2 案例(事务):
A执行以下操作
执行完数据录入后,B执行了下面的代码
B执行后,A执行EXEC
此时k1 k2是多少?
结论:没有被B覆盖掉,保证了批处理成功
11.1.3 案例二(乐观锁):
A线程执行:
此时B线程执行,插队将值改掉:
B执行完A继续执行:发现数据修改失败,数据以及变更
故,A只能在BBB的基础上调整值。
乐观锁:乐观的感觉没有人在我之前操作,如果有人改了再重新操作,直到没人改位置。
DEMO-代码调整:
11.2 解决方案2:适用lua脚本可以解决(日常工作中一般都这样做的)
Redis调用lua脚本通过eval命令保证代码执行的原子性
12 问题-8 确保RedisLock过期时间大于业务时间的问题,Redis分布式锁如何续期
缓存续命时间机制,有点类似futrueTask,如果超时了就续一些时间
redis集群可能会出现CAP的一些小故障,导致加锁失败
CAP:
Redis->AP:
分区容器,高可用(因为保证高可用,牺牲了数据一致性)
redis异步复制造成的锁丢失,如:主节点没来的及把刚刚set进来这条数据给从节点,就挂了
zookeeper->CP 选举算法:
主节点获取key后会先返回给slave节点,数据传输成功后再通知加锁完成。redis是主机有了就通知加锁完成。所以zookeeper主节点挂了也不怕,数据一致性,高可用差
一般还是用redis的多,数据异常的再修复。
13 redlock+redisson方案解决
13.1 RedisConfig
redissonLock.unlock();可能会出现问题
即:当前线程和解锁线程不是一个,不合法的状态异常。
优化后代码:
14 小总结
- synchronized单机版OK,多线程,高并发用锁,但是分布式锁没用
- 取消单机锁,上redis分布式锁setnx
- 加锁就要释放锁,finnaly释放锁保证异常也可以释放
- 宕机无法执行finally,可能无法解锁,故增加lockKey的过期时间设定,保证解锁
- 为redis分布式锁key加过期时间,setnx+过期时间要在一行,保证原子性
- 只能删除自己的锁,避免删除其他线程锁
- redis集群环境下,适用RedLock+Redisson实现即可
15 redis缓存过期淘汰策略
15.1 内存满了怎么办?
15.1.1 查看redis最大占用内存:
查看配置文件 redis.conf
15.1.2 没配置的话,默认内存多少可以用?
15.1.3 生产上如何配置?
底层借鉴了HashMap算法,负载因子0.75
一般存储的数据是热点高频查询数据,不能什么都存。
15.1.4 如何修改redis
1).修改配置文件
2)运行过程中,动态手工修改
重启REDIS并链接
查看配置数据
配置数据
15.1.5 如何查看Redis内存适用情况?
15.1.5 Redis内存用满了这么办?
会报错OOM
提高maxmemory
使用内存淘汰策略
15.2 缓存淘汰策略?
15.2.1 往redis里写的数据是怎么没了的
问题:如果一个键过期了,到期后是否马上从内存种被删除? 答:不是
问题:过期后什么时候删除?怎么操作?
redus过期键的删除策略(3种):
- 定时删除
- 惰性删除
- 定期抽样删除
定时删除命令:set key value ex time
例:set k1 v1 ex 10
惰性删除:
定期抽样key,判断是否过期:
15.2.2 有哪些缓存淘汰策略
总结:
15.2.3 工作中用哪一种?
一般用 allkeys- lru
所有key 使用lru算法,使用率最少的删除
15.2.4 如何配置修改
方法一:
方法二:
执行命令info也可查看配置结果
16 LRU算法
16.1 简介
16.2 算法来源
https://leetcode-cn.com/problems/lru-cache/
16.3 设计思想
当使用key2时,移动到队首,之前的指针指向调整。
16.4 DEMO实现
16.3 DEMO
16.3.1 利用LinkedHashMap实现
public class LRUCacheDemo<K, V> extends LinkedHashMap<K, V>{
private int capacity;//缓存坑位
public LRUCacheDemo(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return super.size() > capacity;
}
public static void main(String[] args) {
LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
lruCacheDemo.put(1,"a");
lruCacheDemo.put(2,"b");
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(4,"d");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(5,"c");
System.out.println(lruCacheDemo.keySet());
}
}
输出
[1, 2, 3]
[2, 3, 4]
[2, 4, 3]
[2, 4, 3]
[2, 4, 3]
[4, 3, 5]
如果将accessOrder的参数调整为false
accessOrder:the ordering mode - <tt>true</tt> for access-order, <tt>false</tt> for insertion-order
即:super(capacity, 0.75F, false);
输出
[1, 2, 3]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[3, 4, 5]
可以看出,false的情况忽略了中间的排序步骤
16.3.2 DEMO2 自己实现LRU
public class LRUCacheDemo2{
//map 负责查找,构建一个虚拟的双向链表,它里面安装的就是一个个Node节点,作为数据载体
//1. 构造一个Node节点,作为数据载体
class Node<K, V> {
K key;
V value;
Node<K, V> prev;
Node<K, V> next;
public Node() {
this.prev = this.next = null;
}
public Node(K key, V value) {
this.key = key;
this.value = value;
this.prev = this.next = null;
}
}
//2. 构建一个虚拟的双向列表,里面安放的时我们的Node
class DoubleLinkedList<K, V> {
Node<K,V> head;
Node<K,V> tail;
//2.1 构造方法
public DoubleLinkedList() {
head = new Node<>();
tail = new Node<>();
head.next = tail;
tail.prev = head;
}
//2.2 添加到头
public void addHead(Node<K,V> node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
//2.3 删除节点
public void removeNode(Node<K,V> node) {
node.next.prev = node.prev;
node.prev.next = node.next;
node.prev = null;
node.next = null;
}
//2.4 获得最后一个节点
public Node getLast() {
return tail.prev;
}
}
private int cacheSize;
Map<Integer, Node<Integer, Object>> map;
DoubleLinkedList<Integer, Object> doubleLinkedList;
public LRUCacheDemo2(int cacheSize) {
this.cacheSize = cacheSize;
map = new HashMap<>();
doubleLinkedList = new DoubleLinkedList<>();
}
public Object get(int key) {
if (!map.containsKey(key)) {
return -1;
}
Node<Integer, Object> node = map.get(key);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
return node.value;
}
public void put(int key, Object value) {
if (map.containsKey(key)) {
Node<Integer, Object> node = map.get(key);
node.value = value;
map.put(key, node);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
} else {
//满了
if (map.size() == cacheSize) {
Node<Integer, Object> lastNode = doubleLinkedList.getLast();
map.remove(lastNode.key);
doubleLinkedList.removeNode(lastNode);
}
//新增
Node<Integer, Object> newNode = new Node<Integer, Object>(key, value);
map.put(key, newNode);
doubleLinkedList.addHead(newNode);
}
}
public static void main(String[] args) {
LRUCacheDemo2 lruCacheDemo = new LRUCacheDemo2(3);
lruCacheDemo.put(1,"a");
lruCacheDemo.put(2,"b");
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(4,"d");
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(5,"e");
System.out.println(lruCacheDemo.map.keySet());
}
}
输出
[1, 2, 3]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[3, 4, 5]
17 其他知识
2种不同形式的持久化方式:
RDB(Redis DataBase)
AOF(Append Of File)
RDB(Redis DataBase)
指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照, 恢复时是将快照文件直接读到内存里
备份如何执行的?
RDB优缺点
RDB保存的文件
RDB保存策略
RDB备份、恢复
AOF(Append Of File) (增量操作)
优缺点
AOF同步频率设置
Rewrite
Redis如何实现重写
何时重写
参考文献
以上内容均来自下方视频,博客内容仅作为个人学习笔记使用