Skip to main content

综合1-20

1.说说 Java 中 HashMap 的原理?

展开

思路:数据结构,底层实现,hash到数组位置,扩容

  • HashMap是基于哈希表的数据结构,底层实现是桶数组,存储的单元是KV节点,通过对Key的hash,生成index,将节点散列到数组的对应位置。 当有hash冲突的时候,会在对应的位置上生成一个链表,当超过一定阈值的时候,会转化成红黑树。
  • 扩容的负载因子默认0.75,初始数组大小16,链表数化的阈值是链表长度大于8并且数组长度大于64。
  • 通过hash值计算数组位置一般是用取模的方式,在hashmap中,length为n的2次方,所以 h & (length -1) 相当于h%length,这种与运算提高了运算效率。

2.Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别?

展开

思路:锁粒度,size计算,扩容

  1. 同步锁的粒度不同,1.7在段上加锁。1.8是桶级别的锁,并且插入的时候先用CAS尝试插入空的table槽,当插入node节点的时候才会加同步锁。
  2. size的计算方法不同,1.7先尝试无锁统计所有 Segment 的 size,如果失败,锁住所有 Segment 重算。1.8使用 CounterCell 数组分段计数,类似 LongAdder,性能更高。
  3. 扩容机制不同,1.7分段扩容,每个段之间互不影响,扩容的时候会锁段。1.8是全局扩容,将扩容任务分成多端,线程之间合作扩容。

3.为什么 JDK 1.8 对 HashMap 进行了红黑树的改动?

展开

思路:时间复杂度,散列不均

当链表长度过长的时候,获取链表节点的操作会从hash表的O(1)变成遍历链表的O(n),因此要转化成红黑树,红黑树是一种平衡树,插入、查找、删除的时间复杂度是O(log n),要链表效率高

4.JDK 1.8 对 HashMap 除了红黑树还进行了哪些改动?

展开
  • put插入链表方式:从头插法改为尾插法,需要遍历到尾部节点,性能有所下降,但是扩容时不会形成环,因为尾插法不会修改原节点的next,原节点本身是一个竞态变量。
    • 如何形成环:假设线程1取出一个节点,然后这个节点已经在另一个节点做了扩容,它在新桶的头部,这时候线程1要把这个节点的next指向新桶,就是它自身。
  • hash的计算:将高 16 位与低 16 位异或,进一步打散哈希值,增强哈希值的均匀性,减少冲突。
  • 扩容机制的优化:1.8利用位运算优化迁移逻辑,避免逐个重新计算哈希值。

5.Java 中有哪些集合类?请简单介绍

展开
  • List:ArrayList,LinkedList
  • Set:HashSet,TreeSet,LinkedHashSet
  • Map:HashMap,ConcurrentHashMap,TreeMap,LinkedHashMap,HashTable
  • Queue:LinkedList,PriorityQueue

6.MySQL 索引的最左前缀匹配原则是什么?

展开
  • 当使用联合索引进行条件查询的时候,查询条件必须从最左边的列开始依次匹配。当遇到索引字段的缺失,或者对索引字段进行范围查询的时候,会导致后面的索引不能完全利用。
  • 举个例子,联合索引是(a,b,c),那么如果where条件中只有ac,那c的索引就用不上,但是可以用到索引下推。

7.数据库的脏读、不可重复读和幻读分别是什么?

展开
  • 脏读,读到其他事务未提交的修改数据
  • 不可重复读,多次读取同一行数据时,由于其他事务提交的修改,导致结果不一致
  • 幻读,在当前事务中,执行相同范围查询时,由于其他事务提交的插入或删除,导致结果集不一致

8.MySQL 的存储引擎有哪些?它们之间有什么区别?

展开
  • InnoDB,支持事务,行级锁和外键,支持聚簇索引,有日志系统,支持崩溃恢复,适合高并发读写场景
  • MyISAM,不支持事务,表级锁,适合只读或读多写少的场景

9.MySQL 的覆盖索引是什么?

展开

覆盖索引指的是条件查询中用到的索引字段覆盖到了查询字段,这样就可以在二级索引的b+树上找到所需要的字段,不用回表去查主键索引。

10.MySQL 的索引类型有哪些?

展开
  • 主键索引,又称聚簇索引,在叶子节点存储整行的数据
  • 二级索引,叶子节点只存储索引字段和主键字段
  • 覆盖索引,条件查询中用到的索引字段覆盖到了查询字段
  • 联合索引,索引的key包含了多个字段,按照从左往右的顺序排序
  • 唯一索引,加上唯一约束的索引
  • 其他数据结构的索引,哈希索引,全文索引,空间索引

11. MySQL 的索引下推是什么?

展开

如果 WHERE 条件中包含索引列因为违反最左匹配原则而无法直接用于索引定位的时候,存储引擎可以在扫描索引时应用这些条件进行过滤,而不是将所有匹配的索引记录都回表给 Server 层处理。这样可以减少回表的记录数,提升查询效率。

12.MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?

展开

聚簇索引通常是主键索引,在叶子节点存储了行的所有字段数据,非聚簇索引只在叶子节点存储主键字段和索引字段

13. MySQL 中的回表是什么?

展开

回表是指当查询利用到二级索引但是查询的字段没有完全被二级索引字段覆盖的时候,会把查询到的行根据主键去主键索引里找到其他字段的数据。

14. MySQL 中使用索引一定有效吗?如何排查索引效果?

展开
  • 不一定
  • 当表的数量较小的时候或者查询结果集占比很高的时候,全表查询省去了回表和额外的索引查找操作。
  • 不恰当的索引使用方式,比如隐式类型转化,违反最左匹配,参与了函数运算。
  • 可以执行explain查看执行计划,通过key字段确定是否用到索引,通过type字段确定扫描的类型,通过rows和filtered确定扫描的行数和占比。

15.RabbitMQ 怎么实现延迟队列?

展开
  • 使用 TTL 和死信交换机配合,主队列设置TTL当消息没有消费会被视为死信消息,然后配置死信消息需要转发的交换机和队列
  • 使用 RabbitMQ Delayed Message Exchange Plugin插件

16.MySQL 中的索引数量是否越多越好?为什么?

展开

不是,索引会增加存储空间,降低写入的效率,加重优化器负担,增加维护成本,还可能让优化器选择相对查询效率不高索引

17.为什么 RocketMQ 不使用 Zookeeper 作为注册中心呢?而选择自己实现 NameServer?

展开

Todo

18.请详细描述 MySQL 的 B+ 树中查询数据的全过程

展开

优化器决定使用哪个索引,然后找到对应的根节点,一般一个节点的大小是一页,遍历该页,判断条件的字段值和该页的key的大小,找到下一个页,直到找到叶子节点
如果索引字段没有覆盖到查询字段,就需要根据查询结果去主键索引找到完整的行

19.RabbitMQ 中消息什么时候会进入死信交换机?

展开

思路:拒绝,ttl,过期

队列绑定了死信队列,并且消息被拒绝或者消息设置了ttl过期或者队列满时旧消息会丢弃进入死信队列

20. 为什么 MySQL 选择使用 B+ 树作为索引结构?

展开

思路:B树特性,B+树特性

  • 首先B+树具有B树的特性,是一种平衡树,可以用二分法的查找到叶子节点的key,并且B树的节点可以存放多个key,一般的大小是一页,这使得B+树可以使用很少的层数去 存储更多的数据。
  • 相对于B树来说,B+树的数据集中在叶子节点,能够在叶子节点形成双向链表,方便顺序扫描和范围扫描,并且非叶子节点只存放key,增加了扇出的大小。