【精品来袭】MySQL 那些你不知道的面试题

前言

长期整合常面试考点,喜欢的可以收藏。Java面试题目大纲导航

正文


【版权所有,文章不得已任何形式转载】。

部分题目链接 [灵魂拷问]MySQL面试高频一百问(工程师方向)

文章目录

索引相关

关于MySQL的索引,曾经进行过一次总结,文章链接在这里 Mysql索引原理及其优化.

题目

1. 什么是索引?

索引是一种数据结构,可以帮助我们快速的进行数据的查找.

2. 索引是个什么样的数据结构呢?

索引的数据结构和具体存储引擎的实现有关,
在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引.

3. Hash索引和B+树所有有什么区别或者说优劣呢?

首先要知道Hash索引和B+树索引的底层实现原理:
hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据.

那么可以看出他们有以下的不同:

hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询.

因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围.

hash索引不支持使用索引进行排序,原理同上.

hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.AAAA和AAAAB的索引没有相关性.

hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询.

hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低.

因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引.

4. 上面提到了B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,什么是聚簇索引?

在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引.
在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引.如果没有唯一键,则隐式的生成一个键来建立聚簇索引.

当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询.

5. 非聚簇索引一定会回表查询吗?

不一定,
这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询.

举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age <
20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询.

6. 在建立索引的时候,都有哪些需要考虑的因素呢?

建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合.如果需要建立联合索引的话,还需要考虑联合索引中的顺序.此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力.这些都和实际的表结构以及查询方式有关.

7. 联合索引是什么?为什么需要注意联合索引中的顺序?

MySQL可以使用多个字段同时建立一个索引,叫做联合索引.在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引.具体原因为:
MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序.

当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,以此类推.因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面.此外可以根据特例的查询或者表结构进行单独的调整.

8. 创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

MySQL提供了explain命令来查看语句的执行计划,MySQL在执行某个语句之前,会将该语句过一遍查询优化器,之后会拿到对语句的分析,也就是执行计划,其中包含了许多信息.
可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len等字段,分别说明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度.

9. 那么在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢?

  • 使用不等于查询,
  • 列参与了数学运算或者函数
  • 在字符串like时左边是通配符.类似于’%aaa’.
  • 当mysql分析全表扫描比使用索引快的时候不使用索引.
  • 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引.

以上情况,MySQL无法使用索引.

相关文章

1.面试MySql索引

2.Mysql索引原理

3.我以为我对Mysql索引很了解,直到我遇到了阿里的面试官

4.深入理解MySQL索引底层实现原理丨技术干货

5.Mysql索引不会怎么办?6000字长文教会你

事务相关

题目

1. 什么是事务?

理解什么是事务最经典的就是转账的栗子,相信大家也都了解,这里就不再说一边了.
事务是一系列的操作,他们要符合ACID特性.
最常见的理解就是:事务中的操作要么全部成功,要么全部失败.但是只是这样还不够的.

2. ACID是什么?可以详细说一下吗?

A=Atomicity
原子性,就是上面说的,要么全部成功,要么全部失败.不可能只执行一部分操作.
C=Consistency
一致性:系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态.
I=Isolation
隔离性: 通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况.
D=Durability
持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果.

3. 同时有多个事务在进行会怎么样呢?

多事务的并发进行一般会造成以下几个问题:

  • 脏读:

    A事务读取到了B事务未提交的内容,而B事务后面进行了回滚. 不可重复读:> 当设置A事务只能读取B事务已经提交的部分,会造成在A事务内的两次查询,结果竟然不一样,因为在此期间B事务进行了提交操作.

  • 幻读:

    A事务读取了一个范围的内容,而同时B事务在此期间插入了一条数据.造成"幻觉".

4. 怎么解决这些问题呢?MySQL的事务隔离级别了解吗?

MySQL的四种隔离级别如下:

未提交读(READ UNCOMMITTED)

这就是上面所说的例外情况了,这个隔离级别下,其他事务可以看到本事务没有提交的部分修改.因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚).
这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.

已提交读(READ COMMITTED)

其他事务只能读取到本事务已经提交的部分.这个隔离级别有
不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另外一个事务对数据进行了修改.

REPEATABLE READ(可重复读)

可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取id> 10 的数据行时,对涉及到的所有行加上了读锁,此时例外一个事务新插入了一条id=11的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题.

SERIALIZABLE(可串行化)

这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用.

5. Innodb使用的是哪种隔离级别呢?

InnoDB默认使用的是可重复读隔离级别.

6. 对MySQL的锁了解吗?

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制.
就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用.

7. MySQL都有哪些锁呢?像上面那样子进行锁定岂不是有点阻碍并发效率了?

锁的类别上来讲,有 > 共享锁排他锁.

  • 共享锁: 又叫做读锁. 当用户要进行数据的读取时,对数据加上共享锁.共享锁可以同时加上多个.
  • 排他锁: 又叫做写锁. 当用户要进行数据的写入时,对数据加上排他锁.排他锁只可以加一个,他和其他的排他锁,共享锁都相斥.

用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的.
一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以.
锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁. 他们的加锁开销从大大小,并发能力也是从大到小.

8.在MySQL中,事务提交与回滚

在MySQL中,默认情况下,事务是自动提交的,也就是说,只要执行一条DML语句就开启了事物,并且提交了事务

9.隔离级别与一致性关系

在这里插入图片描述

10.设置事务隔离级别

  • 方式一
    可以在my.ini文件中使用transaction-isolation选项来设置服务器的缺省事务隔离级别。

该选项值可以是:

  • READ-UNCOMMITTED
  • READ-COMMITTED
  • REPEATABLE-READ
  • SERIALIZABLE
    • 例如:
[mysqld]
transaction-isolation = READ-COMMITTED
  • 方式二
    通过命令动态设置隔离级别
    • 隔离级别也可以在运行的服务器中动态设置,应使用SET TRANSACTION ISOLATION LEVEL语句。
    • 其语法模式为:

    SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL

其中的可以是:

  • READ-UNCOMMITTED
  • READ-COMMITTED
  • REPEATABLE-READ
  • SERIALIZABLE
    • 例如: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

11、隔离级别的作用范围

  • 事务隔离级别的作用范围分为两种:
    • 全局级:对所有的会话有效
    • 会话级:只对当前的会话有效

    例如,设置会话级隔离级别为READ COMMITTED :
    mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

    设置全局级隔离级别为READ COMMITTED :
    mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

查看隔离级别

  • 事务隔离级别的作用范围分为两种:
    • 全局级:对所有的会话有效
    • 会话级:只对当前的会话有效
      • 例如,设置会话级隔离级别为READ COMMITTED :

    mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    或:
    mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    • 设置全局级隔离级别为READ COMMITTED :
    mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

相关文章

1.深入学习MySQL事务:ACID特性的实现原理

2.一文讲清楚MySQL事务隔离级别和实现原理,开发人员必备知识点

表结构设计

题目

1. 为什么要尽量设定一个主键?

主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全.

2. 主键使用自增ID还是UUID?

推荐使用自增ID,不要使用UUID.
因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降.
总之,在数据量大一些的情况下,用自增主键性能会好一些.

  • 图片来源于《高性能MySQL》: 其中默认后缀为使用自增ID,_uuid为使用UUID为主键的测试,测试了插入100w行和300w行的性能.
    在这里插入图片描述
    关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键.

    If you define a PRIMARY KEY on your table, InnoDB uses it as the
    clustered index. If you do not define a PRIMARY KEY for your table,
    MySQL picks the first UNIQUE index that has only NOT NULL columns as
    the primary key and InnoDB uses it as the clustered index.

3. 字段为什么要求定义为not null?

MySQL官网这样介绍:

NULL columns require additional space in the rowto record whether
their values are NULL. For MyISAM tables, each NULL columntakes one
bit extra, rounded up to the nearest byte.

null值会占用更多的字节,且会在程序中造成很多与预期不符的情况.

4. 如果要存储用户的密码散列,应该使用什么字段进行存储?

密码散列,盐,用户身份证号等固定长度的字符串应该 > 使用char而不是varchar来存储,这样可以节省空间且提高检索效率.

相关文章

1.MySQL数据库表结构设计优化技巧总结

2.mysql设计表结构数据类型的选择

存储引擎相关

题目

1. MySQL支持哪些存储引擎?

MySQL支持多种存储引擎,比如InnoDB,MyISAM,Memory,Archive等等.在大多数的情况下,直接选择使用InnoDB引擎都是最合适的,InnoDB也是MySQL的默认存储引擎.

2.InnoDB和MyISAM有什么区别?

  • InnoDB支持事物,而MyISAM不支持事物
  • InnoDB支持行级锁,而MyISAM支持表级锁
  • InnoDB支持MVCC, 而MyISAM不支持
  • InnoDB支持外键,而MyISAM不支持
  • InnoDB不支持全文索引,而MyISAM支持。

相关文章

1.老哥:说实话,不深入了解MySQL存储原理,还真回答不了这几个问题。

2.mysql底层存储原理

3.磁盘读取数据原理(数据库查询性能分析)

零散问题

题目

1. MySQL中的varchar和char有什么区别.

char是一个定长字段 ,假如申请了char(10)的空间,那么无论实际存储多少内容.该字段都占用10个字符,
varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1, 最后一个字符存储使用了多长的空间.
在检索效率上来讲,char > varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则应该尽量使用varchar.例如存储用户MD5加密后的密码,则应该使用char.

2. varchar(10)和int(10)代表什么含义?

varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示.

3. MySQL的binlog有有几种录入格式?分别有什么区别?

有三种格式,statement,row和mixed.

statement模式下,记录单元为语句.即每一个sql造成的影响会记录.由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制.
row级别下,记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter
table),因此这种模式的文件保存的信息太多,日志量太大. mixed.
一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row.

此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录.

4. 超大分页怎么处理?

超大的分页一般从两个方向上来解决.

数据库层面,这也是我们主要集中关注的(虽然收效没那么大)
类似 select * from table where age > 20 limit 1000000,10 这种查询其实也是有可以优化的余地的.
这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为
select * from table> where id in (select id from table where age > 20 limit 1000000,10). 这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快.

同时如果ID连续的好,我们还可以 select * from table where id > 1000000 limit> 10. 效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.
从需求的角度减少这种请求…主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击.

解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.
在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.

5. 关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们.
慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

所以优化也是针对这三个方向来的,

  • 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写.

  • 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引.

  • 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表.

6. 上面提到横向分表和纵向分表,可以分别举一个适合他们的例子吗?

横向分表是按行分表.假设我们有一张用户表,主键是自增ID且同时是用户的ID.数据量较大,有1亿多条,那么此时放在一张表里的查询效果就不太理想.我们可以根据主键ID进行分表,无论是按尾号分,或者按ID的区间分都是可以的.
假设按照尾号0-99分为100个表,那么每张表中的数据就仅有100w.这时的查询效率无疑是可以满足要求的.

纵向分表是按列分表.假设我们现在有一张文章表.包含字段id-摘要-内容.而系统中的展示形式是刷新出一个列表,列表中仅包含标题和摘要,当用户点击某篇文章进入详情时才需要正文内容.此时,如果数据量大,将内容这个很大且不经常使用的列放在一起会拖慢原表的查询速度.我们可以将上面的表分为两张.id-摘要,id-内容.当用户点击详情,那主键再来取一次内容即可.而增加的存储量只是很小的主键字段.代价很小.
当然,分表其实和业务的关联度很高,在分表之前一定要做好调研以及benchmark.不要按照自己的猜想盲目操作.

7. 什么是存储过程?有哪些优缺点?

存储过程是一些预编译的SQL语句。

  • 1、更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。
  • 2、存储过程是一个预编译的代码块,执行效率比较高,一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率,可以一定程度上确保数据安全

    但是,在互联网项目中,其实是不太推荐存储过程的,比较出名的就是阿里的《Java开发手册》中禁止使用存储过程,我个人的理解是,在互联网项目中,迭代太快,项目的生命周期也比较短,人员流动相比于传统的项目也更加频繁,在这样的情况下,存储过程的管理确实是没有那么方便,同时,复用性也没有写在服务层那么好.

8. 说一说三个范式

  • 第一范式: 每个列都不可以再拆分.
  • 第二范式: 非主键列完全依赖于主键,而不能是依赖于主键的一部分.
  • 第三范式: 非主键列只依赖于主键,不依赖于其他非主键.

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由.比如性能. 事实上我们经常会为了性能而妥协数据库的设计.
9. MyBatis中的#和$有什么区别?

乱入了一个奇怪的问题…我只是想单独记录一下这个问题,因为出现频率太高了.
#会将传入的内容当做字符串,而$会直接将传入值拼接在sql语句中.
所以#可以在一定程度上预防sql注入攻击.

题目

1 什么是锁

  • 1.1 锁的概述

在生活中锁的例子多的不能再多了,从古老的简单的门锁,到密码锁,再到现在的指纹解锁,人脸识别锁,这都是锁的鲜明的例子,所以,我们理解锁应该是非常简单的。

再到MySQL中的锁,对于MySQL来说,锁是一个很重要的特性,数据库的锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性,这样才能保证在高并发的情况下,访问数据库的时候,数据不会出现问题。

  • 1.2 锁的两个概念

在数据库中,lock和latch都可以称为锁,但是意义却不同。

Latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差,在InnoDB引擎中,Latch又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。

Lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。

2 讲一讲InnoDB存储引擎中的锁

2.1 锁的粒度

在数据库中,锁的粒度的不同可以分为表锁、页锁、行锁,这些锁的粒度之间也是会发生升级的,锁升级的意思就是讲当前锁的粒度降低,数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁,下面分别介绍一下这三种锁的粒度

  • 表锁

表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。

当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。
使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
特点: 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

  • 页锁

页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源
颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。
使用页级锁定的主要是BerkeleyDB存储引擎。
特点: 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

  • 行锁

行级锁定最大的特点就是锁定对象的粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。

虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。

特点: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

比较表锁我们可以发现,这两种锁的特点基本都是相反的,而从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
>

2.2 锁的类型

InnoDB存储引擎中存在着不同类型的锁,下面一一介绍一下。

S or X (共享锁、排他锁)

数据的操作其实只有两种,也就是读和写,而数据库在实现锁时,也会对这两种操作使用不同的锁;> InnoDB 实现了标准的行级锁,也就是共享锁(Shared Lock)和互斥锁(Exclusive Lock)。

  • 共享锁(读锁)(S Lock),允许事务读一行数据。
  • 排他锁(写锁)(X Lock),允许事务删除或更新一行数据。
    IS or IX (共享、排他)意向锁

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB存储引擎支持一种额外的锁方式,就称为意向锁,意向锁在 InnoDB 中是表级锁,意向锁分为:

  • 意向共享锁:表达一个事务想要获取一张表中某几行的共享锁。
  • 意向排他锁:表达一个事务想要获取一张表中某几行的排他锁。
    另外,这些锁之间的并不是一定可以共存的,有些锁之间是不兼容的,所谓兼容性就是指事务 A 获得一个某行某种锁之后,事务 B 同样的在这个行上尝试获取某种锁,如果能立即获取,则称锁兼容,反之叫冲突。

下面我们再看一下这两种锁的兼容性。
在这里插入图片描述
S or X (共享锁、排他锁)的兼容性
在这里插入图片描述
总结 在这里插入图片描述

3.说说一致性非锁定读和一致性锁定读?

  • 一致性锁定读(Locking Reads)
    在一个事务中查询数据时,普通的SELECT语句不会对查询的数据进行加锁,其他事务仍可以对查询的数据执行更新和删除操作。因此,InnoDB提供了两种类型的锁定读来保证额外的安全性:

    • SELECT … LOCK IN SHARE MODE

      对读取的行添加S锁,其他事物可以对这些行添加S锁,若添加X锁,则会被阻塞。

    • SELECT … FOR UPDATE

      会对查询的行及相关联的索引记录加X锁,其他事务请求的S锁或X锁都会被阻塞。 当事务提交或回滚后,通过这两个语句添加的锁都会被释放。 注意:只有在自动提交被禁用时,SELECT FOR UPDATE才可以锁定行,若开启自动提交,则匹配的行不会被锁定。

  • 一致性非锁定读
    一致性非锁定读(consistent nonlocking read)

    是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB会去读取行的一个快照。所以,非锁定读机制大大提高了数据库的并发性。

在这里插入图片描述

一致性非锁定读是InnoDB默认的读取方式,即读取不会占用和等待行上的锁。 > 在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB使用一致性非锁定读。

然而,对于快照数据的定义却不同。在READ COMMITTED事务隔离级别下,一致性非锁定读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别下,则读取事务开始时的行数据版本。

4.锁的优化策略

  • 读写分离
  • 分段加锁
  • 减少锁持有的时间
  • 多个线程尽量以相同的顺序去获取资源

不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。

6.什么是间隙锁GPA?

当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”。InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。
值得注意的是:间隙锁只会在Repeatable read隔离级别下使用

例子:假如emp表中只有101条记录,其empid的值分别是1,2,,100,101

Select * from emp where empid > 100 for update;
上面是一个范围查询,InnoDB不仅会对符合条件的em

pid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。

InnoDB使用间隙锁的目的:

为了防止幻读:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读。

7.谈谈你对Myql死锁的理解

并发的问题就少不了死锁,在MySQL中同样会存在死锁的问题。

但一般来说MySQL通过回滚帮我们解决了不少死锁的问题了,但死锁是无法完全避免的,可以通过以下的经验参考,来尽可能少遇到死锁:

  • (1)以固定的顺序访问表和行。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁。
  • (2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  • (3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  • (4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  • (5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

8.简单的 select 查询,如:select * from xttblog where id = 1; 是属于快照读吗?

简单的 select 操作,属于快照读,不加锁。

9.属于当前读的常见 SQL 语句有哪些?

select * from xttblog where ? lock in share mode;
select * from xttblog where ? for update;
insert into xttblog values ();
update xttblog set ? where ?;
delete from xttblog where ?;

常见的上面 5 种 SQL 都是属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加 S 锁 (共享锁)外,其他的操作,都加的是 X 锁 (排它锁)。

10、如果 id 是主键,且是 RC 隔离级别,SQL:delete from xttblog where id = 10 会加锁吗?

会加 X 锁,排它锁。只需要将主键上,id = 10 的记录加上 X 锁即可。

11. 问题 3 中的 id 主键改为唯一索引后,SQL:delete from xttblog where id = 10 会加锁吗?

若 id 列是 unique 列,其上有 unique 索引。那么 SQL 需要加两个 X 锁,一个对应于 id unique 索引上的
id = 10 的记录,另一把锁对应于聚簇索引上的相对于 id unique 对应的记录。

12.主键索引和唯一索引的区别?

主要区别总结如下:主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。唯一性索引列允许空值,而主键列不允许为空值。主键列在创建时,已经默认为空值+唯一索引了。主键可以被其他表引用为外键,而唯一索引不能。一个表最多只能创建一个主键,但可以创建多个唯一索引。主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
在RBO 模式下,主键的执行计划优先级要高于唯一索引。 两者可以提高查询的速度。

13.唯一性太差的字段不宜建立索引的原因是什么?

因为mysql首先会将索引中的键值取出来与内存中存储表数据的页中的数据相比较,但是数据页中的数据的顺序和索引队列中键值的顺序并不是一致的

假如索引中的键值a 先在数据页x中找到了符合的数据,然后又在数据页 y 中找到了符合条件的数据,这时 mysql 便会把数据页 x 销毁掉,把数据页 Y读到内存中。如果这时候还有键值 b,然后键值 b 找的数据又在数据页 x 上,则 mysql 又要把数据页 x读到内存中。也就是说从索引去寻找对应的表数据的时候是随机访问的。(实际情况应该是内存中缓存了好几页的数据,应该不只一页,但是这里假定线程内存中只存在一张页表)。这样的随机访问所造成的
io 消耗是比全表扫描的 io 消耗来得大的,还不如遍历整张表。

举个极端的例子。表 xttblog(ID,col1,col2,col3,col4,…col100) 共 100 个字段,现在 COL4
上创建索引,而 COL4 中所有的值都为 1,update a set col4 = 1,10000 条记录,COL4 都是 1。如果你查询
select * from xttblog where col4 = 1; MYSQL 就不会再去走索引。 因为如果走索引反而速度慢。
MYSQL 会自行判断是否需要使用索引。这也是为什么经常会看到 EXPLAIN 中明明有索引,但并未被使用。同样,即使 update xttblog set col4 = 0 where id = 10,这样, 仅 ID = 10的记录 COL4 = 0,而其余 9999 条记录仍是 COL4 = 1。 同样 select * from A where col4=1; 如果此时去走索引开销同样 比不走索引要大。

综上,唯一性太差的字段不宜建立索引。

14.Mysql并发读写的时候,都是需要加锁的么?

答案是不一定 事实上,Mysql的很多数据库引擎为了提升并发性能,都做了多版本并发控制,也就是我们常说的MVCC,事实上,除了Mysql,其他知名的关系型数据库,例如Oracle,PostgreSql也实现了多版本并发控制,尽管实现方式各不相同,但是他们的本质为了实现非阻塞读,也就是即便是这一行数据在做变更的时候,也能被读取到。
那么,Mysql是如何实现MVCC的呢?在Mysql的每一行数据中,
我们定义的数据列,还有2个隐藏的列,一个是数据的变更时间,一个是这行数据的删除时间,当然,这个时间并不是简单的时间戳,而是一个严格递增的系统版本号。

  • 当InnoDB发生Insert事件的时候,会插入当前行并且以取得的系统版本号作为数据版本号。

  • 当InnoDB发生Delete时间的时候,不会删除当前行,而是把对应的行如果未删除,那么打上删除标记位为当前的版本号。

  • 当InnoDB发生Select操作的时候,会取当前的系统版本号,然后到数据库中进行查询,他只会查询比自己当前版本号更小的,并且没有删除版本号或者删除版本号比当前版本号更小的数据。

  • 当InnoDB发生Update事件的时候,不是直接更新旧的数据,而是插入一条新的数据,并且把版本号小于这条记录的并且没有被打上删除标记的同一主键的记录更新打上删除标记,删除版本号为当前的版本号。

    很显然,在这样的一种数据中,同一行数据其实在数据库中是多行的存在。这本质上是一种空间换时间的方案,在多版本控制中,我们几乎可以做到所有的读操作都是无阻塞的,可以避免加锁,这与互联网业务中,多读少写是非常契合的。当然了,在Mysql的InnoDB引擎中,只有事务级别为可重复读跟读提交才可以使用。这是为什么呢?

    数据库(Database)是按照 数据结构来组织、 存储和管理数据的建立在计算机存储设备上的仓库。

    简单来说是本身可视为 电子化的文件柜——存储电子 文件的处所,用户可以对文件中的数据进行新增、截取、更新、删除等操作。

相关链接

1.MySQL死锁系列-常见加锁场景分析

2.了解下Mysql的间隙锁及产生的原因

❤其他Mysql经典文章系列

1.我必须得告诉大家的MySQL优化原理

2.MySQL优化/面试,看这一篇就够了

3.面试官问我知不知道 MySQL 的锁,接下来的 15 分钟让他刮目相看

4.蘑菇街大佬谈MySQL技术精粹:如何快速成为MySQL数据库顶尖高手

你不得不必须掌握的30种SQL语句优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值