Java面试指导
我们是做就业服务的工作室,没有任何培训机构性质!!
主做Java、python、c++,前端vue,react等,
全国各地,简历包装,投递邀约,视频面试,技术面试包通过,离职背调等,
通过正常上班,不拿offer不收费,
不要浪费投递简历的机会和面试机会,
如果已经在职的话,并且不满意目前薪资也可以联系我们。关注公众号回复“就业”即可。

Mysql
索引的基本原理
索引⽤来快速地寻找那些具有特定值的记录。如果没有索引,⼀般来说执⾏查询时遍历整张表。
索引的原理:就是把⽆序的数据变成有序的查询
- 把创建了索引的列的内容进行排序
- 对排序结果生成倒排表
- 在倒排表内容上拼上数据地址链
- 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
Mysql聚簇和⾮聚簇索引的区别
都是B+树的数据结构
- 聚簇索引:将数据存储与索引放到了⼀块、并且是按照⼀定的顺序组织的,找到索引也就找到了数 据,数据的物理存放顺序与索引顺序是⼀致的,即:只要索引是相邻的,那么对应的数据⼀定也是 相邻地存放在磁盘上的
- ⾮聚簇索引:叶⼦节点不存储数据、存储的是数据⾏地址,也就是说根据索引查找到数据⾏的位置 再取磁盘查找数据,这个就有点类似⼀本树的⽬录,⽐如我们要找第三章第⼀节,那我们先在这个 ⽬录⾥⾯找,找到对应的⻚码后再去对应的⻚码看⽂章。
优势:
- 查询通过聚簇索引可以直接获取数据,相⽐⾮聚簇索引需要第⼆次查询(⾮覆盖索引的情况 下)效率要⾼
- 聚簇索引对于范围查询的效率很⾼,因为其数据是按照⼤⼩排列的
- 聚簇索引适合⽤在排序的场合,⾮聚簇索引不适合
劣势:
- 维护索引很昂贵,特别是插⼊新⾏或者主键被更新导⾄要分⻚(page split)的时候。建议在⼤量插⼊ 新⾏后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,因为必须被移动的⾏数据可能造成碎 ⽚。使⽤独享表空间可以弱化碎⽚
- 表因为使⽤UUID(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有⽐全表扫⾯ 更慢,所以建议使⽤int的auto_increment作为主键
- 如果主键⽐较⼤的话,那辅助索引将会变的更⼤,因为辅助索引的叶⼦存储的是主键值;过⻓的主 键值,会导致⾮叶⼦节点占⽤占⽤更多的物理空间
InnoDB中⼀定有主键,主键⼀定是聚簇索引,不⼿动设置、则会使⽤unique索引,没有unique索引, 则会使⽤数据库内部的⼀个⾏的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要⼆次查找,⾮聚簇索引都是辅助索引,像复合索引、前缀索引、唯⼀索引, 辅助索引叶⼦节点存储的不再是⾏的物理位置,⽽是主键值
MyISM使⽤的是⾮聚簇索引,没有聚簇索引,⾮聚簇索引的两棵B+树看上去没什么不同,节点的结构完 全⼀致只是存储的内容不同⽽已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。 表数据存储在独⽴的地⽅,这两颗B+树的叶⼦节点都使⽤⼀个地址指向真正的表数据,对于表数据来 说,这两个键没有任何差别。由于索引树是独⽴的,通过辅助键检索⽆需访问主键的索引树。
如果涉及到⼤数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占 空间⼩,这些操作是需要在内存中完成的。
Mysql索引的数据结构,各⾃优劣
索引的数据结构和具体存储引擎的实现有关,在MySQL中使⽤较多的索引有Hash索引,B+树索引等, InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因 此在绝⼤多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余⼤部分场景,建议 选择BTree索引。
B+树:B+树是⼀个平衡的多叉树,从根节点到每个叶⼦节点的⾼度差值不超过1,⽽且同层级的节点间 有指针相互链接。在B+树上的常规检索,从根节点到叶⼦节点的搜索效率基本相当,不会出现⼤幅波 动,⽽且基于索引的顺序扫描时,也可以利⽤双向指针快速左右移动,效率⾮常⾼。因此,B+树索引被 ⼴泛应⽤于数据库、⽂件系统等场景。
哈希索引:哈希索引就是采⽤⼀定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样 从根节点到叶⼦节点逐级查找,只需⼀次哈希算法即可⽴刻定位到相应的位置,速度⾮常快
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过⼀次算法即可找到相应的键值;前提 是键值都是唯⼀的。如果键值不是唯⼀的,就需要先找到该键所在位置,然后再根据链表往后扫描,直 到找到相应的数据;
如果是范围查询检索,这时候哈希索引就毫⽆⽤武之地了,因为原先是有序的键值,经过哈希算法后, 有可能变成不连续的了,就没办法再利⽤索引完成范围查询检索;
哈希索引也没办法利⽤索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实 本质上也是范围查询);
哈希索引也不⽀持多列联合索引的最左匹配规则;
B+树索引的关键字检索效率⽐较平均,不像B树那样波动幅度⼤,在有⼤量重复键值情况下,哈希索引 的效率也是极低的,因为存在哈希碰撞问题。
索引设计的原则?
查询更快、占⽤空间更⼩
- 适合索引的列是出现在where子句中的列,或者连接子句中指定的列
- 基数较小的表,索引效果较差,没有必要在此列建立索引
- 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配。
- 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
- 定义有外键的数据列一定要建立索引。
- 更新频繁字段不适合创建索!
- 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
- 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(,b)的索引,那么只需要修改原来的索引即可。
- 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
- 对于定义为text、image和bit的数据类型的列不要建立索引。
InnoDB存储引擎的锁的算法
- Record lock:单个⾏记录上的锁
- Gap lock:间隙锁,锁定⼀个范围,不包括记录本身
- Next-key lock:record+gap 锁定⼀个范围,包含记录本身
相关知识点:
- innodb对于行的查询使用next-key lock
- Next-locking keying.为了解决Phantom Problem幻读问题
- 当查询的索引含有唯一属性时,将next-key lock降级为record key
- Gp锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
- 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A.将事务隔离级别设置为RCB.将参数innodb_locks_unsafe_for_binlog设置为1
关⼼过业务系统⾥⾯的sql耗时吗?统计过慢查询吗?对慢查询都 怎么优化过?
在业务系统中,除了使⽤主键进⾏的查询,其他的都会在测试库上测试其耗时,慢查询的统计主要由运 维在做,会定期将业务中的慢查询反馈给我们。
慢查询的优化⾸先要搞明⽩慢的原因是什么?是查询条件没有命中索引?是load了不需要的数据列?还 是数据量太⼤?
所以优化也是针对这三个⽅向来的,
⾸先分析语句,看看是否load了额外的数据,可能是查询了多余的⾏并且抛弃掉了,可能是加载了 许多结果中并不需要的列,对语句进⾏分析以及重写。
分析语句的执⾏计划,然后获得其使⽤索引的情况,之后修改语句或者修改索引,使得语句可以尽 可能的命中索引。
如果对语句的优化已经⽆法进⾏,可以考虑表中的数据量是否太⼤,如果是的话可以进⾏横向或者 纵向的分表。
事务的基本特性和隔离级别
事务基本特性ACID分别是:
原⼦性指的是⼀个事务中的操作要么全部成功,要么全部失败。
⼀致性指的是数据库总是从⼀个⼀致性的状态转换到另外⼀个⼀致性的状态。⽐如A转账给B100块钱, 假设A只有90块,⽀付之前我们数据库⾥的数据都是符合约束的,但是如果事务执⾏成功了,我们的数据库 数据就破坏约束了,因此事务不能成功,这⾥我们说事务提供了⼀致性的保证
隔离性指的是⼀个事务的修改在最终提交前,对其他事务是不可⻅的。
持久性指的是⼀旦事务提交,所做的修改就会永久保存到数据库中。
隔离性有4个隔离级别,分别是:
- read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。 ⽤户本来应该读取到id=1的⽤户age应该是10,结果读取到了其他事务还没有提交的事务,结果读 取结果age=20,这就是脏读。
- read commit 读已提交,两次读取结果不⼀致,叫做不可重复读。 不可重复读解决了脏读的问题,他只会读取已经提交的事务。 ⽤户开启事务读取id=1⽤户,查询到age=10,再次读取发现结果=20,在同⼀个事务⾥同⼀个查询 读取到不同的结果叫做不可重复读。
- repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都⼀样,但是有可能产 ⽣幻读。
- serializable 串⾏,⼀般是不会使⽤的,他会给每⼀⾏读取的数据加锁,会导致⼤量超时和锁竞争 的问题。
脏读(Drity Read):某个事务已更新⼀份数据,另⼀个事务在此时读取了同⼀份数据,由于某些原因, 前⼀个RollBack了操作,则后⼀个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在⼀个事务的两次查询之中数据不⼀致,这可能是两次查询过程中 间插⼊了⼀个事务更新的原有的数据。
幻读(Phantom Read):在⼀个事务的两次查询中数据笔数不⼀致,例如有⼀个事务查询了⼏列(Row)数 据,⽽另⼀个事务却在此时插⼊了新的⼏列数据,先前的事务在接下来的查询中,就会发现有⼏列数据 是它先前所没有的。
ACID靠什么保证的?
A原⼦性由undo log⽇志保证,它记录了需要回滚的⽇志信息,事务回滚时撤销已经执⾏成功的sql
C⼀致性由其他三⼤特性保证、程序代码要保证业务上的⼀致性
I隔离性由MVCC来保证
D持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可 以从redo log恢复
1 | InnoDB redo log 写盘,InnoDB 事务进⼊prepare 状态。 |
redolog的刷盘会在系统空闲时进⾏
什么是MVCC
多版本并发控制:读取数据时通过⼀种类似快照的⽅式将数据保存下来,这样读锁就和写锁不冲突了, 不同的事务session会看到⾃⼰特定版本的数据,版本链
MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下⼯作。其他两个隔离级别够 和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据⾏, ⽽不是符合当前事务版本的数据 ⾏。⽽ SERIALIZABLE 则会对所有读取的⾏都加锁。
聚簇索引记录中有两个必要的隐藏列:
trx_id:⽤来存储每次对某条聚簇索引记录进⾏修改的时候的事务id。
roll_pointer:每次对哪条聚簇索引记录有修改的时候,都会把⽼版本写⼊undo⽇志中。这个 roll_pointer就是存了⼀个指针,它指向这条聚簇索引记录的上⼀个版本的位置,通过它来获得上⼀个版 本的记录信息。(注意插⼊操作的undo⽇志没有这个属性,因为它没有⽼版本)
已提交读和可重复读的区别就在于它们⽣成ReadView的策略不同。
开始事务时创建readview,readView维护当前活动的事务id,即未提交的事务id,排序⽣成⼀个数组 访问数据,获取数据中的事务id(获取的是事务id最⼤的记录),对⽐readview:
如果在readview的左边(⽐readview都⼩),可以访问(在左边意味着该事务已经提交)
如果在readview的右边(⽐readview都⼤)或者就在readview中,不可以访问,获取roll_pointer,取 上⼀版本重新对⽐(在右边意味着,该事务在readview⽣成之后出现,在readview中意味着该事务还未 提交)
已提交读隔离级别下的事务在每次查询的开始都会⽣成⼀个独⽴的ReadView,⽽可重复读隔离级别则在 第⼀次读的时候⽣成⼀个ReadView,之后的读都复⽤之前的ReadView。
这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView⽣成策略的不 同实现不同的隔离级别。
分表后⾮sharding_key的查询怎么处理,分表后的排序?
- 可以做⼀个mapping表,⽐如这时候商家要查询订单列表怎么办呢?不带user_id查询的话你总不能 扫全表吧?所以我们可以做⼀个映射关系表,保存商家和⽤户的关系,查询的时候先通过商家查询 到⽤户列表,再通过user_id去查询。
- 宽表,对数据实时性要求不是很⾼的场景,⽐如查询订单列表,可以把订单表同步到离线(实时) 数仓,再基于数仓去做成⼀张宽表,再基于其他如es提供查询服务。
- 数据量不是很⼤的话,⽐如后台的⼀些查询之类的,也可以通过多线程扫表,然后再聚合结果的⽅ 式来做。或者异步的形式也是可以的。
union
排序字段是唯⼀索引:
- 首先第一页的查询:将各表的结果集进行合并,然后再次排序
- 第二页及以后的查询,需要传入上一页排序字段的最后一个值,及排序方式。
- 根据排序方式,及这个值进行查询。如排序字段date,上一页最后值为3,排序方式降序。查询的时候sql为select … from table where date<3 order by date desc limit 0,10。这样再将几个表的结果合并排序即可。
Mysql主从同步原理
mysql主从同步的过程:
Mysql的主从复制中主要有三个线程: 、 master ( binlog dump thread )、 slave ( I/O thread 、 SQL thread ),Master⼀条线程和Slave中的两条线程。
- 主节点binlog,主从复制的基础是主库记录数据库的所有变更记录到binlog。binlog是数据库服务器启动的那一刻起,保存所有修改数据库结构或内容的一个文件。
- 主节点log dump线程,当binlog有变动时,log dump线程读取其内容并发送给从节点。
- 从节点I/O线程接收binlog内容,并将其写入到relay log文件中。
- 从节点的SQL线程读取relay log文件内容对数据更新进行重放,最终保证主从数据库的一致性。
注:主从节点使⽤ binglog ⽂件 + position 偏移量来定位主从同步的位置,从节点会保存其已接收到的 偏移量,如果从节点发⽣宕机重启,则会⾃动从 position 的位置发起同步。
由于mysql默认的复制⽅式是异步的,主库把⽇志发送给从库后不关⼼从库是否已经处理,这样会产⽣ ⼀个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,⽇志就丢失了。由此产⽣两个概念。
全同步复制
主库写⼊binlog后强制同步⽇志到从库,所有的从库都执⾏完成后才返回给客户端,但是很显然这个⽅ 式的话性能会受到严重影响。
半同步复制
和全同步不同的是,半同步复制的逻辑是这样,从库写⼊⽇志成功后返回ACK确认给主库,主库收到⾄ 少⼀个从库的确认就认为写操作完成。
简述MyISAM和InnoDB的区别
MyISAM:
- 不支持事务,但是每次查询都是原子的;
- 支持表级锁,即每次操作是对整个表加锁;
- 存储表的总行数;
- 一个MYISAM表有三个文件:索引文件、表结构文件、数据文件;
- 采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
InnoDb:
- 支持ACD的事务,支持事务的四种隔离级别;
- 支持行级锁及外键约束:因此可以支持写并发;
- 不存储总行数;
- 一个noDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
- 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。
简述Mysql中索引类型及对数据库的性能的影响
普通索引:允许被索引的数据列包含重复的值。
唯⼀索引:可以保证数据记录的唯⼀性。
主键:是⼀种特殊的唯⼀索引,在⼀张表中只能定义⼀个主键索引,主键⽤于唯⼀标识⼀条记录,使⽤ 关键字 PRIMARY KEY 来创建。
联合索引:索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引。
全⽂索引:通过建⽴ 倒排索引,可以极⼤的提升检索效率,解决判断字段是否包含的问题,是⽬前搜索引 擎使⽤的⼀种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全⽂索 引
索引可以极⼤的提⾼数据的查询速度。
通过使⽤索引,可以在查询的过程中,使⽤优化隐藏器,提⾼系统的性能。
但是会降低插⼊、删除、更新表的速度,因为在执⾏这些写操作时,还要操作索引⽂件
索引需要占物理空间,除了数据表占数据空间之外,每⼀个索引还要占⼀定的物理空间,如果要建⽴聚 簇索引,那么需要的空间就会更⼤,如果⾮聚集索引很多,⼀旦聚集索引改变,那么所有⾮聚集索引都 会跟着变。
Explain语句结果中各个字段分表表示什么
列名 | 描述 |
---|---|
id | 查询语句中每出现⼀个SELECT关键字,MySQL 就会为它分配⼀个唯⼀的id值,某些⼦查询会被 优化为join查询,那么出现的id会⼀样 |
select_type | SELECT关键字对应的那个查询的类型 |
table | 表名 |
partitions | 匹配的分区信息 |
type | 针对单表的查询⽅式(全表扫描、索引) |
possible_keys | 可能⽤到的索引 |
key | 实际上使⽤的索引 |
key_len | 实际使⽤到的索引⻓度 |
ref | 当使⽤索引列等值查询时,与索引列进⾏等值匹 配的对象信息 |
rows | 预估的需要读取的记录条数 |
filtered | 某个表经过搜索条件过滤后剩余记录条数的百分 ⽐ |
Extra | ⼀些额外的信息,⽐如排序等 |
索引覆盖是什么
索引覆盖就是⼀个SQL在执⾏时,可以利⽤索引来快速查找,并且此SQL所要查询的字段在当前索引对 应的字段中都包含了,那么就表示此SQL⾛完索引后不⽤回表了,所需要的字段都在当前索引的叶⼦节 点上存在,可以直接作为结果返回了
最左前缀原则是什么
当⼀个SQL想要利⽤索引是,就⼀定要提供该索引所对应的字段中最左边的字段,也就是排在最前⾯的 字段,⽐如针对a,b,c三个字段建⽴了⼀个联合索引,那么在写⼀个sql时就⼀定要提供a字段的条件,这 样才能⽤到联合索引,这是由于在建⽴a,b,c三个字段的联合索引时,底层的B+树是按照a,b,c三个字段 从左往右去⽐较⼤⼩进⾏排序的,所以如果想要利⽤B+树进⾏快速查找也得符合这个规则
Innodb是如何实现事务的
Innodb通过Buffer Pool,LogBuffer,Redo Log,Undo Log来实现事务,以⼀个update语句为例:
- Innodb在收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在Buffer Pool中
- 执行update语句,修改Buffer Pool中的数据,也就是内存中的数据
- 针对update语句生成一个RedoLog:对象,并存入LogBuffert中
- 针对update语句生成undolog日志,用于事务回滚
- 如果事务提交,那么则把RedoLog对象进行持久化,后续还有其他机制将Buffer Pool中所修改的数据页持久化到磁盘中
- 如果事务回滚,则利用undolog日志进行回滚
B树和B+树的区别,为什么Mysql使⽤B+树
B树的特点:
- 节点排序
- ⼀个节点了可以存多个元素,多个元素也排序了
B+树的特点:
- 拥有B树的特点
- 叶⼦节点之间有指针
- ⾮叶⼦节点上的元素在叶⼦节点上都冗余了,也就是叶⼦节点中存储了所有的元素,并且排好顺序
Mysql索引使⽤的是B+树,因为索引是⽤来加快查询的,⽽B+树通过对数据进⾏排序所以是可以提⾼查 询速度的,然后通过⼀个节点中可以存储多个元素,从⽽可以使得B+树的⾼度不会太⾼,在Mysql中⼀ 个Innodb⻚就是⼀个B+树节点,⼀个Innodb⻚默认16kb,所以⼀般情况下⼀颗两层的B+树可以存2000 万⾏左右的数据,然后通过利⽤B+树叶⼦节点存储了所有数据并且进⾏了排序,并且叶⼦节点之间有指 针,可以很好的⽀持全表扫描,范围查找等SQL语句。
Mysql锁有哪些,如何理解
按锁粒度分类:
- 行锁:锁某行数据,锁粒度最小,并发度高
- 表锁:锁整张表,锁粒度最大,并发度低
- 间隙锁:锁的是一个区间
还可以分为:
- 共享锁:也就是读锁,⼀个事务给某⾏数据加了读锁,其他事务也可以读,但是不能写
- 排它锁:也就是写锁,⼀个事务给某⾏数据加了写锁,其他事务不能读,也不能写
还可以分为:
- 乐观锁:并不会真正的去锁某⾏记录,⽽是通过⼀个版本号来实现的
- 悲观锁:上⾯所的⾏锁、表锁等都是悲观锁
在事务的隔离级别实现中,就需要利⽤锁来解决幻读
Mysql慢查询该如何优化?
- 检查是否走了索引,如果没有则优化SQL利用索引
- 检查所利用的索引,是否是最优索引
- 检查所查字段是否都是必须的,是否查询了过多字段,查出了多余数据
- 检查表中数据是否过多,是否应该进行分库分表了
- 检查数据库实例所在机器的性能配置,是否太低,是否可以适当增加资源