❯
返回顶部
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
我的优惠劵
-
¥优惠劵使用时效:无法使用使用时效:
之前
使用时效:永久有效优惠劵ID:×
没有优惠劵可用!
1:MVCC解决什么问题?
解决多事务并发同时读写问题
2:谈谈Innodb MVCC的底层实现?
(1)两个隐藏字段:事务txr_id字段和undo log 回滚指针字段
(2)由undo log组成的undo log 回滚链
(3)readview快照,包含活跃的事务txr_id
每次读的时候,根据readview+undo log链找最近一次commit提交数据
3:最后谈谈MVCC与事务隔离级别的关系?
主要是为rr 和rc服务的
rr : 事务第一次查询的时候,开启快照读,后续查询都是同一个快照读
rc: 事务内每次查询,都开启一个快照读
sql查询执行过程
为什么写入redo log和 bin log要用两阶段提交?如果直接写入redo log,再去修改bin log,可能会出现什么问题?
1.首先我们要知道这个两阶段提交是什么, 在数据库更新一条语句的时候,不会直接在磁盘修改数据,会把数据加载到缓冲池里进行修改,修改后,会把修改记录写到redo log buffer中, 当事务提交的时候,会把redo log buffer中的数据先刷到磁盘,这个为prepare阶段,然后写把binlog日志刷入磁盘,输入磁盘后,会把binlog名字以及写入磁盘位置写到redo log文件里,同时还写一个commit标记到到redo log你, 这次就是commit阶段。
这么做的目的是为了保持这两个日志的数据一致性。
2.如果不采用两阶段提交,先写redo log, 如果此时机器宕机了,binlog没写入进去。此时,重启机器,redo log把数据能恢复出来,但是Binlog没有,后续基于备份日志备份到其他库时,就不一致了。
1.谈谈事务的4大特性?
原子性:一个事务操作,要么都成功要么都失败
持久性:事务一旦提交,就是永久保存的
隔离性:不同事务的操作相互不影响
一致性:事务从一个状态可以正确转化为下一个一致的状态,并且不影响数据库完整性约束。
2.重点谈谈每一种特性在MySQL底层实现是如来实现解决的?
原子性:undo log 实现回滚
持久性:redo log ,故障后恢复
隔离性:隔离级别、mvcc、锁
一致性:原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证
数据库本身一些约束机制提供保障
应用层业务自己保障
3.紧接着,事务在并发下的有哪些问题?
脏写 :两个事务先后写一行数据,事务B还没提交,事务A回滚,把B修改的值弄丢了
脏读 :事务A读到了事务B未提交的数据
不可重复读 :事务A两次读数据不一样
幻读:事务A查询看到之前查询没查到的值
4.如何来解决这些问题?
事务隔离级别、mvcc机制、锁机制
1.你现在线上的MySQL数据库是哪个版本?什么存储引擎?
5.6 innodb
2.该版本的索引的底层采用的是什么数据结构?(这里回答不上来就pass了,重点是想聊下面的B+树)
b+树
3.那么你知道为什么采用B+ 树吗?
我们知道数据库索引的目的是为了提升查询数据效率,查找数据,最简单是顺序查找,但时间查询复杂度最差是O(N),常见优化方法时二分查找,所以基于二叉树存储的二分查找是比较合适,但二叉树带来一个问题,如果数据量比较大的情况,会导致深度很深,会增加IO次数,因此,我们可以采用B树/B+树来优化。同时,B+树相比B树做了一个改造,所有叶子节点存储数据,并通过链表连接起来。这样做范围查询的时候, 就不用像B树一样,可能还需要跨层。
4.B+树和Hash索引比较起来有什么优缺点吗?
hash索引读取单条数据很快,时间复杂度是o(1), 而B+树是o(log2n)
但是B+树,由于底层叶子节点是链表连起来的,做范围查询很快。而hash这种结构因为无序性就无能为力了。
5.最后,谈谈最左前缀匹配?
我们在设计索引的过程中,不可能为每一种业务查询都设计一个索引,所以我们会考虑联合索引。
联合索引在底层存储是按照索引定义顺序来存储,并且是排好序的。
基于这个结构,我们既可以基于联合索引做左N个字段,也可以根据字符串最左N个字符串来查询,
这样都是可以用的上索引的。
1.JVM 常用垃圾收集器有哪些?
年轻代: Serial串行收集 ParNew 并行收集 Parallel Scavenge并行收集
老年代: Serial old串行 CMS并发收集器 Parallel Old收集
g1 收集器
2.分别谈谈各自收集器的特点?
Serial :新生代,单线程收集, 复制算法
Serial old : 老年代, 单线程收集,标记-整理
ParNew :新生代,多线程收集,复制算法
CMS并发收集器 :老年代,可用和用户线程并行收集,标记-清除
Parallel Scavenge :新生代,多线程,追求吞吐量
Parallel Old : 老年代 ,多线程,标记-整理
g1: 新生代和老年代都能用,把整个内存划分为多个大小相等的独立区域(region),适用于大内存
3.最后重点谈谈CMS和G1收集器的工作机制?以及优缺点与应用场景?
cms:
执行一次垃圾回收分为4个阶段:
(1)初始标记, stw, 标记所有GC roots 直接引用的对象, 速度很快
(2)并发标记 对老年代所有对象进行gc roots追踪,最耗时
(3)重新标记 stw, 对第二阶段中被系统程序运行变动过的少数对象进行标记,速度很快
(4)并发清理 清理之前标记的垃圾对象,最耗时
其中初始标记和重新标记,耗时很短,虽然会导致stw,但影响不大
并发标记和并发清理,耗时最长,但是可以和系统的工程线程并发运行,对系统影响不大
CMS ,并发标记和并发清理阶段,会比较消耗CPU资源,并发并发清理阶段会产生“浮动垃圾”,该算法是标记-清除算法,所有会导致内存碎片。
g1:
(1)初始标记 ,stw, 标记GC ROOTS 直接能引用的对象,速度快
(2)并发标记, 追踪所有存活对象,程序可以运行,耗时
(3)最终标记,stw
(4)混合回收,从新生代、老年代、大对象挑选一些region,保证用指定的时间,回收尽可能多的垃圾
基于复制算法,不会产生内存碎片,可预期的停顿,适合大堆、高并发场景
谈谈内存溢出与内存泄漏的关系?
内存溢出: 内存使用满了,已经无法再申请内存资源
内存泄漏: 内存使用后,未释放内存
内存泄漏会可能导致内存溢出
谈谈引起内存溢出的原因有哪些?
1.堆内存空间不足(大量创建对象/死循环)
2.栈内存空间不足 (递归没有终止条件、大量线程创建)
3.方法去空间不足 (反射或者动态代理创建对象导致大量类加载)
如果是你如何来解决?以及后续如何来避免?
1.如果确实是堆空间不够,增大jvm 内存
如果是内存有溢出,解决对应的bug
如果存在大峰值情况下,导致大量对象创建,做限流。
2. 如果确实stack空间过小, 调整-xss
避免递归没有终止条件,避免大量线程创建
3. 如果确实metaSpace过小,调整大小
对于第三方框架中导致大量类加载的地方,做下评估是否可能导致产生内存溢出。
内存持续上升有两种可能:
1.大峰值流量下,不断创建对象,没有做限流。
2.内存泄漏
如何排查:
通过 HeapDumpOnOutOfMemoryError 和 HeapDumpPath 这两个参数开启堆内存异常日志
1.首先通过linux的top命令查看下内存使用率情况,找出占用内存最高的进程
2. 通过jmap -heap pid 查看堆内存使用情况,看老年代内存使用率
3. 通过jmap-histo 查看堆内存中存活的对象
4.通过MAT 打开 dump的内存日志文件,进行分析,看具体是哪里的代码问题导致
1.如何判断一个对象是否存活?
可达性分析算法,看对象是否有被gc root引用
2.JVM的常见垃圾回收算法有哪些?分别谈谈优缺点?
标记-清除: 优点:简单,容易实现,缺点:效率低
复制: 优点:不会产生内存碎片, 缺点: 耗费空间
标记-整理: 优点:不产生碎片 ,容易实现 缺点: 算法复杂
分代算法: 分代收集,效率高 缺点是算法复杂
3.为什么要使用分代回收机制?
由于JVM堆内存不同空间下对象生命周期不一样,需要不同算法来处理
4.最后再谈谈强引用、软引用、弱引用、虚引用?
强引用就是普通的变量引用
软引用 SoftReference 垃圾回收时,内存不够了就回收
弱引用 WeakReference 垃圾回收立马回收
虚引用 用的比较少,一般用于跟踪垃圾回收活动
count1=1,count2=0
静态变量顺序初始化
1. singleTon 初始化,调用构造函数 两个值都设置为1.
2.count2 重新初始化为0