写点什么

2021 年阿里 Java 高级面试题及答案

发布于: 12 小时前

数据库

1. MySQL 索引使用有哪些注意事项呢?

可以从三个维度回答这个问题:索引哪些情况会失效,索引不适合哪些场景,索引规则

索引哪些情况会失效

  • 查询条件包含 or,可能导致索引失效

  • 如何字段类型是字符串,where 时一定用引号括起来,否则索引失效

  • like 通配符可能导致索引失效。

  • 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。

  • 在索引列上使用 mysql 的内置函数,索引失效。

  • 对索引列运算(如,+、-、*、/),索引失效。

  • 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。

  • 索引字段上使用 is null, is not null,可能导致索引失效。

  • 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。

  • mysql 估计使用全表扫描要比使用索引快,则不使用索引。

索引不适合哪些场景

  • 数据量少的不适合加索引

  • 更新比较频繁的也不适合加索引

  • 区分度低的字段不适合加索引(如性别)

索引的一些潜规则

  • 覆盖索引

  • 回表

  • 索引数据结构(B+树)

  • 最左前缀原则

  • 索引下推

2. MySQL 遇到过死锁问题吗,你是如何解决的?

我排查死锁的一般步骤是酱紫的:


  • 查看死锁日志 show engine innodb status;

  • 找出死锁 Sql

  • 分析 sql 加锁情况

  • 模拟死锁案发

  • 分析死锁日志

  • 分析死锁结果

3. 日常工作中你是怎么优化 SQL 的?

可以从这几个维度回答这个问题:


  • 加索引

  • 避免返回不必要的数据

  • 适当分批量进行

  • 优化 sql 结构

  • 分库分表

  • 读写分离

4. 说说分库与分表的设计

分库分表方案,分库分表中间件,分库分表可能遇到的问题


分库分表方案:


  • 水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中的数据拆分到多个库中。

  • 水平分表:以字段为依据,按照一定策略(hash、range 等),将一个表中的数据拆分到多个表中。

  • 垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。

  • 垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。


常用的分库分表中间件:


  • sharding-jdbc(当当)

  • Mycat

  • TDDL(淘宝)

  • Oceanus(58 同城数据库中间件)

  • vitess(谷歌开发的数据库中间件)

  • Atlas(Qihoo 360)


分库分表可能遇到的问题


  • 事务问题:需要用分布式事务啦

  • 跨节点 Join 的问题:解决这一问题可以分两次查询实现

  • 跨节点的 count,order by,group by 以及聚合函数问题:分别在各个节点上得到结果后在应用程序端进行合并。

  • 数据迁移,容量规划,扩容等问题

  • ID 问题:数据库被切分后,不能再依赖数据库自身的主键生成机制啦,最简单可以考虑 UUID

  • 跨分片的排序分页问题(后台加大 pagesize 处理?)

5. InnoDB 与 MyISAM 的区别

  • InnoDB 支持事务,MyISAM 不支持事务

  • InnoDB 支持外键,MyISAM 不支持外键

  • InnoDB 支持 MVCC(多版本并发控制),MyISAM 不支持

  • select count(*) from table 时,MyISAM 更快,因为它有一个变量保存了整个表的总行数,可以直接读取,InnoDB 就需要全表扫描。

  • Innodb 不支持全文索引,而 MyISAM 支持全文索引(5.7 以后的 InnoDB 也支持全文索引)

  • InnoDB 支持表、行级锁,而 MyISAM 支持表级锁。

  • InnoDB 表必须有主键,而 MyISAM 可以没有主键

  • Innodb 表需要更多的内存和存储,而 MyISAM 可被压缩,存储空间较小,。

  • Innodb 按主键大小有序插入,MyISAM 记录插入顺序是,按记录插入顺序保存。

  • InnoDB 存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全,与 MyISAM 比 InnoDB 写的效率差一些,并且会占用更多的磁盘空间以保留数据和索引

6. 数据库索引的原理,为什么要用 B+树,为什么不用二叉树?

可以从几个维度去看这个问题,查询是否够快,效率是否稳定,存储数据多少,以及查找磁盘次数,为什么不是二叉树,为什么不是平衡二叉树,为什么不是 B 树,而偏偏是 B+树呢?


为什么不是一般二叉树?


如果二叉树特殊化为一个链表,相当于全表扫描。平衡二叉树相比于二叉查找树来说,查找效率更稳定,总体的查找速度也更快。


为什么不是平衡二叉树呢?


我们知道,在内存比在磁盘的数据,查询效率快得多。如果树这种数据结构作为索引,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块,但是平衡二叉树可是每个节点只存储一个键值和数据的,如果是 B 树,可以存储更多的节点数据,树的高度也会降低,因此读取磁盘的次数就降下来啦,查询效率就快啦。


那为什么不是 B 树而是 B+树呢?


1)B+树非叶子节点上是不存储数据的,仅存储键值,而 B 树节点中不仅存储键值,也会存储数据。innodb 中页的默认大小是 16KB,如果不存储数据,那么就会存储更多的键值,相应的树的阶数(节点的子节点树)就会更大,树就会更矮更胖,如此一来我们查找数据进行磁盘的 IO 次数有会再次减少,数据查询的效率也会更快。


2)B+树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的,链表连着的。那么 B+树使得范围查找,排序查找,分组查找以及去重查找变得异常简单。

7. 聚集索引与非聚集索引的区别

  • 一个表中只能拥有一个聚集索引,而非聚集索引一个表可以存在多个。

  • 聚集索引,索引中键值的逻辑顺序决定了表中相应行的物理顺序;非聚集索引,索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。

  • 索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

  • 聚集索引:物理存储按照索引排序;非聚集索引:物理存储不按照索引排序;


何时使用聚集索引或非聚集索引?


8. limit 1000000 加载很慢的话,你是怎么解决的呢?

方案一:如果 id 是连续的,可以这样,返回上次查询的最大记录(偏移量),再往下 limit

select id,name from employee where id>1000000 limit 10.
复制代码

方案二:在业务允许的情况下限制页数:

建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。

方案三:order by + 索引(id 为索引)

select id,name from employee order by id  limit 1000000,10
复制代码

方案四:利用延迟关联或者子查询优化超多分页场景。(先快速定位需要获取的 id 段,然后再关联)

SELECT a.* FROM employee a, (select id from employee where 条件 LIMIT 1000000,10 ) b where a.id=b.id
复制代码

9. 如何选择合适的分布式主键方案呢?

  • 数据库自增长序列或字段。

  • UUID。

  • Redis 生成 ID

  • Twitter 的 snowflake 算法

  • 利用 zookeeper 生成唯一 ID

  • MongoDB 的 ObjectId

10. 事务的隔离级别有哪些?MySQL 的默认隔离级别是什么?

  • 读未提交(Read Uncommitted)

  • 读已提交(Read Committed)

  • 可重复读(Repeatable Read)

  • 串行化(Serializable)


Mysql 默认的事务隔离级别是可重复读(Repeatable Read)

11. 什么是幻读,脏读,不可重复读呢?

  • 事务 A、B 交替执行,事务 A 被事务 B 干扰到了,因为事务 A 读取到事务 B 未提交的数据,这就是脏读

  • 在一个事务范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读

  • 事务 A 查询一个范围的结果集,另一个并发事务 B 往这个范围中插入/删除了数据,并静悄悄地提交,然后事务 A 再次查询相同的范围,两次读取得到的结果集不一样了,这就是幻读

12. 在高并发情况下,如何做到安全的修改同一行数据?

要安全的修改同一行数据,就要保证一个线程在修改时其它线程无法更新这行记录。一般有悲观锁和乐观锁两种方案~

使用悲观锁

悲观锁思想就是,当前线程要进来修改数据时,别的线程都得拒之门外~ 比如,可以使用 select…for update ~


select * from User where name=‘jay’ for update
复制代码


以上这条 sql 语句会锁定了 User 表中所有符合检索条件(name=‘jay’)的记录。本次事务提交之前,别的线程都无法修改这些记录。

使用乐观锁

乐观锁思想就是,有线程过来,先放过去修改,如果看到别的线程没修改过,就可以修改成功,如果别的线程修改过,就修改失败或者重试。实现方式:乐观锁一般会使用版本号机制或 CAS 算法实现。

13. 数据库的乐观锁和悲观锁。

悲观锁:

悲观锁她专一且缺乏安全感了,她的心只属于当前事务,每时每刻都担心着它心爱的数据可能被别的事务修改,所以一个事务拥有(获得)悲观锁后,其他任何事务都不能对数据进行修改啦,只能等待锁被释放才可以执行。


乐观锁:

乐观锁的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。实现方式:乐观锁一般会使用版本号机制或 CAS 算法实现。


14. SQL 优化的一般步骤是什么,怎么看执行计划(explain),如何理解其中各个字段的含义。

  • show status 命令了解各种 sql 的执行频率

  • 通过慢查询日志定位那些执行效率较低的 sql 语句

  • explain 分析低效 sql 的执行计划(这点非常重要,日常开发中用它分析 Sql,会大大降低 Sql 导致的线上事故)

15. select for update 有什么含义,会锁表还是锁行还是其他。

select for update 含义

select 查询语句是不会加锁的,但是 select for update 除了有查询的作用外,还会加锁呢,而且它是悲观锁哦。至于加了是行锁还是表锁,这就要看是不是用了索引/主键啦。


没用索引/主键的话就是表锁,否则就是是行锁。

总目录展示

该笔记共八个节点(由浅入深),分为三大模块。


高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这 4 个方面重点介绍。


一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。


高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个 PlanB 来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。




篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),觉得有需要的码友们,麻烦各位转发一下(可以帮助更多的人看到哟!)点这里,即可获得免费下载的方式!!




由于内容太多,这里只截取部分的内容。需要这份《高并发秒杀顶级教程》的小伙伴,麻烦各位帮忙点赞分享支持一下(可以帮助更多的人看到哟!)

用户头像

还未添加个人签名 2021.07.29 加入

还未添加个人简介

评论

发布
暂无评论
2021年阿里Java高级面试题及答案