关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

蔚来真题和答案,主打一个简单?

发布时间:2023-06-29 13:00:44
问题来源如下: 问题链接:https://www.nowcoder.com/discuss/493178141461041152 答案解析 1.解释脏读/不可重复读/幻读 脏读:指一个事务读取到了另一个事务为提交保存的数据,之后此事务进行了回滚操作,从而导致第一个事务读取了一个不存在的脏数据。 不可重复读:在同一个事务中,同一个查询在不同的时间得到了不同的结果。例如事务在 T1 读取到了某一行数据,在 T2 时间重新读取这一行时候,这一行的数据已经发生修改,所以再次读取时得到了一个和 T1 查询时不同的结果。 幻读:同一个查询在不同时间得到了不同的结果,这就是事务中的幻读问题。例如,一个 SELECT 被执行了两次,但是第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。 不可重复读和幻读的区别 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改); 幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)。2.索引失效的场景有哪些? 常见的索引失效场景有以下这些: 未遵循最左匹配原则 使用列运算 使用函数方法 类型转换 使用 is not null 错误的模糊匹配,使用右 % 开始查询。 具体内容请参考:https://www.javacn.site/interview/mysql/indexinvalid.html 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(分值),它的常见使用场景是:可以用来存储排名信息、关注列表功能,这样就可以根据关注实现排序展示了。 更多内容请参考:https://www.javacn.site/interview/redis/types.html 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,所以把它按顺序保存到最后一层中即可。 其他新增节点以此类推。更多内容请参考:https://www.javacn.site/company/redis_skiplist.html 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 中存储满了之后,并且不能创建新的线程来执行此任务时,就会用到此拒绝策略,它属于一种限流保护的机制。 更多内容请参考:https://juejin.cn/post/7072921565079273480 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(); } } 以上程序执行的结果如下图所示:更多内容请参考:https://www.javacn.site/interview/code/weilai_thread.html 13.力扣括号生成 参考官方解题思路和实现代码:https://leetcode.cn/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-leetcode-solution/ 参考&鸣谢 我没有三颗心脏 本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

/template/Home/leiyu/PC/Static