MySQL数据库初中级常见面试题

1    2058    11

Tags:

👉 本文共约55225个字,系统预计阅读时间或需208分钟。

目录

MySQL数据库初中级常见面试题

本页目录 隐藏

数据库基础知识

为什么要使用数据库

数据保存在内存

优点: 存取速度快

缺点: 数据不能永久保存

数据保存在文件

优点: 数据永久保存

缺点:1)速度比内存操作慢,频繁的IO操作。2)查询数据不方便

数据保存在数据库

1)数据永久保存

2)使用SQL语句,查询方便效率高。

3)管理数据方便

什么是SQL?

结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言。

作用:用于存取数据、查询、更新和管理关系数据库系统。

什么是MySQL?

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。

数据库三大范式是什么

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

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

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

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

mysql有关权限的表都有哪几个

MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。下面分别介绍一下这些表的结构和内容:

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

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

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

参考:https://www.xmmup.com/mysqlzhongdeshuangyicanshu.html#redo_log_he_binlog_you_shen_me_qu_bie

数据类型

mysql有哪些数据类型

分类类型名称说明
整数类型tinyInt很小的整数(8位二进制)
smallint小的整数(16位二进制)
mediumint中等大小的整数(24位二进制)
int(integer)普通大小的整数(32位二进制)
小数类型float单精度浮点数
double双精度浮点数
decimal(m,d)压缩严格的定点数
日期类型yearYYYY 1901~2155
timeHH:MM:SS -838:59:59~838:59:59
dateYYYY-MM-DD 1000-01-01~9999-12-3
datetimeYYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59
timestampYYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC
文本、二进制类型CHAR(M)M为0~255之间的整数
VARCHAR(M)M为0~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、整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。
    长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。
    例子,假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。

  • 2、实数类型,包括FLOAT、DOUBLE、DECIMAL。
    DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
    而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
    计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。

  • 3、字符串类型,包括VARCHAR、CHAR、TEXT、BLOB
    VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
    VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
    VARCHAR存储的内容超出设置的长度时,内容会被截断。
    CHAR是定长的,根据定义的字符串长度分配足够的空间。
    CHAR会根据需要使用空格进行填充方便比较。
    CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
    CHAR存储的内容超出设置的长度时,内容同样会被截断。

    使用策略:
    对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
    对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
    使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
    尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。

  • 4、枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。
    有时可以使用ENUM代替常用的字符串类型。
    ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。
    ENUM在内部存储时,其实存的是整数。
    尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。
    排序是按照内部存储的整数

  • 5、日期和时间类型,尽量使用timestamp,空间效率高于datetime,
    用整数保存时间戳通常不方便处理。
    如果需要存储微妙,可以使用bigint存储。
    看到这里,这道真题是不是就比较容易回答了。

引擎

MySQL存储引擎MyISAM与InnoDB区别

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

常用的存储引擎有以下:

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

MyISAM与InnoDB区别

MyISAMInnodb
存储结构每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
存储空间MyISAM可被压缩,存储空间较小InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
可移植性、备份及恢复由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了
文件格式数据和索引是分别存储的,数据.MYD,索引.MYI数据和索引是集中存储的,.ibd
记录存储顺序按记录插入顺序保存按主键大小有序插入
外键不支持支持
事务不支持支持
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的)表级锁定行级锁定、表级锁定,锁定力度小并发能力高
SELECTMyISAM更优
INSERT、UPDATE、DELETEInnoDB更优
select count(*)myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
索引的实现方式B+树索引,myisam 是堆表B+树索引,Innodb 是索引组织表
哈希索引不支持支持
全文索引支持不支持

MyISAM索引与InnoDB索引的区别?

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

InnoDB引擎的4大特性

  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应哈希索引(ahi)
  • 预读(read ahead)

存储引擎选择

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

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

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

索引

什么是索引?

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

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

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

索引有哪些优缺点?

索引的优点

  • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引的缺点

  • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
  • 空间方面:索引需要占物理空间。

索引使用场景(重点)

MySQL数据库初中级常见面试题

上图中,根据id查询记录,因为id字段仅建立了主键索引,因此此SQL执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据。

MySQL数据库初中级常见面试题

可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引(alter table 表名 add index(字段名)),同样的SQL执行的效率,你会发现查询效率会有明显的提升(数据量越大越明显)。

order by

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

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

join

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

索引覆盖

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

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

索引有哪几种类型?

主键索引: 数据列不允许重复,不允许为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);创建全文索引

索引的数据结构(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树索引)

MySQL数据库初中级常见面试题

查询方式:

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

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

B+tree性质:

1.)n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。

2.)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

3.)所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。

4.)B+ 树中,数据对象的插入和删除仅在叶节点上进行。

5.)B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。

2)哈希索引

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

MySQL数据库初中级常见面试题

索引的基本原理

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

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

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

索引算法有哪些?

索引算法有 BTree算法和Hash算法

BTree算法

BTree是最常用的mysql数据库索引算法,也是mysql默认的算法。因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量, 例如:

Hash算法

Hash Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。

索引设计的原则?

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

创建索引的原则(重中之重)

索引虽好,但也不是无限制的使用,最好符合一下几个原则

1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如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)对于定义为text、image和bit的数据类型的列不要建立索引。

创建索引的三种方式,删除索引

第一种方式:在执行CREATE TABLE时创建索引

第二种方式:使用ALTER TABLE命令去增加索引

ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。

其中table_name是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。

索引名index_name可自己命名,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。

第三种方式:使用CREATE INDEX命令创建

CREATE INDEX可对表增加普通索引或UNIQUE索引。(但是,不能创建PRIMARY KEY索引)

删除索引

根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名

删除主键索引:alter table 表名 drop primary key(因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引):

MySQL数据库初中级常见面试题

需要取消自增长再行删除:

但通常不会删除主键,因为设计主键一定与业务逻辑无关。

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

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

使用索引查询一定能提高查询的性能吗?为什么

通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。

  • 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
  • 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
  • 基于非唯一性索引的检索

百万级别或以上的数据如何删除

关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。

  1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
  2. 然后删除其中无用数据(此过程需要不到两分钟)
  3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
  4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

前缀索引

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

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

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

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

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

  • 顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
  • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如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的查询优化器会帮你优化成索引可以识别的形式

B树和B+树的区别

  • 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。

  • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

    MySQL数据库初中级常见面试题

使用B树的好处

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

使用B+树的好处

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

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

首先要知道Hash索引和B+树索引的底层实现原理:

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

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

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

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

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

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

数据库为什么使用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中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引。

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

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

  • 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
  • 非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因

澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值

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

MySQL数据库初中级常见面试题

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

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

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

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

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

具体原因为:

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

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

事务

什么是数据库事务?

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

事务最经典也经常被拿出来说例子就是转账了。

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

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

关系性数据库需要遵循ACID规则,具体内容如下:

MySQL数据库初中级常见面试题

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

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

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

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

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

隔离级别脏读不可重复读幻影读
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(可串行化)隔离级别。

对MySQL的锁了解吗

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

就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。

隔离级别与锁的关系

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

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!

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

在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。

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

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

在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。
  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

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

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

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

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

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

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

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

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

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

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

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

用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的。 一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以。

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

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

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

答:InnoDB是基于索引来完成行锁

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

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

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

什么是死锁?怎么解决?

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

常见的解决死锁的方法

1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

如果业务处理不好可以用分布式事务锁或者使用乐观锁

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

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

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

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

两种锁的使用场景

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

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

视图

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

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

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

视图有哪些特点?

视图的特点如下:

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

视图的操作包括创建视图,查看视图,删除视图和修改视图。

视图的使用场景有哪些?

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

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

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

视图的优点

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

视图的缺点

  1. 性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。

  2. 修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的

    这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)

什么是游标?

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

存储过程与函数

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

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

优点

1)存储过程是预编译过的,执行效率高。

2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。

3)安全性高,执行存储过程需要有一定权限的用户。

4)存储过程可以重复使用,减少数据库开发人员的工作量。

缺点

1)调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。

2)移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。

3)重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。

4)如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。

存储过程和函数的区别

存储过程和函数是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程和函数可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。

在本质上它们都是存储程序。函数只能通过return语句返回单个值或者表对象;而存储过程 不允许执行return,但是可以通过out参数返回多个值。函数限制比较多,不能用临时表,只能用表变量,还有一些函数都不可用等等;而存储过程的限制相对就比较少。函数可以嵌入在SQL 语句中使用,可以在SELECT语句中作为查询语句的一个部分调用;而存储过程一般是作为一个独立的部分来执行。

相同点

  • 存储过程和函数都是为了可重复的执行操作数据库的 SQL 语句的集合。
  • 存储过程和函数都是一次编译后缓存起来,下次使用就直接命中已经编译好的 sql 语句,减少网络交互提高了效率。

不同点

  • 标识符不同,函数的标识符是 function,存储过程是 procedure。
  • 函数返回单个值或者表对象,而存储过程没有返回值,但是可以通过OUT参数返回多个值。
  • 函数限制比较多,比如不能用临时表,只能用表变量,一些函数都不可用等,而存储过程的限制相对就比较少。
  • 一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强
  • 函数的参数只能是 IN 类型,存储过程的参数可以是IN OUT INOUT三种类型。
  • 存储函数使用 select 调用,存储过程需要使用 call 调用。

触发器

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

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

使用场景

  • 可以通过数据库中的相关表实现级联更改。
  • 实时监控某张表中的某个字段的更改而需要做出相应的处理。
  • 例如可以生成某些业务的编号。
  • 注意不要滥用,否则会造成数据库及应用程序的维护困难。
  • 大家需要牢记以上基础知识点,重点是理解数据类型CHAR和VARCHAR的差异,表存储引擎InnoDB和MyISAM的区别。

MySQL中都有哪些触发器?

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

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

常用SQL语句

SQL语句主要分为哪几类

数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER

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

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

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

数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE

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

数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK

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

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

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

SQL 约束有哪几种?

SQL 约束有哪几种?

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

六种关联查询

  • 交叉连接(CROSS JOIN)
  • 内连接(INNER JOIN)
  • 外连接(LEFT JOIN/RIGHT JOIN)
  • 联合查询(UNION与UNION ALL)
  • 全连接(FULL JOIN)
  • 交叉连接(CROSS 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

联合查询(UNION与UNION ALL)

  • 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
  • 如果使用UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

全连接(FULL JOIN)

  • MySQL不支持全连接
  • 可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用

表连接面试题

有2张表,1张R、1张S,R表有ABC三列,S表有CD两列,表中各有三条记录。

R表

ABC
a1b1c1
a2b2c2
a3b3c3

S表

CD
c1d1
c2d2
c4d3
  1. 交叉连接(笛卡尔积):

select r.*,s.* from r,s

ABCCD
a1b1c1c1d1
a2b2c2c1d1
a3b3c3c1d1
a1b1c1c2d2
a2b2c2c2d2
a3b3c3c2d2
a1b1c1c4d3
a2b2c2c4d3
a3b3c3c4d3
  1. 内连接结果:

    select r.*,s.* from r inner join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
  1. 左连接结果:

    select r.*,s.* from r left join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
a3b3c3
  1. 右连接结果:

    select r.*,s.* from r right join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
c4d3
  1. 全表连接的结果(MySql不支持,Oracle支持):

    select r.*,s.* from r full join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
a3b3c3
c4d3

什么是子查询

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

子查询的三种情况

  1. 子查询是单行单列的情况:结果集是一个值,父查询使用:=、 <、 > 等运算符

  1. 子查询是多行单列的情况:结果集类似于一个数组,父查询使用:in 运算符

  1. 子查询是多行多列的情况:结果集类似于一张虚拟表,不能用于where条件,用于select子句中做为子表

mysql中 in 和 exists 区别

mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。

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

varchar与char的区别

char的特点

  • char表示定长字符串,长度是固定的;
  • 如果插入数据的长度小于char的固定长度时,则用空格填充;
  • 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;
  • 对于char来说,最多能存放的字符个数为255,和编码无关

varchar的特点

  • varchar表示可变长字符串,长度是可变的;
  • 插入的数据是多长,就按照多长来存储;
  • varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
  • 对于varchar来说,最多能存放的字符个数为65532

总之,结合性能角度(char更快)和节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。

varchar(50)中50的涵义

最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)。在早期 MySQL 版本中, 50 代表字节数,现在代表字符数。

int(20)中20的涵义

是指显示字符的长度。20表示最大显示宽度为20,但仍占4字节存储,存储范围不变;

不影响内部存储,只是影响带 zerofill 定义的 int 时,前面补多少个 0,易于报表展示

mysql为什么这么设计

对大多数应用没有意义,只是规定一些工具用来显示字符的个数;int(1)和int(20)存储和计算均一样;

mysql中int(10)和char(10)以及varchar(10)的区别

  • int(10)的10表示显示的数据的长度,不是存储数据的大小;chart(10)和varchar(10)的10表示存储数据的大小,即表示存储多少个字符。

    int(10) 10位的数据长度 9999999999,占32个字节,int型4位
    char(10) 10位固定字符串,不足补空格 最多10个字符
    varchar(10) 10位可变字符串,不足补空格 最多10个字符

  • char(10)表示存储定长的10个字符,不足10个就用空格补齐,占用更多的存储空间

  • varchar(10)表示存储10个变长的字符,存储多少个就是多少个,空格也按一个字符存储,这一点是和char(10)的空格不同的,char(10)的空格表示占位不算一个字符

FLOAT和DOUBLE的区别是什么?

  • FLOAT类型数据可以存储至多8位十进制数,并在内存中占4字节。
  • DOUBLE类型数据可以存储至多18位十进制数,并在内存中占8字节。

drop、delete与truncate的区别

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

DeleteTruncateDrop
类型属于DML属于DDL属于DDL
回滚可回滚不可回滚不可回滚
删除内容表结构还在,删除表的全部或者一部分数据行表结构还在,删除表中的所有数据从数据库中删除表,所有的数据行,索引和权限也会被删除
删除速度删除速度慢,需要逐行删除删除速度快删除速度最快

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

UNION与UNION ALL的区别?

  • 如果使用UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

SQL优化

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

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

MySQL数据库初中级常见面试题

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

  • id相同执行顺序由上至下。
  • id不同,id值越大优先级越高,越先被执行。
  • id为null时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。

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

idselect_typedescription
1SIMPLE不包含任何子查询或union等查询
2PRIMARY包含子查询最外层查询就显示为 PRIMARY
3SUBQUERY在select或 where字句中包含的查询
4DERIVEDfrom字句中包含的查询
5UNION出现在union后的查询语句中
6UNION RESULT从UNION中获取结果集,例如上文的第三个例子

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

type(非常重要,可以看到有没有走索引) 访问类型

  • ALL 扫描全表数据
  • index 遍历索引
  • range 索引范围查找
  • index_subquery 在子查询中使用 ref
  • unique_subquery 在子查询中使用 eq_ref
  • ref_or_null 对Null进行索引的优化的 ref
  • fulltext 使用全文索引
  • ref 使用非唯一索引查找数据
  • eq_ref 在join查询中使用PRIMARY KEYorUNIQUE NOT NULL索引关联。

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

key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

TIPS:查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中

key_length 索引长度

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

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

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

  1. Using index 使用覆盖索引
  2. Using where 使用了用where子句来过滤结果集
  3. Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
  4. Using temporary 使用了临时表 sql优化的目标可以参考阿里开发手册

SQL的生命周期?

  1. 应用服务器与数据库服务器建立一个连接

  2. 数据库进程拿到请求sql

  3. 解析并生成执行计划,执行

  4. 读取数据到内存并进行逻辑处理

  5. 通过步骤一的连接,发送结果到客户端

  6. 关掉连接,释放资源

    MySQL数据库初中级常见面试题

大表数据查询,怎么优化

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

超大分页怎么处理?

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

  • 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于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开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.

mysql 分页

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

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

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

换句话说,LIMIT n 等价于 LIMIT 0,n。

慢查询日志

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

开启慢查询日志

配置项:slow_query_log

可以使用show variables like ‘slov_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

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

在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。

慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

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

  • 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。
  • 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
  • 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

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

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

主键使用自增ID还是UUID?

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

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

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

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

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

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

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

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

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

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

优化长难的查询语句

  • 一个复杂查询还是多个简单查询
  • MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多
  • 使用尽可能小的查询是好的,但是有时将一个大的查询分解为多个小的查询是很有必要的。
  • 切分查询
  • 将一个大的查询分为多个小的相同的查询
  • 一次性删除1000万的数据要比一次删除1万,暂停一会的方案更加损耗服务器开销。
  • 分解关联查询,让缓存的效率更高。
  • 执行单个查询可以减少锁的竞争。
  • 在应用层做关联更容易对数据库进行拆分。
  • 查询效率会有大幅提升。
  • 较少冗余记录的查询。

优化特定类型的查询语句

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

优化关联查询

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

优化子查询

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

优化LIMIT分页

  • LIMIT偏移量大的时候,查询效率较低
  • 可以记录上次查询的最大ID,下次查询时直接根据该ID来查询

优化UNION查询

  • UNION ALL的效率高于UNION

优化WHERE子句

解题方法

对于此类考题,先说明如何定位低效SQL语句,然后根据SQL语句可能低效的原因做排查,先从索引着手,如果索引没有问题,考虑以上几个方面,数据访问的问题,长难查询句的问题还是一些特定类型优化的问题,逐一回答。

SQL语句优化的一些方法?

  • 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
  • 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

  • 3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
  • 4.应尽量避免在 where 子句中使用or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

  • 5.in 和 not in 也要慎用,否则会导致全表扫描,如:

  • 6.下面的查询也将导致全表扫描:select id from t where name like ‘%李%’若要提高效率,可以考虑全文检索。
  • 7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

  • 8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

  • 9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

  • 10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

数据库优化

为什么要优化

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

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

数据库结构优化

一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。

需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。

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

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

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

增加中间表

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

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

增加冗余字段

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

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

注意:

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

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

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

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

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

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

大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?分库分表了是怎么做的?分表分库了有什么问题?有用到中间件么?他们的原理知道么?

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

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

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

  1. 垂直分区:

    根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。

    简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

    MySQL数据库初中级常见面试题

    垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

    垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;

    垂直分表

    把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中

    MySQL数据库初中级常见面试题

    适用场景
    • 1、如果一个表中某些列常用,另外一些列不常用
    • 2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
    缺点
    • 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
    • 对于应用层来说,逻辑算法增加开发成本
    • 管理冗余列,查询所有数据需要join操作
  2. 水平分区:

    保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。

    水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

    MySQL数据库初中级常见面试题

    水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库

    水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join性能较差,逻辑复杂。

    《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

    水平分表:

    表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数

    MySQL数据库初中级常见面试题

    适用场景
    • 1、表中的数据本身就有独立性,例如表中分表记录各个地区的数据或者不同时期的数据,特别是有些数据常用,有些不常用。
    • 2、需要把数据存放在多个介质上。
    水平切分的缺点
    • 1、给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需UNION操作
    • 2、在许多数据库应用中,这种复杂度会超过它带来的优点,查询时会增加读一个索引层的磁盘次数

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

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

分库分表后面临的问题

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

  • 跨库join

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

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

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

  • ID问题

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

UUID 使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。 Twitter的分布式自增ID算法Snowflake 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。

  • 跨分片的排序分页

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

    MySQL数据库初中级常见面试题

MySQL的复制原理以及流程

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

主从复制的作用

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

MySQL主从复制解决的问题

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

MySQL主从复制工作原理

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

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

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

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

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

复制过程

MySQL数据库初中级常见面试题

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

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

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

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

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

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

读写分离是依赖于主从复制,而主从复制又是为读写分离服务的。因为主从复制要求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不会进行拦截,需进行特殊处理。

备份计划,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 的位置(showmaster 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 等其他引擎的表给备份出来,备份完后解锁。这样就做到了完美的热备。

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

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

  • 1)修复前将mysql服务停止。
  • 2)打开命令行方式,然后进入到mysql的/bin目录。
  • 3)执行myisamchk –recover 数据库所在路径/*.MYI

使用repair table 或者 OPTIMIZE table命令来修复,REPAIR TABLE table_name 修复表 OPTIMIZE TABLE table_name 优化表 REPAIR TABLE 用于修复被破坏的表。 OPTIMIZE TABLE 用于回收闲置的数据库空间,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排(注意:是磁盘上,而非数据库)

如何选择服务器的类型?

MySQL服务器配置窗口中各个参数的含义如下。

【Server Configuration Type】该选项用于设置服务器的类型。单击该选项右侧的向下按钮, 即可看到包括3个选项。

3个选项的具体含义如下:

Development Machine(开发机器):该选项代表典型个人用桌面工作站。假定机器上运行 着多个桌面应用程序。将MySQL服务器配置成使用最少的系统资源。Server Machine (服务器):该选项代表服务器,MySQL服务器可以同其它应用程序一起 运行,例如FTP、email和web服务器。MySQL服务器配置成使用适当比例的系统资源。DedicatedMySQL Server Machine (专用 MySQL 服务器):该选项代表只运行MySQL服务的服务器。假定运行没有运行其它应用程序。MySQL服务器配置成使用所有可用系统资源。作为初学者,建议选择【DevelopmentMachine】(开发者机器)选项,这样占用系统的资源 比较少。

如何选择存储引擎

不同存储引擎都有各自的特点,以适应不同的需求,如下表所示。为了做出选择:

首先需 要考虑每一个存储引擎提供了哪些不同的功能。如果要提供提交,回滚和崩溃恢复能力的事务安全(ACID兼容)能力,并要求实现并发控 制,InnoDB是个很好的选择。如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较 高的处理效率;如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果。如果只有INSERT和SELECT操作,可以选择Archive引擎,Archive存储引擎支持高并发的插 入操作,但是本身并不是事务安全的。Archive存储引擎非常适合存储归档数据,如记录日志信 息可以使用Archive引擎。使用哪一种引擎要根据需要灵活选择,一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求。使用合适的存储引擎,将会提高整个数据库的性能。

每个表中都要有一个主键吗?

并不是每一个表中都需要主键,一般的,如果多个表之间进行连接操作时,需要用到主键。 因此并不需要为每个表建立主键,而且有些情况最好不使用主键。

每个表都可以任意选择存储引擎吗

外键约束(FOREIGN KEY)不能跨引擎使用。MySQL支持多种存储引擎,每一个表都可 以指定一个不同的存储引擎,但是要注意:外键约束是用来保证数据的参照完整性,如果表之间 需要关联外键,却指定了不同的存储引擎,这些表之间是不能创建外键约束的。所以说,存储引 擎的选择也不完全是随意的。

带AUTO_INCREMENT约束的字段值是从1开始的吗

默认的,在MySQL中,AUTO_INCREMENT的初始值是1,每新增一条记录,字段值自动加1。设置自增属性(AUTO_INCREMENT)的时候,还可以指定第一条插入记录的自增字段的 值,这样新插入的记录的自增字段值从初始值开始递增,如在tb_emp8中插入第一条记录,同时 指定id值为5,则以后插入的记录的id值就会从6开始往上增加。添加唯一性的主键约束时, 往往需要设置字段自动增加属性。

TIMESTAMP与DATATIME两者的区别

TIMESTAMP与DATETIME除了存储字节和支持的范围不同外,还有一个最大的区别就是: DATETIME在存储日期数据时,按实际输入的格式存储,即输入什么就存储什么,与时区无关; 而TIMESTAMP值的存储是以UTC(世界标准时间)格式保存的,存储时对当前时区进行转换, 检索时再转换回当前时区。即查询时,根据当前时区的不同,显示的时间值是不同的。

选择数据类型的方法和技巧是什么

MySQL提供了大量的数据类型,为了优化存储,提高数据库性能,在任何情况下均应使用 最精确的类型。即在所有可以表示该列值的类型中,该类型使用的存储最少。

整数和浮点数

如果不需要小数部分,则使用整数来保存数据;如果需要表示小数部分,则使用浮点数类 型。对于浮点数据列,存入的数值会对该列定义的小数位进行四舍五入。例如如果列的值的范 围为1〜99999,若使用整数,则MEDIUMINT UNSIGNED是最好的类型;若需要存储小数,则 使用FLOAT类型。浮点类型包括FLOAT和DOUBLE类型。DOUBLE类型精度比FLOAT类型高,因此,如要求存储精度较高时,应选择DOUBLE类型。

浮点数和定点数

浮点数FLOAT,DOUBLE相对于定点数DECIMAL的优势是:在长度一定的情况下,浮点 数能表示更大的数据范围。但是由于浮点数容易产生误差,因此对精确度要求比较高时,建议使 用DECIMAL来存储。DECIMAL在MySQL中是以字符串存储的,用于定义货币等对精确度要 求较高的数据。在数据迁移中,float(M,D)是非标准SQL定义,数据库迁移可能会出现问题,最 好不要这样使用。另外两个浮点数进行减法和比较运算时也容易出问题,因此在进行计算的时候, 一定要小心。如果进行数值比较,最好使用DECIMAL类型。

日期与时间类型

MySQL对于不同种类的日期和时间有很多的数据类型,比如YEAR和TIME。如果只需要 记录年份,则使用YEAR类型即可;如果只记录时间,只须使用TIME类型。如果同时需要记录日期和时间,则可以使用TIMESTAMP或者DATETIME类型。由于 TIMESTAMP列的取值范围小于DATETIME的取值范围,因此存储范围较大的日期最好使用 DATETIME。TIMESTAMP也有一个DATETIME不具备的属性。默认的情况下,当插入一条记录但并没 有指定TIMESTAMP这个列值时,MySQL会把TIMESTAMP列设为当前的时间。因此当需要 插入记录同时插入当前时间时,使用TIMESTAMP是方便的,另外TIMESTAMP在空间上比 DATETIME更有效。

CHAR与VARCHAR之间的特点与选择

CHAR和VARCHAR的区别:

CHAR是固定长度字符,VARCHAR是可变长度字符;CHAR会自动删除插入数据的尾部 空格,VARCHAR不会删除尾部空格。CHAR是固定长度,所以它的处理速度比VARCHAR的速度要快,但是它的缺点就是浪费 存储空间。所以对存储不大,但在速度上有要求的可以使用CHAR类型,反之可以使用 VARCHAR类型来实现。
存储引擎对于选择CHAR和VARCHAR的影响:

对于MyISAM存储引擎:最好使用固定长度的数据列代替可变长度的数据列。这样可以使 整个表静态化,从而使数据检索更快,用空间换时间。对于InnoDB存储引擎:使用可变长度的数据列,因为InnoDB数据表的存储格式不分固定 长度和可变长度,因此使用CHAR不一定比使用VARCHAR更好,但由于VARCHAR是按照 实际的长度存储,比较节省空间,所以对磁盘I/O和数据存储总量比较好。

ENUM 和 SET

ENUM只能取单值,它的数据列表是一个枚举集合。它的合法取值列表最多允许有65 535 个成员。因此,在需要从多个值中选取一个时,可以使用ENUM。比如:性别字段适合定义为 ENUM类型,每次只能从’男’或’女’中取一个值。SET可取多值。它的合法取值列表最多允许有64个成员。

空字符串也是一个合法的SET值。 在需要取多个值的时候,适合使用SET类型,比如:要存储一个人兴趣爱好,最好使用SET类型。ENUM和SET的值是以字符串形式出现的,但在内部,MySQL以数值的形式存储它们。

BLOB 和 TEXT

BLOB是二进制字符串,TEXT是非二进制字符串,两者均可存放大容量的信息。BLOB主 要存储图片、音频信息等,而TEXT只能存储纯文本文件。应分清两者的用途。点击这里总结了55道去BAT面试的MYSQL面试题。

MySQL中如何使用特殊字符

诸如单引号(’),双引号("),反斜线()等符号,这些符号在MySQL中不能直接输入 使用,否则会产生意料之外的结果。在MySQL中,这些特殊字符称为转义字符,在输入时需要 以反斜线符号(’\’)开头,所以在使用单引号和双引号时应分别输入(\’)或者(\"),输入反 斜线时应该输入(),其他特殊字符还有回车符(),换行符(),制表符(ab),退格 符(\b)等。在向数据库中插入这些特殊字符时,一定要进行转义处理。

MySQL中可以存储文件吗?

MySQL中的BLOB和TEXT字段类型可以存储数据量较大的文件,可以使用这些数据类型 存储图像、声音或者是大容量的文本内容,例如网页或者文档。虽然使用BLOB或者TEXT可 以存储大容量的数据,但是对这些字段的处理会降低数据库的性能。如果并非必要,可以选择只 储存文件的路径。

MySQL中如何执行区分大小写的字符串比较?

在Windows平台下,MySQL是不区分大小的,因此字符串比较函数也不区分大小写。如果 想执行区分大小写的比较,可以在字符串前面添加BINARY关键字。例如默认情况下,’a’=‘A’ 返回结果为1,如果使用BINARY关键字,BINARY’a’=‘A’结果为0,在区分大小写的情况下,’a’ 与’A’并不相同。

如何从日期时间值中获取年、月、日等部分日期或时间值?

MySQL中,日期时间值以字符串形式存储在数据表中,因此可以使用字符串函数分别截取日期时间值的不同部分,例如某个名称为dt的字段有值“2010-10-01 12:00:30”,如果只需要获 得年值,可以输入LEFT(dt, 4),这样就获得了字符串左边开始长度为4的子字符串,即YEAR 部分的值;如果要获取月份值,可以输入MID(dt,6,2),字符串第6个字符开始,长度为2的子 字符串正好为dt中的月份值。同理,读者可以根据其他日期和时间的位置,计算并获取相应的值。

如何改变默认的字符集?

CONVERT()函数改变指定字符串的默认字符集,在开始的章节中,向读者介绍使用GUI图形化安装配置工具进行MySQL的安装和配置,其中的一个步骤是可以选择MySQL的默认字符集。但是,如果只改变字符集,没有必要把配置过程重新执行一遍,在这里,一个简单的方式是 修改配置文件。在Windows中,MySQL配置文件名称为my.ini,该文件在MySQL的安装目录下面。修改配置文件中的default-character-set和character-set-server参数值,将其改为想要的字 符集名称,如gbk、gb2312、latinl等,修改完之后重新启动MySQL服务,即可生效。读者可以在修改字符集时使用SHOW VARIABLES LIKE ’characterset°%’;命令查看当前字符集,以进行对比。

DISTINCT可以应用于所有的列吗?

查询结果中,如果需要对列进行降序排序,可以使用DESC,这个关键字只能对其前面的列 进行降序排列。例如,要对多列都进行降序排序,必须要在每一列的列名后面加DESC关键字。

而DISTINCT不同,DISTINCT不能部分使用。换句话说,DISTINCT关键字应用于所有列而不 仅是它后面的第一个指定列。例如,查询3个字段s_id,f_name,f_price,如果不同记录的这3个字段的组合值都不同,则所有记录都会被查询出来。

ORDER BY可以和LIMIT混合使用吗?

在使用ORDER BY子句时,应保证其位于FROM子句之后,如果使用LIMIT,则必须位 于ORDER BY之后,如果子句顺序不正确,MySQL将产生错误消息。

什么时候使用引号?

在查询的时候,会看到在WHERE子句中使用条件,有的值加上了单引号,而有的值未加。 单引号用来限定字符串,如果将值与字符串类型列进行比较,则需要限定引号;而用来与数值进 行比较则不需要用引号。

在WHERE子句中必须使用圆括号吗

任何时候使用具有AND和OR操作符的WHERE子句,都应该使用圆括号明确操作顺序。 如果条件较多,即使能确定计算次序,默认的计算次序也可能会使SQL语句不易理解,因此使 用括号明确操作符的次序,是一个好的习惯。

更新或者删除表时必须指定WHERE子句吗?

在前面章节中可以看到,所有的UPDATE和DELETE语句全都在WHERE子句中指定了条 件。如果省略WHERE子句,则UPDATE或DELETE将被应用到表中所有的行。

因此,除非 确实打算更新或者删除所有记录,否则要注意使用不带WHERE子句的UPDATE或DELETE 语句。建议在对表进行更新和删除操作之前,使用SELECT语句确认需要删除的记录,以免造 成无法挽回的结果。点击这里总结了55道去BAT面试的MYSQL面试题。

索引对数据库性能如此重要,应该如何使用它?

为数据库选择正确的索引是一项复杂的任务。如果索引列较少,则需要的磁盘空间和维护开销 都较少。如果在一个大表上创建了多种组合索引,索引文件也会膨胀很快。

而另一方面,索引较多 可覆盖更多的查询。可能需要试验若干不同的设计,才能找到最有效的索引。可以添加、修改和删 除索引而不影响数据库架构或应用程序设计。因此,应尝试多个不同的索引从而建立最优的索引。

尽量使用短索引

对字符串类型的字段进行索引,如果可能应该指定一个前缀长度。例如,如果有一个 CHAR(255)的列,如果在前10个或30个字符内,多数值是惟一的,则不需要对整个列进行索引。 短索引不仅可以提高查询速度而且可以节省磁盘空间、减少I/O操作。

存储过程中的代码可以改变吗

目前,MySQL还不提供对已存在的存储过程代码的修改,如果必须要修改存储过程,必须使用DROP语句删除之后,再重新编写代码,或者创建一个新的存储过程。

存储过程中可以调用其他存储过程吗?

存储过程包含用户定义的SQL语句集合,可以使用CALL语句调用存储过程,当然在存储 过程中也可以使用CALL语句调用其他存储过程,但是不能使用DROP语句删除其他存储过程。

存储过程的参数不要与数据表中的字段名相同

在定义存储过程参数列表时,应注意把参数名与数据库表中的字段名区别开来,否则将出 现无法预期的结果。

存储过程的参数可以使用中文吗

一般情况下,可能会出现存储过程中传入中文参数的情况,例如某个存储过程根据用户的 名字查找该用户的信息,传入的参数值可能是中文。这时需要在定义存储过程的时候,在后面加 上character set gbk,不然调用存储过程使用中文参数会出错,比如定义userInfo存储过程,代码 如下:

CREATE PROCEDURE useInfo(IN u_name VARCHAR(50) character set gbk, OUT u_age INT)

MySQL中视图和表的区别以及联系是什么?

两者的区别:

(1)视图是已经编译好的SQL语句,是基于SQL语句的结果集的可视化的表,而表不是。(2)视图没有实际的物理记录,而基本表有。(3)表是内容,视图是窗口。(4)表占用物理空间而视图不占用物理空间,视图只是逻辑概念的存在,表可以及时对它 进行修改,但视图只能用创建的语句来修改。(5)视图是查看数据表的一种方法,可以查询数据表中某些字段构成的数据,只是一些SQL 语句的集合。从安全的角度来说,视图可以防止用户接触数据表,因而用户不知道表结构。(6)表属于全局模式中的表,是实表;视图属于局部模式的表,是虚表。(7)视图的建立和删除只影响视图本身,不影响对应的基本表。
两者的联系:

视图(view)是在基本表之上建立的表,它的结构(即所定义的列)和内容(即所有记录) 都来自基本表,它依据基本表存在而存在。一个视图可以对应一个基本表,也可以对应多个基本 表。视图是基本表的抽象和在逻辑意义上建立的新关系。

使用触发器时须特别注意

在使用触发器的时候需要注意,对于相同的表,相同的事件只能创建一个触发器,比如对 表account创建了一个BEFORE INSERT触发器,那么如果对表account再次创建一个BEFORE INSERT触发器,MySQL将会报错,此时,只可以在表account上创建AFTER INSERT或者 BEFORE UPDATE类型的触发器。灵活的运用触发器将为操作省去很多麻烦。点击这里总结了55道去BAT面试的MYSQL面试题。

及时删除不再需要的触发器

触发器定义之后,每次执行触发事件,都会激活触发器并执行触发器中的语句。如果需求 发生变化,而触发器没有进行相应的改变或者删除,则触发器仍然会执行旧的语句,从而会影响 新的数据的完整性。因此,要将不再使用的触发器及时删除。

应该使用哪种方法创建用户?

创建用户有几种方法:GRANT语句、CREATE USER语句和直接操作user表。一般情况, 最好使用GRANT或者CREATE USER语句,而不要直接将用户信息插入user表,因为user表中存储了全局级别的权限以及其他的账户信息,如果意外破坏了 user表中的记录,则可能会对 MySQL服务器造成很大影响。

mysqldump备份的文件只能在MySQL中使用吗

mysqldump备份的文本文件实际是数据库的一个副本,使用该文件不仅可以在MySQL中恢 复数据库,而且通过对该文件的简单修改,可以使用该文件在SQL Server或者Sybase等其他数 据库中恢复数据库。这在某种程度上实现了数据库之间的迁移。

如何选择备份工具?

直接复制数据文件是最为直接、快速的备份方法,但缺点是基本上不能实现增量备份。备 份时必须确保没有使用这些表。如果在复制一个表的同时服务器正在修改它,则复制无效。备份 文件时,最好关闭服务器,然后重新启动服务器。为了保证数据的一致性,需要在备份文件前, 执行以下SQL语句:

CREATE PROCEDURE useInfo(IN u_name VARCHAR(50) character set gbk, OUT u_age INT)

目录下即可。mysqlhotcopy 是一个 PERL 程序,它使用 LOCK TABLES、FLUSH TABLES 和 cp 或 scp来快速备份数据库。它是备份数据库或单个表的最快的途径,但它只能运行在数据库文件所在的 机器上,并且mysqlhotcopy只能用于备份MyISAM表。

mysqlhotcopy适合于小型数据库的备份, 数据量不大,可以使用mysqlhotcopy程序每天进行一次完全备份。mysqldump将数据表导成SQL脚本文件,在不同的MySQL版本之间升级时相对比较合适, 这也是最常用的备份方法。mysqldump比直接复制要慢些。也就是把内存中的数据都刷新到磁盘中,同时锁定数据表,以保证复制过程中不会有新的 数据写入。这种方法备份出来的数据恢复也很简单,直接复制回原来的数据库

平时应该打开哪些日志?

日志既会影响MySQL的性能,又会占用大量磁盘空间。因此,如果不必要,应尽可能少地 开启日志。根据不同的使用环境,可以考虑开启不同的日志。例如,在开发环境中优化查询效率 低的语句,可以开启慢查询日志;如果需要记录用户的所有查询操作,可以开启通用查询日志; 如果需要记录数据的变更,可以开启二进制日志;错误日志是默认开启的。

如何使用二进制日志?

二进制日志主要用来记录数据变更。如果需要记录数据库的变化,可以开启二进制日志。 基于二进制日志的特性,不仅可以用来进行数据恢复,还可用于数据复制。

在数据库定期备份的情况下,如果出现数据丢失,可以先用备份恢复大部分数据,然后使用二进制日志恢复最近备份 后变更的数据。在双机热备情况下,可以使用MySQL的二进制日志记录数据的变更,然后将变 更部分复制到备份服务器上。

如何使用慢查询日志?

慢查询日志主要用来记录查询时间较长的日志。在开发环境下,可以开启慢查询日志来记 录查询时间较长的查询语句,然后对这些语句进行优化。通过配long_query_time的值,可以灵活地掌握不同程度的慢查询语句。

是不是索引建立得越多越好?

合理的索引可以提高查询的速度,但不是索引越多越好。在执行插入语句的时候,MySQL 要为新插入的记录建立索引。所以过多的索引会导致插入操作变慢。原则上是只有查询用的字段 才建立索引。

MySQL 和 MariaDB 的区别

MySQL 最初由瑞典 MySQL AB 公司开发,MySQL 的创始人是乌尔夫·米卡埃尔·维德纽斯,常用昵称蒙提(Monty)。

在被甲骨文公司收购后,现在属于甲骨文公司(Oracle) 旗下产品。Oracle 大幅调涨MySQL商业版的售价,因此导致自由软件社区们对于Oracle是否还会持续支持MySQL社区版有所隐忧。

MySQL 的创始人就是之前那个叫 Monty 的大佬以 MySQL为基础成立分支计划 MariaDB。

MariaDB打算保持与MySQL的高度兼容性,确保具有库二进制奇偶校验的直接替换功能,以及与MySQL API 应用程序接口)和命令的精确匹配。而原先一些使用 MySQL 的开源软件逐渐转向 MariaDB 或其它的数据库。

所以如果看到你公司用的是 MariaDB 不用怀疑,其实它骨子里还是 MySQL,学会了MySQL 也就会了 MariaDB。

你之前处理过MySQL的哪些案例?

解答思路:说到案例,逃离不了MySQL的五大知识模块:体系结构、数据的备份恢复、复制、高可用集群架构和优化。我们可以从这五个方向着手考虑,比如:
①MySQL版本的升级;

②处理集群架构中的各种“坑”和问题(你遇到过的就可以);

③根据公司业务类型,合理设计MySQL库、表和后期架构;

④定期进行灾备恢复演练;

⑤恢复误删除的数据信息。

什么是死锁?锁等待?如何优化这类问题?通过数据库哪些表可以监控?

解答思路:死锁是指两个或多个事务在同一资源上互相占用,并请求加锁时,而导致的恶性循环现象。当多个事务以不同顺序试图加锁同一资源时,就会产生死锁。
锁等待:MySQL数据库中,不同session在更新同行数据时,会出现锁等待的现象。重要的三张锁的监控表:innodb_trx、innodb_locks和innodb_lock_waits。

MySQL主从复制的具体原理是什么?

解答思路:直接阐述原理即可,表达一定要清楚。
主服务器把数据更新记录到二进制日志中,从服务器通过I/O thread向主库发起binlog请求,主服务器通过I/O dump thread把二进制日志传递给从库,从库通过I/O thread记录到自己的中继日志中。然后再通过SQL thread应用中继日志中SQL的内容。

MySQL有哪些索引类型?

解答思路:可以从三个角度去谈。
首先从数据结构角度上可以分为B+tree索引、hash索引、fulltext索引(InnoDB、MyISAM都支持)。其次从存储角度上可以分为聚集索引和非聚集索引。最后逻辑角度上可以分为primary key、normal key、单列、复合和覆盖索引。

服务器负载过高或者网页打开缓慢,简单说说你的优化思路?

解答思路:我们可以通过前面讲过的优化思路中的四维度模型去阐述。
首先要发现问题的过程,通过操作系统、数据库、程序设计、硬件角度四个维度找到问题所在。先找到瓶颈点的位置,制定好优化方案,形成处理问题的体系模型。体系制定好之后,在测试环境进行优化方案的测试。测试环境下如果优化效果很好,再实施到生产环境上。最后做好处理问题的记录。好记性不如烂笔头,多做总结,方可大步前进。

如何优化一条慢SQL语句?

解答思路:针对SQL语句的优化,我们不要一上来就回答添加索引,这样显得太不专业。我们可以从如下几个角度去分析:
①回归到表的设计层面,数据类型选择是否合理。
②大表碎片的整理是否完善。
③表的统计信息是不是准确的。
④审查表的执行计划,判断字段上面有没有合适的索引。
⑤针对索引的选择性,建立合适的索引(就又涉及大表DDL的操作问题。所以说,我们要有能力把各个知识点联系起来)

为什么要为InnoDB表设置自增列做主键?

解答思路:使用自增列做主键,写入顺序是自增的,和B+数叶子节点分裂顺序一致。InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致时,存取效率是最高的。

性能优化系列问题

1、您会推荐哪些最常见的性能优化?

这是关于概念知识的,通常是一个开放式问题。他们是否正在考虑投入硬件和增加资源?这是最简单的解决方案,但通常不是正确的解决方案。如果数据库设计不佳,性能通常会受到限制。他们会谈论这个吗?我希望人们讨论最佳选择并决定哪些变量或修复最有意义。我在客户那里看到的一个简单的答案是调整一个像“innodb_dedicated_server”这样的变量,这当然有帮助,因为它是几种配置的快捷方式,但如果你没有正确理解它,它可能会造成一些不必要的伤害(比如很多不需要的重做日志文件)。我正在寻找能够理解快速问题和持久问题之间的区别的人。

2、为了在受 IO 限制的系统中提高 IO 利用率,您需要调整哪些主要参数?

磁盘 IO 是任何数据库中最慢的部分,任何 DBA 都必须了解这个概念。简单的答案始终是添加更多内存,但这不仅仅与缓冲池有关。候选人是否了解影响 IO 的其他变量和数据库内部结构?例如,他们是否了解重做日志、刷新、io_capacity 等,他们是否可以完成他们所做的工作?这些变量的组合将减少 IO 依赖性,同时延长 SSD 寿命。

3、你得到数据库很慢的电话。你做什么工作?您如何识别慢查询?优化查询的最佳策略是什么?

查询优化是每个 DBA 都必须知道的活动。即使数据库在 99% 的时间里看起来都很快且性能良好,一个恶意或缓慢的查询也会导致整个应用程序崩溃。在这里,我们不仅要查找慢查询,还要查找调整和修复它的过程。我们需要了解查询缓慢的原因是什么,例如索引、SQL 编写技巧不佳等。幸运的是,我们有工具可以识别坏人并帮助优化过程。候选人决定使用哪些工具比他们经历的过程更不重要。我们正在寻找诸如识别缺失索引、坏主键、超大表问题、模式设计等的能力。

4、假设您需要将针对 MySQL 服务器的流量增加 10 倍。您的可扩展性策略是什么?读取的缩放比例与写入的缩放比例相同吗?

这里有许多可扩展性策略。您不仅要寻找具体的技术或流程,还要寻找它们背后的原因。需要讨论和深入研究的关键事项:添加副本以卸载流量、设置负载均衡器、增加可用硬件、分片等。所有这些都是有效的策略。真正的问题是候选人是否理解它们以及何时应该使用和不应该使用它们?

每个 DBA 都必须构建并确保他们的数据库系统在需要时可用。因此,了解数据库可用性策略至关重要。必须提出问题来衡量他们的理解:

5、您如何解释异步复制滞后?为什么会出问题?你会怎么修?

异步复制仍然是一个非常重要的主题,虽然随着 MySQL 的每个版本和更改的添加,性能都在不断提高,但我仍在帮助许多客户解决复制设置和滞后问题。许多公司使用副本进行报告、备份和高可用性,因此我们希望我们的副本尽可能保持最新。好消息是这也为候选人提供了另一个跳出框框思考的机会,因为异步复制延迟会受到数据库设计问题的极大影响,例如没有主键、长时间运行的查询、选择更新等。

6、他们能解释异步复制和同步复制之间的区别吗?他们什么时候会选择一个?

当然,您可以在 MySQL 中使用几种不同类型的异步复制(MySQL GTID、MariaDB GTID、Tungsten Replicator)和同步复制(Percona XtraDB Cluster、MySQL Group Replication),因此衡量他们对此的一般知识可能会有所帮助评估他们的技能。虽然这里没有 100% 正确的答案,但重要的是要了解选择一种解决方案的决策驱动因素是什么,例如性能、一致性、可用性等。

7、基于 Galera 的解决方案需要的最少节点数是多少?添加更多节点的利弊是什么?

MySQL 领域有两种主要的集群解决方案。基于 Galera 的复制,如 Percona XtraDB 集群和基于组复制的集群,如 InnoDB 集群。这个问题的关键是要知道 Galera 是为一致性和可用性而设计的,而不是写可扩展性。理解这些差异非常重要,因为“更多的写入者更多的写入”的想法很常见,就好像添加更多的写入者节点可能会增加集群的写入能力一样。

当然,需要确保候选人不仅知道如何排除性能故障和设置高可用性,他们还需要了解一些基本的数据库操作和技术。至少询问一个关于安全性、备份和托管(云与否)的问题是确保您涵盖基础知识的好方法。

8、请描述您推荐的备份策略。您会使用哪些工具以及如何使用?

了解备份的重要性很重要。我正在寻找能够引导我了解他们的经验并告诉我他们认为什么是好的和安全的备份策略的人。在他们交谈时,我会提出问题,看看他们是否了解他们所运行的设置的局限性或好处。我希望他们能够掌握不同的备份选项(物理的、逻辑的)以及可以推荐的备份策略(每日、每周、每月、完整、增量等)。当然,我对人们如何确保他们的备份“良好”很感兴趣。并非所有人都认为还原测试应该被视为备份策略的一部分。

9、部署 MySQL 时,您会遵循哪些基本安全策略?

再次没有一刀切的答案,但毫无疑问,DBA 必须保护公司的数据。需要注意的一些事项:强密码、仅从本地主机进行 root 访问、删除没有密码的用户、删除非管理员用户的 SUPER priv、限制用户访问他们自己的数据库/表、加密数据等。

10、请解释 RDS MySQL 和 Aurora 之间的主要区别。您何时会推荐一种解决方案而不是另一种?

毫无疑问,云已经改变了我们运行数据库的方式。候选人是否对 AWS 的前两个 MySQL 选项之间的差异有基本的了解?他们是不同的,他们知道吗?了解利弊至关重要。虽然 RDS 提供了一种设置异步复制的简单方法,开箱即用,但它需要一些操作活动来确保环境健康,即数据一致性检查、在主故障的情况下处理故障转移等。另一方面。 , Aurora 通过以更高的成本确保一致性和可用性以及一些额外的写入开销来解决操作部分,因此它可能无法在高流量环境中很好地扩展。

MySQL求连续登陆天数-鹅厂面试题

一.需求

求每个用户的最长连续登录天数,两个日期的间隔小于或等于 3 均视为连续登录。比如 01-01 号登录,最近的下一次登录是 01-04 号,两个日期的间隔等于 3 天,因此这两个日期之间的天数都算作活跃天数,一共 4 天。

因为考虑MySQL版本问题,暂时不能使用分析函数。

二.解决方案

测试数据:

代码

这样看起来是不是觉得非常的复杂,下面我们拆解开

2.1 同一天多次登陆去重

考虑同一天会有多次登陆的,此处用group by语句进行去重,同一天只保留一条记录

代码:

测试记录:

2.2 求上一次的登陆日期

我们需要找到上一次登陆日期,与此次的登陆日期对比,才可以判断是否符合小于等于3天的条件
此处,我们可以使用标量子查询来实现
需要记录的每个用户的第一条登陆信息上一天为null

代码:

测试记录:

2.3 打标记判断是否间隔天数<=3

求出上一个登陆日期,与本次登陆日期比较,如果<=3,则标记为0,否则标记为1
需要注意的是上一步的空值直接标记为1
这样通过flag1 我们就可以看到连续登陆的区间了

代码:

测试记录:

2.4 打分组的标记

因为同一个用户存在多个连续登陆的区间,根据上一个步骤的flag1没办法区分开,此时需要区分开同一个用户的不同连续登陆区间。

因为MySQL版本不支持分析函数,只能通过临时表表连接的方式实现,再考虑去除重复,需要用到分组语句进行去重。

其实flag2是这个解决方案核心所在,同一个用户第一次连续登陆区间标记为1,第二次则累加为2,以此类推。

代码:

测试记录:

2.5 分组求最大的间隔天数

其实有了上一个步骤的flag2,就可以判断区分开每一个用户的每一个连续登陆区间,直接进行分组就可以求出该连续区间 最大和最小的登陆日期,两个日期差值就是间隔天数。最后根据user_id进行分组,求最大的间隔天数就是最终结果。

需要审题 01-01到01-04 间隔是3天,但是算4天连续登陆 所以datediff函数的结果要+1

代码:

测试记录:

三.MySQL 8.0写法

MySQL 8.0的with语句以及分析窗口函数,可以使上面的解决方案的代码简洁度大大提升

代码:

测试记录:

一条update SQL在MySQL的内部生命历程是怎样的?

一、Server层阶段:

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

2、分析器:验证通过以后,分析器会对该语句分析, 判断是否语法有错误等。

3、优化器:选择索引,生成执行计划。

4、执行器:根据优化器生成的执行计划,调用存储引擎API执行SQL。

二、InnoDB引擎层阶段:

1、事务执行阶段:

进入InnoDB后引擎层,首先会判断该SQL涉及到的页是否存在于缓存中,如果不存在则从磁盘读取该行记录所在的数据页并加载到BP缓冲池。

假设不存在,然后通过 B+Tree 读取到磁盘的索引页加载到BP缓冲池中 ,如何加载到BP缓冲池中:

首先 通过 space id 和 page no 哈希计算之后把 索引页加载到指定的 buffer pool instance 中

判断 free list 是否有空闲页可用(Innodb_buffer_pool_pages_free、 Innodb_buffer_pool_wait_free),没有则淘汰脏页或者lru list的Old页

把数据页 copy到 free list中,然后加载到 lru list的 old区的 midpoint(头部);

通过二分法查找该页对应的记录,试图给这个SQL涉及到的行记录加上排他锁,过程如下:

如果事务当前记录的行锁被其他事务占用的话,就需要进入锁等待;

进入锁等待之后,同时判断会不会由于自己的加入导致了死锁;

检测到没有锁等待和不会造成死锁之后,行记录加上排他锁。

写逻辑的undo:

将修改前的记录写入undo中

修改当前行的值,填写事务编号,使用回滚指针指向undo log中的修改前的行

从而构建回滚段,用于回滚数据和实现MVCC的多版本

写redo log buffer:

先判断redo log buffer是否够用,redo log buffer不够用就等待,体现在状态值 Innodb_log_waits 上;

在 BP缓冲池 的 Lru list中old区的midpont中对该数据页的行记录的字段值做更新操作,并把修改之后的字段值写入到redo log buffer中

并给LSN加上当前redo log写入的长度(写入长度为 length 的redo log,LSN就会加上 length)

(因为redo group commit的原因,这次事务所产生的redo log buffer可能会跟随其它事务一同flush并且sync到磁盘上)

字段值在BP缓冲池更新成功以后,对应的数据页就是脏页了

写binlog cache:

同时修改的信息,会按照event的格式,记录到binlog_cache中。

写change buffer:

之后把这条sql, 需要在二级索引上做的修改,写入到change buffer page,等到下次有其他sql需要读取该二级索引时,再去与二级索引做merge

(随机I/O变为顺序I/O,但是由于现在的磁盘都是SSD,所以对于寻址来说,随机I/O和顺序I/O差距不大)

事务commit or rollback:

此时update语句已经完成,需要commit或者rollback。这里讨论双1即sync_binlog=1 和 innodb_flush_log_at_trx_commit=1;

2、假设事务COMMIT

(1)事务的COMMIT 分为prepare阶段与commit阶段

事务的COMMIT操作,在存储引擎层与server层之间采用的是内部XA;

两阶段提交协议, 保证两个事务的一致性,这里主要保证redo log和binlog的原子性;

(2)redo log prepare:

写入 redo log处于prepare状态 并且写入事务的xid;

将 redo log buffer 刷新到 redo log磁盘文件中,用于崩溃恢复; #刷盘的方式由 innodb_flush_log_at_trx_commit 决定

(3)binlog write&fsync: 执行器把 binlog cache 里的完整事务和 redo log prepare中的XID 写入到 binlog 中

dump线程会从binlog_cache里把event主动发送给slave的I/O线程,同时执行 fsync刷盘(大事务的话这步非常耗时),并清空 binlog cache。

事务中写 binlog 的部分日志:

190511 11:06:54 server id 123306 end_log_pos 439 CRC32 0x1c809de0 Xid = 614

COMMIT/!/;

binlog刷盘的方式由 sync_binlog 决定;binlog写入完成,事务就算是成功。

事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog file 中。当sync_binlog为1时,当binlog落盘以后才会通知dump thread进行主从复制

(4)redo log commit: commit阶段,由于之前该事务产生的redo log已经sync到磁盘了。所以这步只是在redo log里标记commit,说明事务提交成功。

(5)事务提交成功,释放行记录持有的排他锁;

(6)当binlog和redo log都已经落盘以后,如果触发了刷新脏页的操作:

先把该脏页复制到doublewrite buffer里,其次把doublewrite buffer里的刷新到共享表空间(ibdata),然后才是把脏页写入到磁盘中;

这时候内存页与磁盘的数据页一致。

3、假设事务ROLLBACK

如果事务因为异常或者被显式的回滚了,那么所有数据变更都要改回去。这里就要借助回滚日志中的数据来进行恢复了。

对于in-place(原地)更新,将数据回滚到最老版本;

对于delete+insert方式进行的,标记删除的记录清理删除标记,同时把插入的聚集索引和二级索引记录也会被直接删除。

三、至此,一条update SQL在MySQL中结束生命历程

参考连接

https://thinkwon.blog.csdn.net/article/details/104778621#_26

标签:

Avatar photo

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

1 条回复

  1. Avatar photo 小麦苗说道:

    本文麦老师没有细看,对于其中的一些细节不敢保证其正确性,但是,对刚毕业,急需找工作面试的人有很大帮助,可以背背面试题哈。 总之,有准备比没准备的效果要好很多,另外,“尽信书则不如无书”,自己悟哈。

发表回复

嘿,我是小麦,需要帮助随时找我哦。
  • 18509239930
  • 个人微信

  • DB宝
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部