Java面试—MySQL篇

文章目录

    • 1、InnoDB和 MyISAM 的区别?
    • 2、聚簇索引和非聚簇索引的区别?
    • 3、MySQL的索引类型?
    • 4、最左匹配原则是什么?
    • 5、为什么要用B+Tree而不是BTree、普通二叉树呢?
    • 6、索引哪些情况下会失效?如何建立索引呢?
    • 7、事务的ACID原则?幻读、脏读、不可重复读?事务隔离级别?
    • 8、数据库的三大范式?
    • 9、MySQL的数据类型?
    • 10、MySQL有哪几种锁?
    • 11、InnoDB有哪几类行锁呢?
    • 12、意向锁和插入意向锁?
    • 13、乐观锁和悲观锁是什么?
    • 14、binlog 和 redo log 有什么区别?
    • 15、一条更新语句怎么执行的了解吗?
    • 16、主从复制的原理是什么?
    • 17、主从同步延迟怎么处理?
    • 18、什么是分库分表?怎么分?什么情况下需要分?
    • 19、分库分表会带来什么问题?

1、InnoDB和 MyISAM 的区别?

InnoDB MyISAM
存储结构 所有表存储在一个文件中 每个MyISAM存储在三个文件中
事务支持 支持 不支持
最小锁粒度 行级锁 表级锁
索引类型 索引是聚簇索引,数据结构是B+树 索引是非聚簇索引,数据结构是B树
外键支持 支持 不支持

2、聚簇索引和非聚簇索引的区别?

  • 聚簇索引:索引结构和数据一起存放的索引,并不是一种单独的索引类型,例如InnoDB中的主键索引。
  • 非聚簇索引:索引结构和数据分开存放的索引,并不是一种单独的索引类型,例如MyISAM主键和非主键。
优点 缺点
聚簇索引 查询速度很快(B+树是多叉平衡树,叶子结点有序,少做了一次读取数据的IO操作)、对排序查找和范围查找优化(对主键) 依赖于有序的数据、更新数据代价大
非聚簇索引 更新代价比聚簇索引要小一些 依赖于有序的数据、可能会二次查询

3、MySQL的索引类型?

  • 按照数据结构:B+Tree索引、Hash索引、Full-Text索引
  • 按照物理存储:聚簇索引(主键索引)、二级索引(辅助索引)
  • 按照字段特性:主键索引、唯一索引、普通索引、前缀索引
  • 按照字段个数:单列索引、联合索引

B+Tree索引:

  • 主键索引的B+Tree索引:

Java面试—MySQL篇_第1张图片
如果要执行select * from product where id = 5;

  1. 将5与根节点索引数据(1, 10, 20)比较,5在1和10之间,所以会找到(1, 4, 7)
  2. 5与第二层索引数据(1, 4,7)比较,5在4和7之间,所以找到第三层索引数据(4,5, 6)
  3. 在叶子节点索引数据(4, 5, 6)中进行查询,然后找了索引值为5的数据

B+Tree存储千万级别的数据只需要3-4层高度就可以满足,这意味着千万级别的查询目标数据最多需要3-4次磁盘I/O,所以B+Tree相对于BTree和二叉树来说,最大的优势在于查询效率很高,即使在数据量很大的情况下,查询一个数据的磁盘I/O依然维持在3-4次。

  • 二级索引的B+Tree:

Java面试—MySQL篇_第2张图片

二级索引中叶子结点存放的不是实际的数据,而是主键值

Java面试—MySQL篇_第3张图片

先查询到主键值,然后通过主键值再去做一次B+Tree查询,中间的这个过程就叫做回表;在辅助索引里面,不管是单列索引还是联合索引,如果select的数据列只用辅助索引中就能够取得,不用去做主键索引,这个索引就叫做覆盖索引了,也避免了回表。例如是在name上建立了索引,然后又执行select name from user where name = “张三”;这样的话就使用的就是覆盖索引了。

4、最左匹配原则是什么?

在使用联合索引的时候,会根据联合索引中字段的顺序,从左到右去匹配,可以过滤掉一批数据,直到联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询(>、<)才会停止匹配。对于>=、<=、Between、Like这些都不会停止匹配。看下面的例子理解:
比如我们创建一个name和age的联合索引。
Java面试—MySQL篇_第4张图片
当我们使用where name = ‘张三’ and age = '20’去查询数据的时候,B+Tree就会优先比较name来确定下一步要搜索的方向,然后再根据age找到要找的数据,如果只用age去搜索的话,不知道先该去搜什么。

5、为什么要用B+Tree而不是BTree、普通二叉树呢?

对比B Tree:

  1. 它是B Tree的变种,B Tree能解决的问题,它都能解决。
  2. 扫库、扫表能力更强
  3. B+ Tree的磁盘读写能力相对于B Tree来说更强,IO次数更少
  4. 排序能力更强
  5. 效率更加稳定

对比二叉树:

  1. 对于普通二叉树,它存在退化的情况,如果它退化成链表的话,相当于是全表扫描了。
  2. 对于平衡二叉树,读数据的时候,是从磁盘读到内存。按照这样每查找一次数据需要从磁盘中读取一个节点,但是平衡二叉树每个节点只存储一个键值和数据的,如果是B+树,可以存储更多的节点数据,树的高度也会下降,读取磁盘的次数就降下来了,查询效率就快了。

6、索引哪些情况下会失效?如何建立索引呢?

失效的情况:

  1. 查询字段中包含or,可能会导致失效
  2. 如果查询字段类型是字符串,where时一定要用引号引起来,否则会因为隐式类型转换而失效
  3. like通配符可能导致索引失效
  4. 存在联合索引的时候,没有按照最左匹配原则进行操作
  5. 对索引列上使用mysql的内置函数
  6. 对索引列使用运算符号。。。。

如何建立索引:

  1. 索引应该建在查询应用频繁的字段上
  2. 索引的个数应该适量
  3. 区分度低的字段,不要建立索引
  4. 频繁更新的值,不要作为主键和索引
  5. 组合索引把区分度高的值放在前面,为了满足最左匹配原则
  6. 不使用无序的值作为索引。。。。

7、事务的ACID原则?幻读、脏读、不可重复读?事务隔离级别?

ACID:

  1. A(Atomicity):原子性,事务作为一个整体,要么都执行,要么都不执行

由redo log来保证,redolog 被称作重做日志,是物理日志,事务提交的时候,必须先将事务的所有日志写入 redo log 持久化,到事务的提交操作才算完成。

  1. C(Consistency):一致性,事务的前后数据必须保持一致,A有10块钱,B有10块钱,事务前是20块钱,A转给B5块钱,事务后A有5块钱,B有15块钱,总量还是20块钱,这就一致了。

undo log 来保证:undo log 是逻辑日志,记录了事务的 insert、update、deltete 操作,回滚的时候做相反的 delete、update、insert 操作来恢复数据。

  1. I(Isolation):隔离性,多个事务并发执行的时,事务之间是互相隔离的,事务之间井水不犯河水。

通过数据库锁的机制实现的。

  1. D(Durability):持久性,事务完成之后,事务对数据库的操作更改,将持久地保存在数据库之中。

幻读、脏读、不可重复读:

  1. 幻读:指的是一个事务读取到了别的事务插入的数据,导致前后数据不一致
  2. 脏读:指一个事务读取了另外一个事务未提交的数据
  3. 不可重复读:在一个事务中读取表中的某一行数据,多次读取结果不同

事务隔离级别:

  1. 读未提交:允许读取尚未提交的数据变更
  2. 读已提交:允许读取并发事务已经提交的数据
  3. 可重复读:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改

默认的事务隔离级别

  1. 串行化:完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰
隔离级别 幻读 脏读 不可重复读
读未提交 能发生 能发生 能发生
读已提交 能阻止 能发生 能发生
可重复读 能阻止 能阻止 能发生
串行化 能阻止 能阻止 能阻止

8、数据库的三大范式?

  1. 第一范式:要求数据库表中的每一列都不可再分

Java面试—MySQL篇_第5张图片

  1. 满足第一范式,每张表只描述一件事情

Java面试—MySQL篇_第6张图片

  1. 满足第一范式、第二范式,第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

Java面试—MySQL篇_第7张图片

但是商业化会采取下面的做法,而不是完完全全的遵从三大范式:

  1. 考虑商业化的需求和目标(成本、用户体验),数据库的性能更加重要
  2. 在规范性能的问题的时候,需要适当考虑一下规范性
  3. 故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
  4. 故意增加一些计算列(从大数据量降低为小数据量的查询;增加索引)

9、MySQL的数据类型?

  1. 数值:
类型 大小
tinyint 十分小的数据 1个字节
smallint 较小的数据 2个字节
mediumint 中等大小的数据 3个字节
int 标准的整数 4个字节
bigint 较大的数据 8个字节
float 浮点数 4个字节
double 浮点型 8个字节
decimal 字符串形式的浮点数
  1. 字符串:
类型 大小
char 字符串固定大小 0-255
varchar 可变字符串 0-65535 (对应java 里面的 String)
tinytext 微型文本 2^8 - 1
text 文本串 2^16 - 1 保存大文本
  1. 时间日期:
类型 说明
data YYYY-MM-DD , 日期格式
time HH::mm:ss,时间格式
datatime YYYY-MM-DD HH::mm:ss 最常用的
timestamp 1970.1.1 到现在的毫秒数
year 年份表示

10、MySQL有哪几种锁?

  1. 按照锁的粒度来划分:
    1. 表锁:开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低,不会出现死锁
    2. 行锁:开销大,加锁慢;会出现死锁,锁定粒度小,发生锁冲突的概率低,并发度高
    3. 页锁:开销和加锁速度介于表锁和行锁之间;会出现死锁;锁粒度也在它两个之间,并发度一般
  2. 按照兼容性来划分:
    1. 共享锁,也叫读锁,互相不阻塞
    2. 排它锁,也叫写锁,阻塞,在一定时间内,只有一个请求能执行写入,并阻止其他锁读取正在写入的数据

11、InnoDB有哪几类行锁呢?

  1. 记录锁:直接锁定某行记录,当我们使用唯一性的索引(包括唯一索引和聚簇索引)进行等值查询且精准匹配到一条记录时候,就会将这条记录锁定住
  2. 间隙锁:顾名思义,锁的是两个记录之间尚没有填入数据的部分,是一个左开右开的区间。
  3. 临键锁:它锁的是间隙加上右边的记录,是一个左开右闭的区间。

12、意向锁和插入意向锁?

  1. 插入意向锁:假如我们有个T1事务,给这个一段数据加了间隙锁,然后有个T2事务,想要在里面插入数据,就会获取一个插入意向锁,等待插入,如果又来了一个T3事务,它也会获得一个插入意向锁,一起等着。
  2. 意向锁:如果需要用到表锁的话,如何判断表中的记录没有行锁呢,一行一行遍历肯定是不行的,性能太差,我们需要用到一个叫做意向锁的东西来快速判断是否可以对某个表使用表锁。

13、乐观锁和悲观锁是什么?

  1. 乐观锁:认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,也就是不采用数据库自身的锁机制,而是通过程序来实现,在程序上我们可以使用版本号和时间戳机制实现。

乐观锁的版本号机制:在表中设计一个版本字段,第一次读的时候,会获取版本的值,对该数据进行操作的时候,会将版本号加1(update … set version = version + 1 where version = version),如果此时有其他的事务,进行了操作,那么version就不同了,不满足where条件,操作就失败了。
时间戳机制:和版本号一样,只不过对比的是时间戳罢了。

  1. 悲观锁:也是一种思想,对数据被其他事务的修改保持保守态度,会通过数据库自身的锁机制来实现,从而保证数据库操作的排他性。

适用场景:

  1. 乐观锁适合读操作多的操作,相对来说写操作比较少,它的优点在于程序实现,不存在死锁的问题,适用场景比较乐观,阻止不了除了程序以外的的数据库操作
  2. 悲观锁比较适合写操作多的场景,因为它写的操作具有排他性,采用悲观锁的方式,可以在数据库层面阻止其他事务对数据的操作权限,防止读-写和写-写的冲突

14、binlog 和 redo log 有什么区别?

binlog redo log
会记录所有与数据库有关的日志记录,包括 InnoDB、MyISAM 等存储引擎的日志 redo log 只记 InnoDB 存储引擎的日志
bin log 记录的是关于一个事务的具体操作内容,即该日志是逻辑日志 redo log 记录的是关于每个页(Page)的更改的物理情况
bin log 仅在事务提交前进行提交,也就是只写磁盘一次 事务进行的过程中,却不断有 redo ertry 被写入 redo log 中
bin log 是追加写入,不会覆盖已经写的文件 redo log 是循环写入和擦除

15、一条更新语句怎么执行的了解吗?

Java面试—MySQL篇_第8张图片

16、主从复制的原理是什么?

Java面试—MySQL篇_第9张图片

  1. master将数据库中数据的变化写入到binlog
  2. slave连接到master
  3. slave创建一个I/O线程向master请求更新的binlog
  4. master 创建一个 dump 线程向 slave 推送 binlog,slave的I/O负责接收,并记录到relay log中
  5. slave读取relay log中的东西,相当于再执行了一遍sql语句,完成主从复制

数据库的读写分离用的就是这个原理

17、主从同步延迟怎么处理?

  1. 主从同步延迟:当master数据库压力骤增的时候,导致不能及时的把数据同步给slave数据库,导致了主从不一致,也就是主从延迟。
  2. 解决办法:
    1. 写操作后的读操作指定发给数据库主服务器
    2. 读从机失败后再读一次主机
    3. 关键业务读写操作全部指向主机,非关键业务采用读写分离

18、什么是分库分表?怎么分?什么情况下需要分?

  1. 分库:就是将数据库中的数据分散到不同的数据库上,可以垂直分库,也可以水平分库
    1. 垂直分库:把单一数据库按照业务进行划分,不同的业务使用不同的数据库,进而将一个数据库的压力分担到了多个数据库

比如将数据库中的用户表、订单表和商品表分别单独的拆分为用户数据库、订单数据库和商品数据库

  2. **水平分库:是把同一个表按照一定规则拆分到不同的数据库中,每个库可以位于不同的服务器上,这样就实现了水平扩展,解决了单表的存储和性能瓶颈的问题**

比如订单表数据量太大,对订单表进行了水平拆分,将切分好的2张订单表分别放在两个不同的数据库中

  1. 分表:就是对单表的数据进行拆分,可以是垂直拆分,也可以是水平拆分
    1. 垂直分表:是对数据库表列的拆分,把一张列比较多的表拆分为多张表

比如将用户信息表中的一些列单独抽出来作为一个表

  2. **水平分表:是对数据库表行进行拆分,把一张行比较多的表拆分为多张表,可以解决单一表数据量过大的问题**

我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

水平拆分只能解决单表数据量大的问题,为了提升性能,我们通常会选择将拆分后的多张表放在不同的数据库中。也就是说,水平分表通常和水平分库同时出现

  1. 情况:
    1. 单表的数据达到千万级别以上,数据库读写速度比较缓慢
    2. 数据库中的数据占用的空间越来越大,备份时间越来越长
    3. 应用的并发量太大

19、分库分表会带来什么问题?

  1. join操作:同一个数据库表中的表分布在了不同的数据库中,导致无法使用join操作
  2. 事务问题:同一个数据库中的表分布在了不同的数据库中,如果单个操作涉及到了多个数据库,那么数据库自带的事务就无法满足我们的需求了
  3. 分布式id:分库之后,数据遍布在不同服务器上的数据库,数据库自带的自增主键就无法发挥作用了,这时候又要引入分布式id了。。

做技术选型的时候,既要考虑这个技术能不能满足我们的要求,是否适合当前业务场景,又要重点考虑其带来的成本。

你可能感兴趣的:(面试,mysql,数据库,java)