10 MySQL面试题汇总

Java全栈面试题汇总目录icon-default.png?t=N7T8https://blog.youkuaiyun.com/weixin_42173947/article/details/138966376

目录

1 数据库三大范式是什么?

2 MySQL有关权限的表都有哪几个?

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

4 MySQL有哪些数据类型?

5 MySQL存储引擎MyISAM与InnoDB区别?

6 MyISAM索引与InnoDB索引的区别?

7 存储引擎选择?

8 什么是索引?

9 索引有哪些优缺点?

10 索引使用场景?

11 索引有哪几种类型?

12 索引的数据结构(B树,hash)?

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

14 索引的基本原理?

15 创建索引的原则?

16 创建索引时需要注意什么?

17 前缀索引?

18 什么是最左前缀原则,什么是最左匹配原则?

19 B树和B+树的区别?

20 使用B树的好处?

21 使用B+树的好处?

22 数据库为什么使用B+树而不是B树?

23 什么是聚簇索引,何时使用聚簇索引与非聚簇索引?

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

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

26 什么是数据库事务?

27 事务的四大特性(ACID)介绍一下?

28 什么是脏读,幻读,不可重复读?

29 什么是事务的隔离级别,MySQL的默认隔离级别是什么?

30 对MySQL的锁了解吗?

31 隔离级别与锁的关系?

32 按照锁的粒度分数据库锁有哪些,锁机制与InnoDB锁算法?

33 从锁的类别上分MySQL都有哪些锁和锁的粒度呢?

34 MySQL中InnoDB引擎的行锁是怎么实现的?

35 InnoDB存储引擎的锁的算法?

36 InnoDB引擎的4大特性?

37 什么是死锁,怎么解决?

38 数据库的乐观锁和悲观锁是什么,怎么实现的?

39 为什么要使用视图,什么是视图?

40 视图有哪些特点?

41 视图的用途和使用场景有哪些?

42 视图的优点?

43 视图的缺点?

44 什么是游标?

45 什么是存储过程,有哪些优缺点?

46 什么是触发器?触发器的使用场景有哪些?

47 MySQL中都有哪些触发器?

48 SQL语句主要分为哪几类?

49 超键、候选键、主键、外键分别是什么?

50 SQL约束有哪几种?

51 六种关联查询?

52 什么是子查询?

53 子查询的三种情况?

54 in和exists区别?

55 drop、delete与truncate的区别?

56 如何定位及优化SQL语句的性能问题,创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

57 SQL的生命周期?

58 SQL语句的执行过程?

59 大表数据查询,怎么优化?

60 超大分页怎么处理?

61 MySQL分页?

62 慢查询日志?

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

64 主键使用自增ID还是UUID?

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

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

67 优化查询过程中的数据访问?

68 优化长难的查询语句?

69 优化count类型的查询语句?

70 优化关联查询?

71 优化子查询?

72 优化WHERE子句?

73 为什么要优化数据库?

74 数据库结构优化?

75 MySQL数据库CPU飙升到500%的话怎么处理?

76 大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?

77 分库分表后面临的问题?

78 MySQL的复制原理以及流程?

79 读写分离有哪些解决方案?

80 备份计划,mysqldump以及xtranbackup的实现原理?

81 数据表损坏的修复方式有哪些?

82 什么是MVCC?


1 数据库三大范式是什么?

第一范式:每个列都不可以再拆分。

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

2 MySQL有关权限的表都有哪几个?

  • user权限表:记录允许连接到服务器的用户账号信息,里面的权限是全局级的
  • db权限表:记录各个账号在各个数据库上的操作权限
  • table_priv权限表:记录数据表级的操作权限
  • columns_priv权限表:记录数据列级的操作权限
  • host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANTREVOKE语句的影响

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

  • statement模式下,每一条会修改数据的SQL都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于SQL的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制
  • row别下,不记录SQL语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大级
  • mixed,一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row

4 MySQL有哪些数据类型?

分类

类型名称

说明

整数

类型

tinyint

很小的整数(1字节)

smallint

小的整数(2字节)

mediumint

中等大小的整数(3字节)

int(integer)

普通大小的整数(4字节)

BIGINT

大的整数(8字节)

小数

类型

float

单精度浮点数

double

双精度浮点数

decimal(m, d)

压缩严格的定点数

日期

类型

year

YYYY 1901~2155

time

HH:MM:SS -838:59:59~838:59:59

date

YYYY-MM-DD 1000-01-01~9999-12-3

datetime

YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59

timestamp

YYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC

文本、

二进制

类型

CHAR(M)

M0~255之间的整数

VARCHAR(M)

M0~65535之间的整数

TINYBLOB

允许长度0~255字节

BLOB

允许长度0~65535字节

MEDIUMBLOB

允许长度0~167772150字节

LONGBLOB

允许长度0~4294967295字节

TINYTEXT

允许长度0~255字节

TEXT

允许长度0~65535字节

MEDIUMTEXT

允许长度0~167772150字节

LONGTEXT

允许长度0~4294967295字节

VARBINARY(M)

允许长度0~M个字节的变长字节字符串

BINARY(M)

允许长度0~M个字节的定长字节字符串

1. 整数类型,包括TINYINTSMALLINTMEDIUMINTINTBIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。

长度:整数类型可以被指定长度,例如:INT(11)表示长度为11INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。

例子:假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012

2. 实数类型,包括FLOATDOUBLEDECIMAL

DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。

FLOATDOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。

计算时FLOATDOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。

3. 字符串类型,包括VARCHARCHARTEXTBLOB

VARCHAR用于存储可变长字符串,它比定长类型更节省空间。

VARCHAR使用额外12个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。

VARCHAR存储的内容超出设置的长度时,内容会被截断。

CHAR是定长的,根据定义的字符串长度分配足够的空间。

CHAR会根据需要使用空格进行填充方便比较。

CHAR适合存储很短的字符串,或者所有值都接近同一个长度。

CHAR存储的内容超出设置的长度时,内容同样会被截断。

因为长度固定,所以CHAR存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法。

对于char来说,最多能存放的字符个数为255,和编码无关

varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法。

对于varchar来说,最多能存放的字符个数为65532

使用策略:

对于经常变更的数据来说,CHARVARCHAR更好,因为CHAR不容易产生碎片。

对于非常短的列,CHARVARCHAR在存储空间上更有效率。

使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。

尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。

4. 枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。

有时可以使用ENUM代替常用的字符串类型。

ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。

ENUM在内部存储时,其实存的是整数。

尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。

排序是按照内部存储的整数

5. 日期和时间类型,尽量使用timestamp,空间效率高于datetime。

用整数保存时间戳通常不方便处理。

如果需要存储微妙,可以使用bigint存储。

5 MySQL存储引擎MyISAM与InnoDB区别?

存储引擎Storage engineMySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。

  • InnoDB引擎:InnoDB引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
  • MyISAM引擎(原本MySQL的默认引擎):不提供事务的支持,也不支持行级锁和外键。
  • MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

MyISAMInnoDB区别

MyISAM

InnoDB

存储结构

每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件

所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件)InnoDB表的大小只受限于操作系统文件的大小,一般为2GB

存储空间

MyISAM可被压缩,存储空间较小

InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引

可移植性、备份及恢复

由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作

免费的方案可以是拷贝数据文件、备份binlog,或者用mysqldump,在数据量达到几十G的时候就相对痛苦了

文件格式

数据和索引是分别存储的,数据.MYD,索引.MYI

数据和索引是集中存储的,.ibd

记录存储顺序

按记录插入顺序保存

按主键大小有序插入

外键

不支持

支持

事务

不支持

支持

锁支持

表级锁定

行级锁定、表级锁定,锁定力度小并发能力高

SELECT

MyISAM更优

INSERTUPDATEDELETE

InnoDB更优

select count(*)

MyISAM更快,因为MyISAM内部维护了一个计数器,可以直接调取。

索引的实现方式

B+树索引,MyISAM是堆表

B+树索引,InnoDB是索引组织表

哈希索引

不支持

支持

全文索引

支持

不支持

6 MyISAM索引与InnoDB索引的区别?

  • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引
  • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效
  • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据
  • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效

7 存储引擎选择?

如果没有特别的需求,使用默认的InnoDB即可。

MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。

InnoDB:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。

8 什么是索引?

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

9 索引有哪些优缺点?

  • 索引的优点
  1. 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  2. 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
  • 索引的缺点
  1. 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增//删的执行效率;
  2. 空间方面:索引需要占物理空间。

10 索引使用场景?

  • where
  • order by

当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。

但是如果我们对该字段建立索引alter table表名add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

  • join

join语句匹配关系(on)涉及的字段建立索引能够提高效率

  • 索引覆盖

如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。

这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。

  • 基数较小的类,索引效果较差,没有必要在此列建立索引
  • 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
  • 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可

11 索引有哪几种类型?

  • 主键索引:数据列不允许重复,不允许为NULL,一个表只能有一个主键。
  • 唯一索引:数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

可以通过ALTER TABLE table_name ADD UNIQUE(column);创建唯一索引

可以通过ALTER TABLE table_name ADD UNIQUE(column1, column2);创建唯一组合索引

  • 普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值。

可以通过ALTER TABLE table_name ADD INDEX index_name(column);创建普通索引

可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

  • 全文索引:是目前搜索引擎使用的一种关键技术。

可以通过ALTER TABLE table_name ADD FULLTEXT(column);创建全文索引

12 索引的数据结构(B树,hash)?

索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTREE索引。

1. B树索引

MySQL通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B)索引和HASH索引。B树索引是MySQL数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTREE索引。通常我们说的索引不出意外指的就是(B)索引(实际是用B+树实现的,因为在查看表索引时,MySQL一律打印BTREE,所以简称为B树索引)

查询方式:

主键索引区:PI(关联保存的时数据的地址)按主键查询,

普通索引区:SI(关联的id的地址,然后再到达上面的地址)。所以按主键查询,速度最快

B+树性质:

  1. n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
  2. 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  3. 所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
  4. B+树中,数据对象的插入和删除仅在叶节点上进行。
  5. B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。

2. 哈希索引

简要说下,类似于数据结构中简单实现的HASH(散列表)一样,当我们在MySQL中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。

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

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

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

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

  • hash索引不支持使用索引进行排序,原理同上。
  • hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。AAAAAAAAB的索引没有相关性。
  • hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
  • hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。

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

14 索引的基本原理?

索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

索引的原理很简单,就是把无序的数据变成有序的查询。

把创建了索引的列的内容进行排序,对排序结果生成倒排表,在倒排表内容上拼上数据地址链,在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据。

15 创建索引的原则?

  1. 最左前缀匹配原则,组合索引非常重要的原则,MySQL会一直向右匹配直到遇到范围查询(><betweenlike)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a, b, c, d)顺序的索引,d是用不到索引的,如果建立(a, b, d, c)的索引则都可以用到,a, b, d的顺序可以任意调整。
  2. 较频繁作为查询条件的字段才去创建索引
  3. 更新频繁字段不适合创建索引
  4. 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
  5. 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a, b)的索引,那么只需要修改原来的索引即可。
  6. 定义有外键的数据列一定要建立索引。
  7. 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
  8. 对于定义为textimagebit的数据类型的列不要建立索引。

16 创建索引时需要注意什么?

  • 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值
  • 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高
  • 索引字段越小越好:数据库的数据存储以页为单位,一页存储的数据越多,一次IO操作获取的数据越大效率越高

17 前缀索引?

语法:index(field(10)),使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。

前提:前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。

实操的难度:在于前缀截取的长度。

我们可以利用select count(*)/count(distinct left(password, prefixLen));,通过从调整prefixLen的值(1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen个字符几乎能确定唯一一条记录)

18 什么是最左前缀原则,什么是最左匹配原则?

顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。

最左前缀匹配原则,非常重要的原则,MySQL会一直向右匹配直到遇到范围查询(><betweenlike)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a, b, c, d)顺序的索引,d是用不到索引的,如果建立(a, b, d, c)的索引则都可以用到,a, b, d的顺序可以任意调整。

= in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a, b, c)索引可以任意顺序,MySQL的查询优化器会帮你优化成索引可以识别的形式

19 B树和B+树的区别?

  1. B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
  2. B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

20 使用B树的好处?

B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。

21 使用B+树的好处?

由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

22 数据库为什么使用B+树而不是B树?

  • B树只适合随机检索,而B+树同时支持随机检索和顺序检索
  • B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素
  • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当
  • B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作
  • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率
  • B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据。在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引。在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引
  • 当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询

23 什么是聚簇索引,何时使用聚簇索引与非聚簇索引?

  • 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
  • 非聚簇索引:将数据存储与索引分开结构,索引结构的叶子节点指向了数据的对应行
  • MyISAM通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因
  • InnoDB中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值

何时使用聚簇索引与非聚簇索引

动作

使用聚簇索引

使用非聚簇索引

列经常被分组排序

返回某范围内的数据

不应

一个或极少不同值

不应

不应

小数目的不同值

不应

大数目的不同值

不应

频繁更新的列

不应

外键列

主键列

频繁修改索引列

不应

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

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

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

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

MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

具体原因为:

MySQL使用索引时需要索引有序,假设现在建立了"nameageschool"的联合索引,那么索引的排序为:先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

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

26 什么是数据库事务?

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

27 事务的四大特性(ACID)介绍一下?

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

28 什么是脏读,幻读,不可重复读?

  • 脏读(Dirty Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

29 什么是事务的隔离级别,MySQL的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommittedRead committedRepeatable readSerializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

隔离级别

脏读

不可重复读

幻读

READ-UNCOMMITTED

READ-COMMITTED

×

REPEATABLE-READ

×

×

SERIALIZABLE

×

×

×

SQL标准定义了四个隔离级别:

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

MySQL默认采用的REPEATABLE_READ隔离级别Oracle默认采用的READ_COMMITTED隔离级别

事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容),但是InnoDB存储引擎默认使用REPEATABLE-READ(可重读)并不会有任何性能损失。InnoDB存储引擎在分布式事务的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。

30 对MySQL的锁了解吗?

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

31 隔离级别与锁的关系?

Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁

Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁

SERIALIZABLE是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成

32 按照锁的粒度分数据库锁有哪些,锁机制与InnoDB锁算法?

在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎)MyISAM采用表级锁(table-level locking)InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

行级锁,表级锁和页级锁对比

类型

说明

特点

行级锁

行级锁是MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。

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

表级锁

表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAMINNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)

开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁

页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

33 从锁的类别上分MySQL都有哪些锁和锁的粒度呢?

从锁的类别上来讲,有共享锁和排他锁。

共享锁:又叫做读锁。当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。

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

锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁。

他们的加锁开销从大到小,并发能力也是从大到小。

34 MySQL中InnoDB引擎的行锁是怎么实现的?

InnoDB是基于索引来完成行锁

例:select * from tab_with_index where id = 1 for update;

for update可以根据条件来完成行锁锁定,并且id是有索引键的列,如果id不是索引键那么InnoDB将完成表锁,并发将无从谈起

35 InnoDB存储引擎的锁的算法?

  • Record lock:单个行记录上的锁
  • Gap lock:间隙锁,锁定一个范围,不包括记录本身
  • Next-key lockrecord + gap锁定一个范围,包含记录本身

相关知识点:

1.  InnoDB对于行的查询使用next-key lock

2.  Next-locking keying为了解决Phantom Problem幻读问题

3.  当查询的索引含有唯一属性时,将next-key lock降级为record key

4.  Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生

5.  有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock)

  1. 将事务隔离级别设置为RC
  2. 将参数innodb_locks_unsafe_for_binlog设置为1

36 InnoDB引擎的4大特性?

1. 插入缓冲

插入缓冲(Insert Buffer/Change Buffer):提升插入性能,change bufferinginsert buffer的加强,insert buffer只针对insert有效,change bufferinginsertdeleteupdate(delete + insert)purge都有效

只对于非聚集索引(非唯一)的插入和更新有效,对于每一次的插入不是写到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,如果在则直接插入;若不在,则先放到Insert Buffer中,再按照一定的频率进行合并操作,再写回disk。这样通常能将多个插入合并到一个操作中,目的还是为了减少随机IO带来性能损耗。

使用插入缓冲的条件:

*非聚集索引

*非唯一索引

Change buffer是作为buffer pool中的一部分存在。innodb_change_buffering参数缓存所对应的操作:(update会被认为是delete + insert)

innodb_change_buffering,设置的值有:insertsdeletespurgeschanges(insertsdeletes)all(默认)none

all:默认值,缓存insertdeletepurges操作

none:不缓存

inserts:缓存insert操作

deletes:缓存delete操作

changes:缓存insertdelete操作

purges:缓存后台执行的物理删除操作

可以通过参数控制其使用的大小:

innodb_change_buffer_max_size,默认是25%,即缓冲池的1/4。最大可设置为50%。当MySQL实例中有大量的修改操作时,要考虑增大innodb_change_buffer_max_size

上面提过在一定频率下进行合并,那所谓的频率是什么条件?

1) 辅助索引页被读取到缓冲池中。正常的select先检查Insert Buffer是否有该非聚集索引页存在,若有则合并插入

2) 辅助索引页没有可用空间。空间小于1/32页的大小,则会强制合并操作

3) Master Thread每秒和每10秒的合并操作

2. 二次写

doublewrite缓存是位于系统表空间的存储区域,用来缓存InnoDB的数据页从InnoDB buffer poolflush之后并写入到数据文件之前,所以当操作系统或者数据库进程在数据页写磁盘的过程中崩溃,InnoDB可以在doublewrite缓存中找到数据页的备份而用来执行crash恢复。数据页写入到doublewrite缓存的动作所需要的IO消耗要小于写入到数据文件的消耗,因为此写入操作会以一次大的连续块的方式写入

在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write

doublewrite组成:

内存中的doublewrite buffer,大小2M

物理磁盘上共享表空间中连续的128个页,即2个区(extend),大小同样为2M

对缓冲池的脏页进行刷新时,不是直接写磁盘,而是会通过memcpy()函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite再分两次,每次1M顺序地写入共享表空间的物理磁盘上,在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,InnoDB可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。

3. 自适应哈希索引

Adaptive Hash index属性使得InnoDB更像是内存数据库。该属性通过innodb_adapitve_hash_index开启,也可以通过—skip-innodb_adaptive_hash_index参数关闭

注:可以关闭自适应hash

生成hash索引的条件比较苛刻

1) 索引是否被访问了17

2) 索引中的某个页已经被访问了100

3) 访问模式必须是一样的。

例如对于(a, b)访问模式情况:

where a = xxx

where a = xxx and b = xxx

InnoDB存储引擎会监控对表上二级索引的查找,如果发现某二级索引被频繁访问,二级索引成为热数据,建立哈希索引可以带来速度的提升

经常访问的二级索引数据会自动被生成到hash索引里面去(最近连续被访问三次的数据),自适应哈希索引通过缓冲池的B+树构造而来,因此建立的速度很快。

哈希(hash)是一种非常快的等值查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般3-4层,故需要3-4次的查询。

InnoDB会监控对表上个索引页的查询。如果观察到建立哈希索引可以带来速度提升,则自动建立哈希索引,称之为自适应哈希索引(Adaptive Hash IndexAHI)

特点:

1) 无序,没有树高

2) 降低对二级索引树的频繁访问资源,索引树高<=4,访问索引:访问树、根节点、叶子节点

3) 自适应

缺陷:

1) hash自适应索引会占用InnoDB buffer pool

2) 自适应hash索引只适合搜索等值的查询,如select * from table where index_col='xxx',而对于其他查找类型,如范围查找,是不能使用的

3) 极端情况下,自适应hash索引才有比较大的意义,可以降低逻辑读

4. 预读(read ahead)

InnoDB使用两种预读算法来提高I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)

为了区分这两种预读的方式,我们可以把线性预读放到以extent为单位,而随机预读放到以extent中的page为单位。线性预读着眼于将下一个extent提前读取到buffer pool中,而随机预读着眼于将当前extent中的剩余的page提前读取到buffer pool中。

线性预读(linear read-ahead)

方式有一个很重要的变量控制是否将下一个extent预读到buffer pool中,通过使用配置参数innodb_read_ahead_threshold,可以控制InnoDB执行预读操作的时间。如果一个extent中的被顺序读取的page超过或者等于该参数变量时,InnoDB将会异步的将下一个extent读取到buffer pool中,innodb_read_ahead_threshold可以设置为0-64的任何值,默认值为56,值越高,访问模式检查越严格。

例如,如果将值设置为48,则InnoDB只有在顺序访问当前extent中的48pages时才触发线性预读请求,将下一个extent读到内存中。如果值为8InnoDB触发异步预读,即使程序段中只有8页被顺序访问。你可以在MySQL配置文件中设置此参数的值,或者使用SET GLOBAL需要该SUPER权限的命令动态更改该参数。

在没有该变量之前,当访问到extent的最后一个page的时候,InnoDB会决定是否将下一个extent放入到buffer pool中。

随机预读(randomread-ahead)

随机预读方式则是表示当同一个extent中的一些pagebuffer pool中发现时,InnoDB会将该extent中的剩余page一并读到buffer pool中,由于随机预读方式给InnoDB code带来了一些不必要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃。要启用此功能,请将配置变量设置innodb_random_read_aheadON

37 什么是死锁,怎么解决?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

常见的解决死锁的方法

  1. 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会
  2. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率
  3. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率
  4. 如果业务处理不好可以用分布式事务锁或者使用乐观锁

38 数据库的乐观锁和悲观锁是什么,怎么实现的?

数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version的方式来进行锁定。实现方式:乐一般会使用版本号机制或CAS算法实现。

两种锁的使用场景

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

39 为什么要使用视图,什么是视图?

为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。

视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

40 视图有哪些特点?

  • 视图的列可以来自不同的表,是表的抽象和在逻辑意义上建立的新关系
  • 视图是由基本表(实表)产生的表(虚表)
  • 视图的建立和删除不影响基本表
  • 对视图内容的更新(添加,删除和修改)直接影响基本表
  • 当视图来自多个基本表时,不允许添加和删除数据
  • 视图的操作包括创建视图,查看视图,删除视图和修改视图

41 视图的用途和使用场景有哪些?

视图根本用途:简化SQL查询,提高开发效率。如果说还有另外一个用途那就是兼容老的表结构。

下面是视图的常见使用场景:

  • 重用SQL语句
  • 简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节
  • 使用表的组成部分而不是整个表
  • 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据

42 视图的优点?

  1. 查询简单化。视图能简化用户的操作
  2. 数据安全性。视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
  3. 逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性

43 视图的缺点?

  1. 性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
  2. 修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的。这些视图有如下特征:
  1. UNIQUE等集合操作符的视图
  2. GROUP BY子句的视图
  3. 有诸如AVG\SUM\MAX等聚合函数的视图
  4. 使用DISTINCT关键字的视图
  5. 连接表的视图(其中有些例外)

44 什么是游标?

游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理。

45 什么是存储过程,有哪些优缺点?

存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

优点:

  1. 存储过程是预编译过的,执行效率高。
  2. 存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
  3. 安全性高,执行存储过程需要有一定权限的用户。
  4. 存储过程可以重复使用,减少数据库开发人员的工作量。

缺点:

  1. 调试麻烦。
  2. 移植问题,数据库端代码当然是与数据库相关的。
  3. 重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译。
  4. 如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难、而且代价是空前的,维护起来更麻烦。

46 什么是触发器?触发器的使用场景有哪些?

触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。

使用场景:

  • 可以通过数据库中的相关表实现级联更改
  • 实时监控某张表中的某个字段的更改而需要做出相应的处理
  • 例如可以生成某些业务的编号
  • 注意不要滥用,否则会造成数据库及应用程序的维护困难

47 MySQL中都有哪些触发器?

MySQL数据库中有如下六种触发器:

  • Before Insert
  • After Insert
  • Before Update
  • After Update
  • Before Delete
  • After Delete

48 SQL语句主要分为哪几类?

数据定义语言DDL(Data Definition Language)CREATEDROPALTER

主要为以上操作即对逻辑结构等有操作的,其中包括表结构,视图和索引。

数据查询语言DQL(Data Query Language)SELECT

这个较为好理解即查询操作,以select关键字。各种简单查询,连接查询等都属于DQL

数据操纵语言DML(Data Manipulation Language)INSERTUPDATEDELETE

主要为以上操作即对数据进行操作的,对应上面所说的查询操作DQLDML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种被划分到DQL中。

数据控制语言DCL(Data Control Language)GRANTREVOKECOMMITROLLBACK

主要为以上操作即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

49 超键、候选键、主键、外键分别是什么?

  • 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键
  • 候选键:是最小超键,即没有冗余元素的超键
  • 主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)
  • 外键:在一个表中存在的另一个表的主键称此表的外键

50 SQL约束有哪几种?

  • NOT NULL:用于控制字段的内容一定不能为空(NULL)
  • UNIQUE:控件字段内容不能重复,一个表允许有多个Unique约束
  • PRIMARY KEY:也是用于控件字段内容不能重复,但它在一个表只允许出现一个
  • FOREIGN KEY:用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一
  • CHECK:用于控制字段的值范围

51 六种关联查询?

  • 交叉连接(CROSS JOIN)

生成笛卡尔积,结果是生成矩阵中元素的个数

  • 内连接(INNER JOIN)

内连接分为三类

等值连接:ON A.id = B.id

不等值连接:ON A.id > B.id

自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid

  • 外连接(LEFT JOIN/RIGHT JOIN)

左外连接:LEFT OUTER JOIN,以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN

右外连接:RIGHT OUTER JOIN,以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN

  • 联合查询(UNIONUNION ALL)

就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并

如果使用UNION ALL,不会合并重复的记录行

效率UNION ALL高于UNION

  • 全连接(FULL JOIN)

MySQL不支持全连接

可以使用LEFT JOINUNIONRIGHT JOIN联合使用

SELECT * FROM A LEFT JOIN B ON A.id = B.id UNION SELECT * FROM A RIGHT JOIN B ON A.id = B.id

52 什么是子查询?

  1. 条件:一条SQL语句的查询结果做为另一条查询语句的条件或查询结果
  2. 嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询

53 子查询的三种情况?

  1. 子查询是单行单列的情况:结果集是一个值,父查询使用:=<>等运算符
  2. 子查询是多行单列的情况:结果集类似于一个数组,父查询使用:in运算符
  3. 子查询是多行多列的情况:结果集类似于一张虚拟表,不能用于where条件,用于select子句中做为子表。

例:select * from dept d, (select * from employee where join_date > '2011-1-1') e where e.dept_id = d.id;

54 in和exists区别?

MySQL中的in语句是把外表和内表作hash连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。

  1. 如果查询的两个表大小相当,那么用inexists差别不大
  2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in
  3. not innot exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快

55 drop、delete与truncate的区别?

三者都表示删除,但是三者有一些差别:

Drop

Delete

Truncate

类型

属于DDL

属于DML

属于DDL

回滚

不可回滚

可回滚

不可回滚

删除内容

表结构不存在,删除表中的所有数据

表结构还在,删除表的全部或者一部分数据行

表结构还在,删除表中的所有数据

删除速度

删除速度快

删除速度慢,需要逐行删除

删除速度快

因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate

56 如何定位及优化SQL语句的性能问题,创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

对于低性能的SQL语句的定位,最重要也是最有效的方法就是使用执行计划,MySQL提供了explain命令来查看语句的执行计划。我们知道,不管是哪种数据库,或者是哪种数据库引擎,在对一条SQL语句进行执行的过程中都会做很多相关的优化,对于查询语句,最重要的优化方式就是使用索引。而执行计划,就是显示数据库引擎对于SQL语句的执行的详细情况,其中包含了是否使用索引,使用什么索引,使用的索引的相关信息等。

1. id

执行计划包含的信息id有一组数字组成。表示一个查询中各个子查询的执行顺序;

id相同执行顺序由上至下。

id不同,id值越大优先级越高,越先被执行。

id为null时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。

2. select_type

每个子查询的查询类型,一些常见的查询类型。

id

select_type

description

1

SIMPLE

不包含任何子查询或union等查询

2

PRIMARY

包含子查询最外层查询就显示为PRIMARY

3

SUBQUERY

在select或where字句中包含的查询

4

DERIVED

from字句中包含的查询

5

DEPENDENT SUBQUERY

在SELECT或WHERE列表中包含了子查询,子查询基于外层。

6

UNCACHEABLE SUBQUREY

当使用了@@来引用系统变量的时候,不会使用缓存

7

UNION

出现在union后的查询语句中

8

UNION RESULT

从UNION中获取结果集

3. table

查询的数据表,当从衍生表中查数据时会显示x表示对应的执行计划id partitions表分区、表创建的时候可以指定通过那个列进行表分区。

4. type(非常重要,可以看到有没有走索引)

访问类型

类型

解释

null

MySQL不访问任何表或索引,直接返回结果

system

const的特例,仅返回一条数据的时候。

const

查找主键索引,返回的数据至多一条(0或者1条)。属于精确查找

eq_ref

查找唯一性索引,返回的数据至多一条。属于精确查找

ref

查找非唯一性索引,返回匹配某一条件的多条数据。属于精确查找、数据返回可能是多条

range

查找某个索引的部分索引,一般在where子句中使用<、>、in、between等关键词。只检索给定范围的行,属于范围查找

index

查找所有的索引树,比ALL要快的多,因为索引文件要比数据文件小的多。

ALL

不使用任何索引,进行全表扫描,性能最差。

5. possible_keys

可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为NULL时就要考虑当前的SQL是否需要优化了。

6. key

显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中

7. key_length

索引长度。使用的索引越多,长度越长。

8. ref

表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

9. rows

返回估算的结果集数目,并不是一个准确的值。

10. extra

信息非常丰富,常见的有:

Using index使用覆盖索引

Using where使用了用where子句来过滤结果集

Using filesort使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。

Using temporary使用了临时表

57 SQL的生命周期?

  1. 应用服务器与数据库服务器建立一个连接
  2. 数据库进程拿到请求SQL
  3. 解析并生成执行计划,执行
  4. 读取数据到内存并进行逻辑处理
  5. 通过步骤一的连接,发送结果到客户端
  6. 关掉连接,释放资源

58 SQL语句的执行过程?

1. 客户端的数据库驱动与数据库连接池

(1) 客户端与数据库进行通信前,通过数据库驱动与MySQL建立连接,建立完成之后,就发送SQL语句

(2) 为了减少频繁创建和销毁连接造成系统性能的下降,通过数据库连接池维护一定数量的连接线程,当需要进行连接时,就直接从连接池中获取,使用完毕之后,再归还给连接池。常见的数据库连接池有Druid、C3P0、DBCP

2. MySQL架构的Server层的执行过程

(1) 连接器:主要负责跟客户端建立连接、获取权限、维持和管理连接

(2) 查询缓存:优先在缓存中进行查询,如果查到了则直接返回,如果缓存中查询不到,在去数据库中查询

MySQL缓存是默认关闭的,也就是说不推荐使用缓存,并且在MySQL8.0版本已经将查询缓存的整块功能删掉了。这主要是它的使用场景限制造成的:

先说下缓存中数据存储格式:key(SQL语句)-value(数据值),所以如果SQL语句(key)只要存在一点不同之处就会直接进行数据库查询了;

由于表中的数据不是一成不变的,大多数是经常变化的,而当数据库中的数据变化了,那么相应的与此表相关的缓存数据就需要移除掉;

(3) 解析器/分析器:分析器的工作主要是对要执行的SQL语句进行词法解析、语法解析,最终得到抽象语法树,然后再使用预处理器对抽象语法树进行语义校验,判断抽象语法树中的表是否存在,如果存在的话,在接着判断select投影列字段是否在表中存在等

(4) 优化器:主要将SQL经过词法解析、语法解析后得到的语法树,通过数据字典和统计信息的内容,再经过一系列运算,最终得出一个执行计划,包括选择使用哪个索引

(5) 在分析是否走索引查询时,是通过进行动态数据采样统计分析出来;只要是统计分析出来的,那就可能会存在分析错误的情况,所以在SQL执行不走索引时,也要考虑到这方面的因素

(6) 执行器:根据一系列的执行计划去调用存储引擎提供的API接口去调用操作数据,完成SQL的执行

3. InnoDB存储引擎的执行过程

(1) 首先MySQL执行器根据执行计划,调用存储引擎的API查询数据

(2) 存储引擎先从缓存池Buffer pool中查询数据,如果没有就会去磁盘中查询,如果查询到了就将其放到缓存池中

(3) 在数据加载到Buffer Pool的同时,会将这条数据的原始记录保存到undo日志文件中

(4) InnoDB会在Buffer Pool中执行更新操作

(5) 更新后的数据会记录在redo log buffer中

(6) 提交事务在提交的同时会做以下三件事

        (a) 将redo log buffer中的数据刷入到redo_log文件中

        (b) 将本次操作记录写入到bin_log文件中

        (c) 将bin_log文件名字和更新内容在bin_log中的位置记录到redo_log中,同时在redo_log 最后添加commit标记

(7) 使用一个后台线程,它会在某个时机将我们Buffer Pool中的更新后的数据刷到MySQL数据库中,这样就将内存和数据库的数据保持统一了

59 大表数据查询,怎么优化?

  1. 优化schema、SQL语句+索引
  2. 加缓存。memcached,redis
  3. 主从复制,读写分离
  4. 垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统
  5. 水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key,为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,SQL中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表

60 超大分页怎么处理?

  • 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于select * from table where age > 20 limit 1000000,10这种查询其实也是有可以优化的余地的。这条语句需要load 1000000数据然后基本上全部丢弃,只取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,下次查询时直接根据该ID来查询)以及防止ID泄漏且连续被人恶意攻击
  • 解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可

61 MySQL分页?

LIMIT子句可以被用于强制SELECT语句返回指定的记录数。LIMIT接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是0(而不是1)

mysql> SELECT * FROM table LIMIT 5,10;//检索记录行6到15

为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为-1:

mysql> SELECT * FROM table LIMIT 95,-1;//检索记录行96到最后一行

如果只给定一个参数,它表示返回最大的记录行数目:

mysql> SELECT * FROM table LIMIT 5;//检索前5个记录行,LIMIT n等价于LIMIT 0,n。

62 慢查询日志?

用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。

开启慢查询日志

配置项:slow_query_log

可以使用show variables like ‘slow_query_log’查看是否开启,如果状态值为OFF,可以使用set GLOBAL slow_query_log = on来开启,它会在datadir下产生一个xxx-slow.log的文件。

设置临界时间

配置项:long_query_time

查看:show VARIABLES like 'long_query_time',单位秒

设置:set long_query_time = 0.5

实操时应该从长时间设置到短的时间,即将最慢的SQL优化掉

查看日志,一旦SQL超过了我们设置的临界时间就会被记录到xxx-slow.log中

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

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

64 主键使用自增ID还是UUID?

推荐使用自增ID,不要使用UUID。

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

总之,在数据量大一些的情况下,用自增主键性能会好一些。

关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。

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

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

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

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

67 优化查询过程中的数据访问?

  1. 访问数据太多导致查询性能下降
  2. 确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
  3. 确认MySQL服务器是否在分析大量不必要的数据行
  4. 避免犯如下SQL语句错误
  • 查询不需要的数据。解决办法:使用limit解决
  • 多表关联返回全部列。解决办法:指定列名
  • 总是返回全部列。解决办法:避免使用SELECT *
  • 重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
  • 是否在扫描额外的记录。解决办法:使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过如下技巧去优化:
    • 使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果
    • 改变数据库和表的结构,修改数据表范式
    • 重写SQL语句,让优化器可以以更优的方式执行查询。

68 优化长难的查询语句?

  • 使用尽可能小的查询,有时将一个大的查询分解为多个小的查询是很有必要的
  • 分解关联查询,让缓存的效率更高
  • 执行单个查询可以减少锁的竞争
  • 在应用层做关联更容易对数据库进行拆分

69 优化count类型的查询语句?

  • count(*)会忽略所有的列,直接统计所有列数,不要使用count(列名)
  • MyISAM中,没有任何where条件的count(*)非常快。当有where条件时,MyISAM的count统计不一定比其它引擎快。
  • 可以使用explain查询近似值,用近似值替代count(*)
  • 增加汇总表
  • 使用缓存

70 优化关联查询?

  • 确定ON或者USING子句中是否有索引
  • 确保GROUP BY和ORDER BY只有一个表中的列,这样MySQL才有可能使用索引

71 优化子查询?

  • 用关联查询替代
  • 优化GROUP BY和DISTINCT。这两种查询据可以使用索引来优化,是最有效的优化方法
  • 关联查询中,使用标识列分组的效率更高
  • 如果不需要ORDER BY,进行GROUP BY时加ORDER BY NULL,MySQL不会再进行文件排序
  • WITH ROLLUP超级聚合,可以挪到应用程序处理

72 优化WHERE子句?

  • 对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引
  • 应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描
  • 应尽量避免在where子句中使用or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
  • in和not in也要慎用,否则会导致全表扫描
  • 双%也将导致全表扫描
  • 如果在where子句中使用参数,也会导致全表扫描,例:select id from t where num = @num,可以改为强制查询使用索引:select id from t with(index(索引名)) where num=@num
  • 应尽量避免在where子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描,例:select id from t where num/2 = 100,应改为:select id from t where num = 100*2
  • 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描

73 为什么要优化数据库?

  • 系统的吞吐量瓶颈往往出现在数据库的访问速度上
  • 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
  • 数据是存放在磁盘上的,读写速度无法和内存相比

优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。

74 数据库结构优化?

  • 将字段很多的表分解成多个表

对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。

因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。

  • 增加中间表

对于需要经常联合查询的表,可以建立中间表以提高查询效率。

通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。

  • 增加冗余字段

设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询速度。

表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

注意:冗余字段的值在一个表中修改了,就要想办法在其他表中更新,否则就会导致数据不一致的问题。

75 MySQL数据库CPU飙升到500%的话怎么处理?

CPU飙升到500%时,先用操作系统命令top命令观察是不是mysqld占用导致的,如果不是,找出占用高的进程,并进行相关处理。

如果是mysqld造成的,show processlist,看看里面跑的session情况,是不是有消耗资源的SQL在运行。找出消耗高的SQL,看看执行计划是否准确,index是否缺失,或者实在是数据量太大造成。

一般来说,肯定要kill掉这些线程(同时观察CPU使用率是否下降),等进行相应的调整(比如说加索引、改SQL、改内存参数)之后,再重新跑这些SQL

也有可能是每个SQL消耗资源并不多,但是突然之间,有大量的session连进来导致CPU飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等

76 大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?

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

  1. 限定数据的范围:务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
  2. 读/写分离:经典的数据库拆分方案,主库负责写,从库负责读;
  3. 缓存:使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;

还有就是通过分库分表的方式进行优化,主要有垂直分表和水平分表

垂直分表:

根据数据库里面数据表的相关性进行拆分。例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。

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

水平分表:

保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。

需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以水平拆分最好分库。水平拆分能够支持非常大的数据量存储,应用端改造也少,但分片事务难以解决,跨界点Join性能较差,逻辑复杂。

数据库分片的两种常见方案:

客户端代理:分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。当当网的Sharding-JDBC、阿里的TDDL是两种比较常用的实现。

中间件代理:在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。我们现在谈的Mycat、360的Atlas、网易的DDB等等都是这种架构的实现。

77 分库分表后面临的问题?

  • 事务支持

分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

  • 跨库Join

只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

  • 跨节点的count, order by, group by以及聚合函数问题

这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

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

来自淘宝综合业务平台团队,它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了扩容的难度。

  • ID问题

一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由

一些常见的主键生成策略:

UUID

使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。

Twitter的分布式自增ID算法Snowflake。

在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位机器ID10位毫秒内序列12位。

  • 跨分片的排序分页

一般来讲,分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。

78 MySQL的复制原理以及流程?

主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。

主从复制的作用:

  1. 主数据库出现问题,可以切换到从数据库
  2. 可以进行数据库层面的读写分离
  3. 可以在从数据库上进行日常备份

MySQL主从复制解决的问题

  • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
  • 负载均衡:降低单个服务器的压力
  • 高可用和故障切换:帮助应用程序避免单点失败
  • 升级测试:可以用更高版本的MySQL作为从库

MySQL主从复制工作原理

  1. 在主库上把数据更高记录到二进制日志
  2. 从库将主库的日志复制到自己的中继日志
  3. 从库读取中继日志的事件,将其重放到从库数据中

基本原理流程,3个线程以及之间的关联

主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;

从:IO线程——在使用start slave之后,负责从master上拉取binlog内容,放进自己的relay log中;

从:SQL执行线程——执行relay log中的语句;

复制过程:

Binary log:主数据库的二进制日志

Relay log:从服务器的中继日志

第一步:master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。

第二步:salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。

第三步:SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。

79 读写分离有哪些解决方案?

读写分离是依赖于主从复制,而主从复制又是为读写分离服务的。因为主从复制要求slave不能写只能读(如果对slave执行写操作,那么show slave status将会呈现Slave_SQL_Running = NO,此时你需要按照前面提到的手动同步一下slave)。

方案一:使用mysql-proxy代理

优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的账号,MySQL官方不建议实际生产中使用

缺点:降低性能,不支持事务

方案二

使用AbstractRoutingDataSource + aop + annotation在Dao层决定数据源。

如果采用了Mybatis,可以将读写分离放在ORM层,比如Mybatis可以通过Mybatis plugin拦截SQL语句,所有的insert/update/delete都访问master库,所有的select都访问salve库,这样对于Dao层都是透明。plugin实现时可以通过注解或者分析语句是读写方法来选定主从库。不过这样依然有一个问题,也就是不支持事务,所以我们还需要重写一下DataSourceTransactionManager,将read-only的事务扔进读库,其余的有读有写的扔进写库。

方案三

使用AbstractRoutingDataSource + aop + annotation在service层决定数据源,可以支持事务。

缺点:类内部方法通过this.xx()方式相互调用时,aop不会进行拦截,需进行特殊处理。

80 备份计划,mysqldump以及xtranbackup的实现原理?

1. 备份计划

视库的大小来定,一般来说100G内的库,可以考虑使用mysqldump来做,因为mysqldump更加轻巧灵活,备份时间选在业务低峰期,可以每天进行都进行全量备份(mysqldump备份出来的文件比较小,压缩之后更小)。

100G以上的库,可以考虑用xtranbackup来做,备份速度明显要比mysqldump要快。一般是选择一周一个全备,其余每天进行增量备份,备份时间为业务低峰期。

2. 备份恢复时间

物理备份恢复快,逻辑备份恢复慢

这里跟机器,尤其是硬盘的速率有关系,以下列举几个仅供参考

20G的2分钟(mysqldump)

80G的30分钟(mysqldump)

111G的30分钟(mysqldump)

288G的3小时(xtra)

3T的4小时(xtra)

逻辑导入时间一般是备份时间的5倍以上

3. 备份恢复失败如何处理

首先在恢复之前就应该做足准备工作,避免恢复的时候出错。比如说备份之后的有效性检查、权限检查、空间检查等。如果万一报错,再根据报错的提示来进行相应的调整。

4. mysqldump和xtrabackup实现原理

mysqldump

mysqldump属于逻辑备份。加入–single-transaction选项可以进行一致性备份。后台进程会先设置session的事务隔离级别为RR(SET SESSION TRANSACTION ISOLATION LEVELREPEATABLE READ),之后显式开启一个事务(START TRANSACTION /*!40100 WITH CONSISTENTSNAPSHOT */),这样就保证了该事务里读到的数据都是事务发生时候的快照。之后再把表的数据读取出来。如果加上–master-data=1的话,在刚开始的时候还会加一个数据库的读锁(FLUSH TABLES WITH READ LOCK),等开启事务后,再记录下数据库此时binlog的位置(show master status),马上解锁,再读取表的数据。等所有的数据都已经导完,就可以结束事务

Xtrabackup:

Xtrabackup属于物理备份,直接拷贝表空间文件,同时不断扫描产生的redo日志并保存下来。最后完成InnoDB的备份后,会做一个flush engine logs的操作(老版本在有bug,在5.6上不做此操作会丢数据),确保所有的redo log都已经落盘(涉及到事务的两阶段提交概念,因为xtrabackup并不拷贝binlog,所以必须保证所有的redo log都落盘,否则可能会丢最后一组提交事务的数据)。这个时间点就是InnoDB完成备份的时间点,数据文件虽然不是一致性的,但是有这段时间的redo就可以让数据文件达到一致性(恢复的时候做的事情)。然后还需要flush tables with read lock,把MyISAM等其他引擎的表给备份出来,备份完后解锁。这样就做到了完美的热备。

81 数据表损坏的修复方式有哪些?

使用myisamchk来修复,具体步骤:

  1. 修复前将MySQL服务停止。
  2. 打开命令行方式,然后进入到MySQL的/bin目录。
  3. 执行myisamchk–recover数据库所在路径/*.MYI
  4. 使用repair table或者OPTIMIZE table命令来修复

REPAIR TABLE table_name修复表,OPTIMIZE TABLE table_name优化表。REPAIR TABLE用于修复被破的表,OPTIMIZE TABLE用于回收闲置的数据库空间。当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排。

82 什么是MVCC?

MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。我们每行数实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始一个新的事务,版本号都会自动递增。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务自己插入或者修改的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值