MySQL面试常见问题总结

什么是MySQL

MySQL是一个关系型数据库管理系统,由Oracle公司开发、发布和支持,是一种关系型数据库,在Java企业集开发中非常常用,而且开源免费,并且方便扩展,例如你开发网站或Web应用程序,MySQL是个不错的选择

说一下MyISAMInnoDB的区别

MyISAM MySQL 的默认数据库引擎( 5.5 版之前)。虽然性能极佳,而且提供了大量的特性,包括全 文索引、压缩、空间函数等,但MyISAM 不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5 版本之后, MySQL 引入了 InnoDB (事务性数据库引擎), MySQL 5.5 版本后默认的存储 引擎为InnoDB
大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
两者的对比:
1. 是否支持行级锁 : MyISAM 只有表级锁 (table-level locking) ,而 InnoDB 支持行级锁 (row-level
locking) 和表级锁 , 默认为行级锁。
2. 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原子性 , 其执行速度比 InnoDB类型更快,但是不提供事务支持。但是 InnoDB 提供事务支持事务,外部键等高级数据库 功能。 具有事务(commit) 、回滚 (rollback) 和崩溃修复能力 (crash recovery capabilities) 的事务安
(transaction-safe (ACID compliant)) 型表。
3. 是否支持外键: MyISAM 不支持,而 InnoDB 支持。
4. 是否支持 MVCC :仅 InnoDB 支持。应对高并发事务 , MVCC 比单纯的加锁更高效 ;MVCC 只在 READCOMMITTED 和 REPEATABLE READ 两个隔离级别下工作 ;MVCC 可以使用 乐观 (optimistic) 锁 和 悲观(pessimistic) 锁来实现 ; 各数据库中 MVCC 实现并不统一。
MySQL 高性能》上面有一句话这样写到 :
不要轻易相信 “MyISAM InnoDB 之类的经验之谈,这个结论往往不是绝对的。在很多我们已
知场景中, InnoDB 的速度都可以让 MyISAM 望尘莫及,尤其是用到了聚簇索引,或者需要访问的
数据都可以放入内存的应用。
一般情况下我们选择InnoDB都是没有问题的, 但是某些情况下你并不在乎可扩展能力和并发能力,也 不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择 MyISAM 也是一个不错的选择。但是一般 情况下,我们都是需要考虑到这些问题的。

字符集和校队规则

字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。
MySQL 中每一种字符集都会对应一系列的校对规则。
MySQL 采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值, 他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集)

MySQL索引

MySQL索引使用的数据结构主要有BTree索引 哈希索引 。对于哈希索引来说,底层的数据结构就是 哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分 场景,建议选择BTree索引。

MySQL BTree 索引使用的是 B 树中的 B+Tree ,但对于主要的两种存储引擎的实现方式是不同的。
MyISAM: B+Tree 叶节点的 data 域存放的是数据记录的地址。在索引检索的时候,首先按照
B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其 data 域的值,然后以 data 域的值为
地址读取相应的数据记录。这被称为 非聚簇索引
InnoDB: 其数据文件本身就是索引文件。相比 MyISAM ,索引文件和数据文件是分离的,其表数据
文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。这个索
引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。这被称为 聚簇索引(或聚集
索引) 。而其余的索引都作为辅助索引,辅助索引的 data 域存储相应记录主键的值而不是地址,
这也是和 MyISAM 不同的地方。 在根据主索引搜索时,直接找到 key 所在的节点即可取出数据;在
根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建
议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。

什么是事务?

事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000 元,将小红的余额增加 1000 元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

事物的四大特性(ACID)

原子性( Atomicity ): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部
完成,要么完全不起作用;
  一致性( Consistency ): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是
相同的;
  隔离性( Isolation ): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之
间数据库是独立的;
  持久性( Durability ): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库
发生故障也不应该对其有任何影响。

并发事务带来哪些问题?

在典型 应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一 数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读( Dirty read : 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到
数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提
交的数据,那么另外一个事务读到的这个数据是 脏数据 ,依据 脏数据 所做的操作可能是不正确
的。
丢失修改( Lost to modify : 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那
么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结
果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20 ,事务 2 也读取 A=20 ,事
1 修改 A=A-1 ,事务 2 也修改 A=A-1 ,最终结果 A=19 ,事务 1 的修改被丢失。
不可重复读( Unrepeatableread : 指在一个事务内多次读同一数据。在这个事务还没有结束
时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改
导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样
的情况,因此称为不可重复读。
幻读(Phantom read):与不可重复读类似。它发生在一个事务(T1 )读取了几行数据,接
着另一个并发事务( T2 )插入了一些数据时。在随后的查询中,第一个事务( T1 )就会发现多了
一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。

事务隔离级别有哪些?MySQL的默认隔离级别是?

SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED( 读取未提交 ) 最低的隔离级别,允许读取尚未提交的数据变更, 可能会
导致脏读、幻读或不可重复读
READ-COMMITTED( 读取已提交 ) 允许读取并发事务已经提交的数据, 可以阻止脏读,但是幻
读或不可重复读仍有可能发生
REPEATABLE-READ( 可重复读 ) 对同一字段的多次读取结果都是一致的,除非数据是被本身事务 自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
SERIALIZABLE( 可串行化 ) 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执 行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻
 
隔离级别 脏读 不可重复读 幻影读
READ-UNCOMMITTED
   √
         √
     √
READ-COMMITTED
   ×
         √
     √
REPEATABLE-READ
   ×
         ×
     √
SERIALIZABLE
   ×
         ×
      ×

锁机制与InnoDB锁算法

MyISAM InnoDB 存储引擎使用的锁:
MyISAM 采用表级锁 (table-level locking)
InnoDB 支持行级锁 (row-level locking) 和表级锁 , 默认为行级锁
表级锁和行级锁对比:
表级锁: MySQL 中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也
比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,
MyISAM InnoDB 引擎都支持表级锁。
行级锁: MySQL 中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少
数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB 存储引擎的锁的算法有三种:
Record lock :单个行记录上的锁
Gap lock :间隙锁,锁定一个范围,不包括记录本身
Next-key lock record+gap 锁定一个范围,包含记录本身
相关知识点:
1. innodb 对于行的查询使用 next-key lock
2. Next-locking keying 为了解决 Phantom Problem 幻读问题
3. 当查询的索引含有唯一属性时,将 next-key lock 降级为 record key
4. Gap 锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
5. 有两种方式显式关闭 gap 锁:(除了外键约束和唯一性检查外,其余情况仅使用 record lock           A. 将事务隔离级别设置为RC
    B. 将参数 innodb_locks_unsafe_for_binlog 设置为 1

大表优化

MySQL 单表记录数过大时,数据库的 CRUD 性能会明显下降,一些常见的优化措施如下:

1. 限定数据的范围

务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以 控制在一个月的范围内;

2. /写分离

经典的数据库拆分方案,主库负责写,从库负责读

3.垂直分区

根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息, 可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。如下图所示
MySQL面试常见问题总结_第1张图片

垂直拆分的优点:可以使得列数据变小,在查询时减少读取的Block 数,减少 I/O 次数。此外,垂
直分区可以简化表的结构,易于维护。
垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起 Join 操作,可以通过在应用层进行
Join 来解决。此外,垂直分区会让事务变得更加复杂;

4. 水平分区

保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了 分布式的目的。 水平拆分可以支撑非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过 200 万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
MySQL面试常见问题总结_第2张图片
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL 并发能力没有什么意义,所以 水平拆分最好 分库
水平拆分能够 支持非常大的数据量存储,应用端改造也少 ,但 分片事务难以解决 ,跨节点 Join 性能较差,逻辑复杂。《Java 工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、 部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

补充一下数据库分片的两种常见方案:

客户端代理: 分片逻辑在应用端,封装在 jar 包中,通过修改或者封装 JDBC 层来实现。 当当网的
Sharding-JDBC 、阿里的 TDDL 是两种比较常用的实现。
中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在
谈的 Mycat 360 Atlas 、网易DDB等等都是这种架构的实现

解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?

池化设计应该不是一个新名词。我们常见的如 java 线程池、 jdbc 连接池、 redis 连接池等就是这类设计的 代表实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。就好比你去食堂打饭,打饭的大妈会先把饭盛好几份放那里,你来了就直接拿着饭盒加菜即可,不用再临时又盛饭又打菜,效率就高了。除了初始化资源,池化设计还包括如下这些特征:池子的初始值、池子的活跃值、池子的最大值等,这些特征可以直接映射到java 线程池和数据库 连接池的成员属性中。这篇文章对 池化设计思想 介绍的还不错,直接复制过来,避免重复造轮子了。数据库连接本质就是一个 socket 的连接。数据库服务端还要维护一些缓存和用户权限信息之类的 所以占用了一些内存。我们可以把数据库连接池是看做是维护的数据库连接的缓存,以便将来需要对数据库的请求时可以重用这些连接。为每个用户打开和维护数据库连接,尤其是对动态数据库驱动的网站应用程序的请求,既昂贵又浪费资源。在连接池中,创建连接后,将其放置在池中,并再次使用它,因此不 必建立新的连接。如果使用了所有连接,则会建立一个新连接并将其添加到池中。 连接池还减少了用户必须等待建立与数据库的连接的时间。

分库分表之后,id 主键如何处理?

因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。
生成全局 id 有下面这几种方式:
UUID :不适合作为主键,因为太长了,并且无序不可读,查询效率低。比较适合用于生成唯一的
名字的标示比如文件的名字。
数据库自增 id : 两台数据库分别设置不同步长,生成不重复 ID 的策略来实现高可用。这种方式生成
id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。
利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更
加复杂,可用性降低,编码更加复杂,增加了系统成本。
Twitter snowflflake 算法 Github 地址: https://github.com/twitter-archive/snowflflake
美团的 Leaf 分布式 ID 生成系统 Leaf 是美团开源的分布式 ID 生成器,能保证全局唯一性、趋势递
增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也需要依赖关系数据库、
Zookeeper 等中间件。感觉还不错。美团技术团队的一篇文章: https://tech.meituan.com/2017/
04/21/mt-leaf.html

一条SQL语句在MySQL中如何执行的

MySQL面试常见问题总结_第3张图片

第一步:应用程序把查询SQL语句发送给服务器端执行。

  我们在数据库层执行SQL语句时,应用程序会连接到相应的数据库服务器,把SQL语句发送给服务器处理。

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个

第二步:查询缓存

  服务器在解析一个查询语句之前,如果查询缓存是打开的(MySQL默认打开,可以使用have_query_cache查看),在接收到查询请求后,并不会直接去数据库查询,而是在数据库的查询缓存中找是否有相对应的查询数据(某条给定的查询语句在第一次执行时,服务器会缓存这条查询语句和他返回的结果。),如果存在,那么在返回查询结果之前MySQL会检查一次用户权限。这仍然无需解析查询SQL语句,因为查询缓存中已经存放了当前查询需要访问的表信息。如果权限没有问题,key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被则直接从缓存中拿到结果返回给客户端。这种情况下,查询不会被解析不用生成执行计划不会被执行。而其中是否命中缓存是将此查询语句和缓存中的查询语进行比对,如果完全相同,那就认为它们是相同的,就认为命中缓存了如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中

第三步:查询优化处理,生成执行计划 (没有命中缓存)

  接下来服务器会将一个SQL转换成一个执行计划,而这个阶段包括:解析SQL、预处理、优化SQL执行计划其中任何一个阶段出错都会导致查询进行不下去

解析SQL:Mysql通过将SQL语句进行解析,并生成一棵对应的解析树。MySQL解析器将使用MySQL语法分析(语法规则验证)和解析查询,如将验证是否使用错误的关键字,或者关键字的顺序是否正确。

  预处理:预处理器根据一些Mysql规则进一步检查解析树是否合法,如数据表数据列是否存在解析列名别名是否有歧义。接下来预处理器会验证用户权限(precheck)。查看用户是否有相应的操作权限。

  优化SQL:优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序,将SQL语句转化成执行计划,一条查询可以有很多种执行方式,最后都返回相同的结果,最后找到其中最好的执行计划(Mysql使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划的成本,选择其中成本最小的一个)。

第四步:Mysql根据相应的执行计划完成整个查询(此处的执行计划是一个数据结构)

    Mysql根据执行计划给出的指令逐步执行。在此过程中,有大量的操作需要通过调用存储引擎实现的接口完成,这些接口即为“handler API”接口。查询中的每一个表由一个handler的实例表示。(实际上,在优化阶段Mysql就为每一个表创建了一个handelr实例,优化器可以根据这些实例的接口获取表的相关信息,如表的所有列名、索引统计信息等)

第五步:将查询结果返回客户端

  如果查询可以缓存,Mysql在这个阶段也会将结果放到查询缓存中。

MySQL面试常见问题总结_第4张图片

注意:MySQL8之后就没有缓存了 

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