问题来历如下:
问题链接:www.nowcoder.com/discuss/493…
答案解析
1.解说脏读/不可重复读/幻读
- 脏读:指一个事务读取到了另一个事务为提交保存的数据,之后此事务进行了回滚操作,然后导致第一个事务读取了一个不存在的脏数据。
- 不可重复读:在同一个事务中,同一个查询在不同的时刻得到了不同的成果。例如事务在 T1 读取到了某一行数据,在 T2 时刻从头读取这一行时候,这一行的数据已经产生修正,所以再次读取时得到了一个和 T1 查询时不同的成果。
- 幻读:同一个查询在不同时刻得到了不同的成果,这便是事务中的幻读问题。例如,一个 SELECT 被履行了两次,但是第2次回来了第一次没有回来的一行,那么这一行便是一个“幻像”行。
不可重复读和幻读的差异
- 不可重复读的重点是修正:在同一事务中,相同的条件,第一次读的数据和第2次读的数据不一样。(由于中心有其他事务提交了修正);
- 幻读的重点在于新增或者删去:在同一事务中,相同的条件,,第一次和第2次读出来的记载数不一样。(由于中心有其他事务提交了刺进/删去)。
2.索引失效的场景有哪些?
常见的索引失效场景有以下这些:
- 未遵循最左匹配准则
- 运用列运算
- 运用函数方法
- 类型转换
- 运用 is not null
- 过错的含糊匹配,运用右 % 开始查询。
具体内容请参阅:www.javacn.site/interview/m…
3.Explain履行计划用过吗?
Explain 是用来分析 SQL 的履行情况的,explain 运用如下,只需求在查询的 SQL 前面添加上 explain 关键字即可,如下图所示: 而以上查询成果的列中,咱们最首要观察 key 这一列,key 这一列表明实际运用的索引,假如为 NULL 则表明未运用索引,反之则运用了索引。
以上一切成果列阐明如下:
- id — 挑选标识符,id 越大优先级越高,越先被履行;
- select_type — 表明查询的类型;
- table — 输出成果集的表;
- partitions — 匹配的分区;
- type — 表明表的查询类型;
- possible_keys — 表明查询时,或许运用的索引;
- key — 表明实际运用的索引;
- key_len — 索引字段的长度;
- ref— 列与索引的比较;
- rows — 大概估算的行数;
- filtered — 按表条件过滤的行百分比;
- Extra — 履行情况的描述和阐明。
4.Type字段有什么信息?
Explain 履行计划中最重要的便是 type 字段,type 包括的信息如下:
- all — 扫描全表数据;
- index — 遍历索引;
- range — 索引范围查找;
- index_subquery — 在子查询中运用 ref;
- unique_subquery — 在子查询中运用 eq_ref;
- ref_or_null — 对 null 进行索引的优化的 ref;
- fulltext — 运用全文索引;
- ref — 运用非仅有索引查找数据;
- eq_ref — 在 join 查询中运用主键或仅有索引关联;
- const — 将一个主键放置到 where 后面作为条件查询, MySQL 优化器就能把这次查询优化转化为一个常量,如何转化以及何时转化,这个取决于优化器,这个比 eq_ref 功率高一点。
5.binlog和redolog差异?
binlog(二进制日志)和 redolog(重做日志)都是 MySQL 中的重要日志,但二者存在以下不同。
- binlog(二进制日志):
- binlog 是 MySQL 的服务器层日志,用于记载对数据库履行的一切修正操作,包括刺进、更新和删去等。它以二进制格局记载,能够被用于数据仿制、康复和毛病康复等操作。
- binlog 记载了逻辑上的操作,即履行的 SQL 句子或句子的逻辑表明。
- binlog 是在事务提交后才会生成,因此它是耐久化的。
- binlog 能够被配置为不同的格局,包括基于句子的仿制(statement-based replication)、基于行的仿制(row-based replication)和混合仿制(mixed replication)。
- redolog(重做日志):
- redolog 是 MySQL 的存储引擎层日志,用于确保数据库的事务耐久性和溃散康复能力。
- redolog 记载了物理层面的修正操作,即对数据页的物理修正。它首要用于确保事务的耐久性,确保在产生溃散时,已经提交的事务对数据库的修正能够被康复。
- redolog 是循环写入的,它的数据写入到磁盘上的文件中。在产生溃散时,经过 redolog 的重做操作,能够将数据库康复到溃散前的一致状态。
- redolog 是在事务履行期间不断写入的,以确保在体系溃散时能够重做一切已提交的事务。
小结:binlog 用于记载逻辑层面的操作,能够用于数据仿制和康复,而 redolog 用于记载物理层面的操作,确保事务的耐久性和溃散康复。它们在功用和运用上有一些不同,但都是 MySQL 中重要的日志机制。
6.Redis根本数据类型
Redis 常用的数据类型有 5 种:String 字符串类型、List 列表类型、Hash 哈希表类型、Set 调集类型、Sorted Set 有序调集类型,如下图所示: 这 5 种常用类型的用处如下:
- String:字符串类型,常见运用场景是:存储 Session 信息、存储缓存信息(如详情页的缓存)、存储整数信息,可运用 incr 完成整数+1,和运用 decr 完成整数 -1;
- List:列表类型,常见运用场景是:完成简略的消息行列、存储某项列表数据;
- Hash:哈希表类型,常见运用场景是:存储 Session 信息、存储产品的购物车,购物车非常适合用哈希字典表明,运用人员仅有编号作为字典的 key,value 值能够存储产品的 id 和数量等信息、存储详情页信息;
- Set:调集类型,是一个无序并仅有的键值调集,它的常见运用场景是:重视功用,比方重视我的人和我重视的人,运用调集存储,能够确保人员不会重复;
- Sorted Set:有序调集类型,相比于 Set 调集类型多了一个排序特点 score(分值),它的常见运用场景是:能够用来存储排名信息、重视列表功用,这样就能够依据重视完成排序展示了。
更多内容请参阅:www.javacn.site/interview/r…
7.有序调集底层完成数据结构?
有序调集是由 ziplist (紧缩列表) 或 skiplist (跳动表) 组成的。
- 紧缩列表 ziplist 本质上便是一个字节数组,是 Redis 为了节约内存而规划的一种线性数据结构,能够包括多个元素,每个元素能够是一个字节数组或一个整数。
- 跳动表 skiplist 是一种有序数据结构,它经过在每个节点中保持多个指向其他节点的指针,然后达到快速拜访节点的意图。跳动表支持均匀 O(logN)、最坏 O(N) 复杂度的节点查找,还能够经过次序性操作来批量处理节点。
当数据比较少时,有序调集是紧缩列表ziplist存储的(反之则为跳动表 skiplist 存储),运用紧缩列表存储必满意以下两个条件:
- 有序调集保存的元素个数要小于 128 个;
- 有序调集保存的一切元素成员的长度都必须小于 64 字节。
假如不能满意以上两个条件中的恣意一个,有序调集将会运用跳动表 skiplist 结构进行存储。
8.跳表刺进数据的进程?
在开始讲跳动表的添加流程之前,必须先搞懂一个概念:节点的随机层数。 所谓的随机层数指的是每次添加节点之前,会先生成当前节点的随机层数,依据生成的随机层数来决定将当前节点存在几层链表中。
为什么要这样规划呢?
这样规划的意图是为了确保 Redis 的履行功率。
为什么要生成随机层数,而不是拟定一个固定的规矩,比方上层节点是基层跨过两个节点的链表组成,如下图所示:
假如拟定了规矩,那么就需求在添加或删去时,为了满意其规矩,做额外的处理,比方添加了一个新节点,如下图所示: 这样就不满意拟定的上层节点跨过基层两个节点的规矩了,就需求额外的调整上层中的一切节点,这样程序的功率就降低了,所以运用随机层数,不强制拟定规矩,这样就不需求进行额外的操作,然后也就不会占用服务履行的时刻了。
添加流程
Redis 中跳动表的添加流程如下图所示:
- 第一个元素添加到最底层的有序链表中(最底层存储了一切元素数据)。
- 第二个元素生成的随机层数是 2,所以再添加 1 层,并将此元素存储在第 1 层和最低层。
- 第三个元素生成的随机层数是 4,所以再添加 2 层,整个跳动表变成了 4 层,将此元素保存到一切层中。
- 第四个元素生成的随机层数是 1,所以把它按次序保存到最后一层中即可。
其他新增节点以此类推。 更多内容请参阅:www.javacn.site/company/red…
9.线程池有哪些参数?
线程池(ThreadPoolExecutor)有 7 个参数,这 7 个参数的意义如下:
- 第 1 个参数:corePoolSize 表明线程池的常驻中心线程数。假如设置为 0,则表明在没有任何使命时,毁掉线程池;假如大于 0,即使没有使命时也会确保线程池的线程数量等于此值。但需求留意,此值假如设置的比较小,则会频频的创立和毁掉线程(创立和毁掉的原因会在本课时的下半部分讲到);假如设置的比较大,则会浪费体系资源,所以开发者需求依据自己的实际事务来调整此值;
- 第 2 个参数:maximumPoolSize 表明线程池在使命最多时,最大能够创立的线程数。官方规定此值必须大于 0,也必须大于等于 corePoolSize,此值只有在使命比较多,且不能存放在使命行列时,才会用到;
- 第 3 个参数:keepAliveTime 表明线程的存活时刻,当线程池空闲时而且超过了此时刻,剩余的线程就会毁掉,直到线程池中的线程数量毁掉的等于 corePoolSize 为止,假如 maximumPoolSize 等于 corePoolSize,那么线程池在空闲的时候也不会毁掉任何线程;
- 第 4 个参数:unit 表明存活时刻的单位,它是合作 keepAliveTime 参数共同运用的;
- 第 5 个参数:workQueue 表明线程池履行的使命行列,当线程池的一切线程都在处理使命时,假如来了新使命就会缓存到此使命行列中排队等候履行;
- 第 6 个参数:threadFactory 表明线程的创立工厂,此参数一般用的比较少,咱们通常在创立线程池时不指定此参数,它会运用默许的线程创立工厂的方法来创立线程;
- 第 7 个参数:RejectedExecutionHandler 表明指定线程池的回绝战略,当线程池的使命已经在缓存行列 workQueue 中存储满了之后,而且不能创立新的线程来履行此使命时,就会用到此回绝战略,它归于一种限流保护的机制。
更多内容请参阅:/post/707292…
10.回绝战略有哪些?
线程池的回绝战略默许有以下 4 种:
- AbortPolicy:间断战略,线程池会抛出异常并间断履行此使命;
- CallerRunsPolicy:把使命交给添加此使命的(main)线程来履行;
- DiscardPolicy:疏忽此使命,疏忽最新的一个使命;
- DiscardOldestPolicy:疏忽最早的使命,最早加入行列的使命。
默许的回绝战略为 AbortPolicy 间断战略。当然除了 JDK 内置的 4 种回绝战略之外,用户还能够自界说回绝战略,经过完成 new RejectedExecutionHandler,并重写 rejectedExecution 方法来完成自界说回绝战略,完成代码如下:
public static void main(String[] args) {
// 使命的具体方法
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("当前使命被履行,履行时刻:" + new Date() +
" 履行线程:" + Thread.currentThread().getName());
try {
// 等候 1s
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// 创立线程,线程的使命行列的长度为 1
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 履行自界说回绝战略的相关操作
System.out.println("我是自界说回绝战略~");
}
});
// 添加并履行 4 个使命
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
}
11.你常用的回绝战略是哪种?为什么?
最常用的回绝战略是自界说回绝战略,由于里边能够完成自己的事务代码,比方,咱们能够经过自界说回绝战略,发送警告信息给相关人员,这样就能及时发现程序履行的问题,同时再将回绝的使命记载下来,让开发人员手动处理,这样就能够及时发现问题,并解决问题了。
12.三个线程替换打印ABC
三个线程替换打印 ABC 的完成方法有很多,我个人比较倾向于运用 JUC 下的 CyclicBarrier(循环栅门,也叫循环屏障)来完成,由于循环栅门天生便是用来完成一轮一轮多线程使命的,它的完成代码如下:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 3 个线程替换打印 ABC
*/
public class ThreadLoopPrint {
// 同享计数器
private static int sharedCounter = 0;
public static void main(String[] args) {
// 打印的内容
String printString = "ABC";
// 界说循环栅门
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
});
// 履行使命
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < printString.length(); i++) {
synchronized (this) {
sharedCounter = sharedCounter > 2 ? 0 : sharedCounter; // 循环打印
System.out.println(printString.toCharArray()[sharedCounter++]);
}
try {
// 等候 3 个线程都打印一遍之后,持续走下一轮的打印
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
};
// 敞开多个线程
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
以上程序履行的成果如下图所示: 更多内容请参阅:www.javacn.site/interview/c…
13.力扣括号生成
参阅官方解题思路和完成代码:leetcode.cn/problems/ge…
参阅&道谢
我没有三颗心脏
本文已收录到我的面试小站 www.javacn.site,其中包括的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、规划模式、消息行列等模块。