redis
redis使用场景?
string、list(队列)、hash(hmset)、set(统计网站访问ip)、zset(top10)
缓存、服务无状态化、锁(redison)
及时性、数据一致性要求不高的数据、访问量大更新频率低的
redis单线程还是多线程?
无论什么版本,工作线程只有一个。
6.X版本出现了IO多线程(redis性能瓶颈在网络请求、所以使用多线程处理io 如:用户空间内、内核空间以及网卡的输入输出,单线程处理数据的计算)
遇到过缓存穿透吗?
当key不存在时,直接查db
解决:
1.value为null的key也放入缓存,设置一个过期时间
2.使用布隆过滤器
遇到过缓存击穿吗?
对一个热点key,非常热,key过期后会有大量请求直接落到db
解决:1.查缓存没有
2.1获取到锁:查db
2.2未获取到锁:sleep一会,回到步骤1
3.更新db到缓存
4.解锁
sleep是blocking状态,不会抢占cpu时间,做好线程池管理就行了
如何避免缓存雪崩?
当key过期时间一样时 所有的key统一过期
解决:过期时间使用随机值
缓存回收策略?
1.后台在轮询的时候,分段删除过期key
2.请求的时候判断已过期的
3.内存空间不足的情况下
4.lru ttl random lfu
如何进行缓存预热?
提前把数据存入redis,你知道哪些是热数据吗?
不知道哪些是热数据情况下,结合缓存击穿策略来回答
数据库与缓存不一致?
1.canal+binlog+rocketmq顺序消费+重试+时间版本号
redis主从不一致问题?
1.redis默认弱一致的,异步同步
2.锁不能用主从,可以用单实例、分片集群 ==> redisson
3.wait 2 0同步复制完返回,不推荐
redis持久化原理
RDB AOF(默认1秒fsync一个pageCache)
开启AOF是可以通过执行日志得到全部内存数据的方式
aof文件体积会变大、重复无效命令,重写所有的kv生成命令到aof
4.X版本,重写不再生成kv命令(性能原因),改为把rdb文件放到aof文件的头部,再追加日志
redis扛不住了,万级流量达到DB上
回答缓存穿透
为什么使用setnX?
原子性的,不存在则完成创建
分布式锁?
lua脚本单线程+ set NX EX命令 不存在则设置 +过期时间
NX EX要在同一个原子内执行避免死锁
redisson 分布式锁,读写锁、countdownLatch 、 semaphore等
如果未设置过期时间、线程未结束可以自动续期、默认30秒
redis存储
1.redis实例哈希槽slots ,有利于数据迁移
2.一致性hash算法,有利于负载防止hash倾斜,且添加移除节点只有影响下一个node节点的查询
https://zhuanlan.zhihu.com/p/179266232
jvm模型?
— 线程独享区—
栈
- 每个线程都有自己的栈,栈中的数据都是以栈帧(一个方法)的格式存在。
- 在这个线程上正在执行的每一个方法都各自对应一个栈帧
- 栈帧是一个内存区块,是一个数据集维系着方法执行过程中的各种数据信息
程序计数器
- 是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
本地方法栈
(Native MethodStacks):
- 与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。
— 共享区—
方法区
存放class文件、常量
堆
存放共享对象、大对象
默认新生代空间的分配:Eden : Fron : To = 8 : 1 : 1
新生带,默认15Minor GC ->老年代 Full GC
* 垃圾回收算法
根搜索算法、标记清除(易产生磁盘碎片)、复制算法、标记整理算法(被引用对象左端移动)
对于新生代内存的回收(Minor GC)主要采用复制算法。而对于老年代的回收(Major GC),大多采用标记-整理算法。
CMS收集器在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收
* 垃圾收集器
CMS才要采用多线程标记清除算法、后会产生大量的内存碎片,当有不足以提供整块连续的空间给新对象/晋升为老年代对象时又会触发FullGC。且在1.9后将其废除
G1从整体上来看是基于‘标记-整理’算法实现,从局部(相关的两块Region)上来看是基于‘复制’算法实现,这两种算法都不会产生内存空间碎片。
可以并行回收各个region空间,不停止应用线程
并行:两个cpu分别执行两个任务
并发:两个cpu执行3+任务
执行引擎
解释执行字节码指令、优化热点字节码为指令、垃圾回收
- 解释器
- JIT即时编译
- GC
类加载器
一、类加载流程
- 加载–>连接(验证–>准备–>解析)–>初始化。
- 1.加载
这个过程主要是通过类的全限定名,例如 java.lang.String 这样带上包路径的类名,获取到字节码文件;然后将这个字节码文件代表的静态存储结构(可简单理解为对象创建的模板)存在方法区,并在堆中生成一个代表此类的 Class 类型的对象,作为访问方法区中“模板”的入口,往后创建对象的时候就按照这个模板创建。举个例子,有时候通过反射创建对象,像当初学 JDBC 时会通过 Class.getName(“com.mysql.jdbc.Driver.class”).newInstance() 创建对象,通过 Class 和相应的全限定类名获取到方法区中的“模板”然后创建对象。
- 2、验证
验证过程主要确保被加载的类的正确性。首先要先验证文件格式是否规范,如果只是通过 .class 后缀来辨别,那随便把后缀名改一下就可以跑程序了,那岂不是很容易出事。 - 3、准备与初始化
这个阶段主要是给类变量(静态变量)分配方法区的内存并初始化类对象,放入方法区
二、类加载器分类
启动类加载器是由C/C++写的,主要负责加载 jre\lib 目录下的类;扩展类加载器主要负责加载 jre\lib\ext 目录下的类;而应用程序类加载器主要负责加载我们自己编写的类;当然还能自己写类加载器,即自定义加载器。程序主要由前面三个类加载器相互配合加载的。
三、类加载时机
创建类的实例,也就是new一个对象
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射(Class.forName(“com.lyj.load”))
初始化一个类的子类(会首先初始化子类的父类)
JVM启动时标明的启动类,即文件名和类名相同的那个类
jvm 对象内存分配
栈上分配-> tlab分配->eden区分配->老年代
栈上分配:
-XX:+DoEscapeAnalysis 是开启逃逸分析 默认就是打开的
对象