竹笋

首页 » 问答 » 环境 » 面试100问,吊打面试官绰绰有余
TUhjnbcbe - 2024/7/14 23:00:00

来源:Javakeeper

作者:派大新

写在之前:不建议那种上来就是各种面试题罗列,然后背书式的去记忆,对技术的提升帮助很小,对正经面试也没什么帮助,有点东西的面试官深挖下就懵逼了。个人建议把面试题看作是费曼学习法中的回顾、简化的环节,准备面试的时候,跟着题目先自己讲给自己听,看看自己会满意吗,不满意就继续学习这个点,如此反复,好的offer离你不远的,奥利给

一、MySQL架构

和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

连接层:最上层是一些客户端和连接服务。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。服务层:第二层服务层,主要完成大部分的核心服务功能,包括查询解析、分析、优化、缓存、以及所有的内置函数,所有跨存储引擎的功能也都在这一层实现,包括触发器、存储过程、视图等引擎层:第三层存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取存储层:第四层为数据存储层,主要是将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互画出MySQL架构图,这种变态问题都能问的出来MySQL的查询流程具体是?or一条SQL语句在MySQL中如何执行的?

客户端请求---连接器(验证用户身份,给予权限)---查询缓存(存在缓存则直接返回,不存在则执行后续操作)---分析器(对SQL进行词法分析和语法分析操作)---优化器(主要对执行的sql优化选择最优的执行方案方法)---执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)---去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)图:极客时间

说说MySQL有哪些存储引擎?都有哪些区别?

二、存储引擎

存储引擎是MySQL的组件,用于处理不同表类型的SQL操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以获得特定的功能。

使用哪一种引擎可以灵活选择,一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求,使用合适的存储引擎,将会提高整个数据库的性能。

MySQL服务器使用可插拔的存储引擎体系结构,可以从运行中的MySQL服务器加载或卸载存储引擎。

查看存储引擎

--查看支持的存储引擎SHOWENGINES--查看默认存储引擎SHOWVARIABLESLIKEstorage_engine--查看具体某一个表所使用的存储引擎,这个默认存储引擎被修改了!showcreatetabletablename--准确查看某个数据库中的某一表所使用的存储引擎showtablestatusliketablenameshowtablestatusfromdatabasewherename=tablename设置存储引擎

--建表时指定存储引擎。默认的就是INNODB,不需要设置CREATETABLEt1(iINT)ENGINE=INNODB;CREATETABLEt2(iINT)ENGINE=CSV;CREATETABLEt3(iINT)ENGINE=MEMORY;--修改存储引擎ALTERTABLEtENGINE=InnoDB;--修改默认存储引擎,也可以在配置文件my.cnf中修改默认引擎SETdefault_storage_engine=NDBCLUSTER;默认情况下,每当CREATETABLE或ALTERTABLE不能使用默认存储引擎时,都会生成一个警告。为了防止在所需的引擎不可用时出现令人困惑的意外行为,可以启用NO_ENGINE_SUBSTITUTIONSQL模式。如果所需的引擎不可用,则此设置将产生错误而不是警告,并且不会创建或更改表

存储引擎对比

常见的存储引擎就InnoDB、MyISAM、Memory、NDB。

InnoDB现在是MySQL默认的存储引擎,支持事务、行级锁定和外键

文件存储结构对比

在MySQL中建立任何一张数据表,在其数据目录对应的数据库目录下都有对应表的.frm文件,.frm文件是用来保存每个数据表的元数据(meta)信息,包括表结构的定义等,与数据库存储引擎无关,也就是任何存储引擎的数据表都必须有.frm文件,命名方式为数据表名.frm,如user.frm。

查看MySQL数据保存在哪里:showvariableslikedata%

MyISAM物理文件结构为:

.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等.MYD(MYData)文件:MyISAM存储引擎专用,用于存储MyISAM表的数据.MYI(MYIndex)文件:MyISAM存储引擎专用,用于存储MyISAM表的索引相关信息InnoDB物理文件结构为:

.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等.ibd文件或.ibdata文件:这两种文件都是存放InnoDB数据的文件,之所以有两种文件形式存放InnoDB的数据,是因为InnoDB的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独享表空间存放存储数据。独享表空间存储方式使用.ibd文件,并且每个表一个.ibd文件共享表空间存储方式使用.ibdata文件,所有表共同使用一个.ibdata文件(或多个,可自己配置)ps:正经公司,这些都有专业运维去做,数据备份、恢复啥的,让我一个Javaer搞这的话,加钱不?

面试这么回答

InnoDB支持事务,MyISAM不支持事务。这是MySQL将默认存储引擎从MyISAM变成InnoDB的重要原因之一;InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;InnoDB是聚簇索引,MyISAM是非聚簇索引。聚簇索引的文件存放在主键索引的叶子节点上,因此InnoDB必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。InnoDB不保存表的具体行数,执行selectcount(*)fromtable时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;InnoDB最小的锁粒度是行锁,MyISAM最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是MySQL将默认存储引擎从MyISAM变成InnoDB的重要原因之一;

一张表,里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把Mysql重启,再insert一条记录,这条记录的ID是18还是15?

如果表的类型是MyISAM,那么是18。因为MyISAM表会把自增主键的最大ID记录到数据文件中,重启MySQL自增主键的最大ID也不会丢失;

如果表的类型是InnoDB,那么是15。因为InnoDB表只是把自增主键的最大ID记录到内存中,所以重启数据库或对表进行OPTION操作,都会导致最大ID丢失。

哪个存储引擎执行selectcount(*)更快,为什么?

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

在MyISAM存储引擎中,把表的总行数存储在磁盘上,当执行selectcount(*)fromt时,直接返回总数据。在InnoDB存储引擎中,跟MyISAM不一样,没有将总行数存储在磁盘上,当执行selectcount(*)fromt时,会先把数据读出来,一行一行的累加,最后返回总数量。InnoDB中count(*)语句是在执行的时候,全表扫描统计总数量,所以当数据越来越大时,语句就越来越耗时了,为什么InnoDB引擎不像MyISAM引擎一样,将总行数存储到磁盘上?这跟InnoDB的事务特性有关,由于多版本并发控制(MVCC)的原因,InnoDB表“应该返回多少行”也是不确定的。

三、数据类型

主要包括以下五大类:

整数类型:BIT、BOOL、TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT浮点数类型:FLOAT、DOUBLE、DECIMAL字符串类型:CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB日期类型:Date、DateTime、TimeStamp、Time、Year其他数据类型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等

CHAR和VARCHAR的区别?

char是固定长度,varchar长度可变:

char(n)和varchar(n)中括号中n代表字符的个数,并不代表字节个数,比如CHAR(30)就可以存储30个字符。

存储时,前者不管实际存储数据的长度,直接按char规定的长度分配存储空间;而后者会根据实际存储的数据分配最终的存储空间

相同点:

char(n),varchar(n)中的n都代表字符的个数超过char,varchar最大长度n的限制后,字符串会被截断。不同点:

char不论实际存储的字符数都会占用n个字符的空间,而varchar只会占用实际字符应该占用的字节空间加1(实际长度length,0=length)或加2(length)。因为varchar保存数据时除了要保存字符串之外还会加一个字节来记录长度(如果列声明长度大于则使用两个字节来保存长度)。能存储的最大空间限制不一样:char的存储上限为字节。char在存储时会截断尾部的空格,而varchar不会。char是适合存储很短的、一般固定长度的字符串。例如,char非常适合存储密码的MD5值,因为这是一个定长的值。对于非常短的列,char比varchar在存储空间上也更有效率。

列的字符串类型可以是什么?

字符串类型是:SET、BLOB、ENUM、CHAR、CHAR、TEXT、VARCHAR

BLOB和TEXT有什么区别?

BLOB是一个二进制对象,可以容纳可变数量的数据。有四种类型的BLOB:TINYBLOB、BLOB、MEDIUMBLO和LONGBLOB

TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。

BLOB保存二进制数据,TEXT保存字符数据。

四、索引

说说你对MySQL索引的理解?数据库索引的原理,为什么要用B+树,为什么不用二叉树?聚集索引与非聚集索引的区别?InnoDB引擎中的索引策略,了解过吗?创建索引的方式有哪些?聚簇索引/非聚簇索引,mysql索引底层实现,为什么不用B-tree,为什么不用hash,叶子结点存放的是数据还是指向数据的内存地址,使用索引需要注意的几个地方?

MYSQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构,所以说索引的本质是:数据结构索引的目的在于提高查询效率,可以类比字典、火车站的车次表、图书的目录等。可以简单的理解为“排好序的快速查找数据结构”,数据本身之外,数据库还维护者一个满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图是一种可能的索引方式示例。

左边的数据表,一共有两列七条记录,最左边的是数据记录的物理地址。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值,和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到对应的数据,从而快速检索出符合条件的记录。索引本身也很大,不可能全部存储在内存中,一般以索引文件的形式存储在磁盘上平常说的索引,没有特别指明的话,就是B+树(多路搜索树,不一定是二叉树)结构组织的索引。其中聚集索引,次要索引,覆盖索引,符合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。此外还有哈希索引等。基本语法:

创建:创建索引:CREATE[UNIQUE]INDEXindexNameONmytable(username(length));如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定length。修改表结构(添加索引):ALTERtabletableNameADD[UNIQUE]INDEXindexName(columnName)删除:DROPINDEX[indexName]ONmytable;查看:SHOWINDEXFROMtable_name\G--可以通过添加\G来格式化输出信息。使用ALERT命令ALTERTABLEtbl_nameADDPRIMARYKEY(column_list):该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。ALTERTABLEtbl_nameADDUNIQUEindex_name(column_list这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。ALTERTABLEtbl_nameADDINDEXindex_name(column_list)添加普通索引,索引值可出现多次。ALTERTABLEtbl_nameADDFULLTEXTindex_name(column_list)该语句指定了索引为FULLTEXT,用于全文索引。优势

提高数据检索效率,降低数据库IO成本降低数据排序的成本,降低CPU的消耗劣势

索引也是一张表,保存了主键和索引字段,并指向实体表的记录,所以也需要占用内存虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息MySQL索引分类

数据结构角度

B+树索引Hash索引Full-Text全文索引R-Tree索引从物理存储角度

聚集索引(clusteredindex)非聚集索引(non-clusteredindex),也叫辅助索引(secondaryindex)聚集索引和非聚集索引都是B+树结构从逻辑角度

主键索引:主键索引是一种特殊的唯一索引,不允许有空值普通索引或者单列索引:每个索引只包含单个列,一个表可以有多个单列索引多列索引(复合索引、联合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合唯一索引或者非唯一索引空间索引:空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOTNULL,空间索引只能在存储引擎为MYISAM的表中创建为什么MySQL索引中用B+tree,不用B-tree或者其他树,为什么不用Hash索引聚簇索引/非聚簇索引,MySQL索引底层实现,叶子结点存放的是数据还是指向数据的内存地址,使用索引需要注意的几个地方?使用索引查询一定能提高查询的性能吗?为什么?

MySQL索引结构

首先要明白索引(index)是在存储引擎(storageengine)层面实现的,而不是server层面。不是所有的存储引擎都支持所有的索引类型。即使多个存储引擎支持某一索引类型,它们的实现和行为也可能有所差别。

B+Tree索引

MyISAM和InnoDB存储引擎,都使用B+Tree的数据结构,它相对与B-Tree结构,所有的数据都存放在叶子节点上,且把叶子节点通过指针连接到一起,形成了一条数据链表,以加快相邻数据的检索效率。

先了解下B-Tree和B+Tree的区别

B-Tree

B-Tree是为磁盘等外存储设备设计的一种平衡查找树。

系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。

InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每个页的大小为16KB,可通过参数innodb_page_size将页的大小设置为4K、8K、16K,在MySQL中可通过如下命令查看页的大小:showvariableslikeinnodb_page_size;

而系统一个磁盘块的存储空间往往没有这么大,因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。InnoDB在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。

B-Tree结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组[key,data],key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不同的记录,key值互不相同。

一棵m阶的B-Tree有如下特性:

每个节点最多有m个孩子除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。若根节点不是叶子节点,则至少有2个孩子所有叶子节点都在同一层,且不包含其它关键字信息每个非终端节点包含n个关键字信息(P0,P1,…Pn,k1,…kn)关键字的个数n满足:ceil(m/2)-1=n=m-1ki(i=1,…n)为关键字,且关键字升序排序Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)B-Tree中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个3阶的B-Tree:

图片:DobbinSoong每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

模拟查找关键字29的过程:

根据根节点找到磁盘块1,读入内存。比较关键字29在区间(17,35),找到磁盘块1的指针P2。根据P2指针找到磁盘块3,读入内存。比较关键字29在区间(26,30),找到磁盘块3的指针P2。根据P2指针找到磁盘块8,读入内存。在磁盘块8中的关键字列表中找到关键字29。分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

B+Tree

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。

B+Tree相对于B-Tree有几点不同:

非叶子节点只存储键值信息;所有叶子节点之间都有一个链指针;数据记录都存放在叶子节点中将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:

通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为10^3)。也就是说一个深度为3的B+Tree索引可以维护10^3*10^3*10^3=10亿条记录。

实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2-4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

B+Tree性质

通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m=磁盘块的大小/数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了,这个是非常重要的性质,即索引的最左匹配特性。MyISAM主键索引与辅助索引的结构

MyISAM引擎的索引文件和数据文件是分离的。MyISAM引擎索引结构的叶子节点的数据域,存放的并不是实际的数据记录,而是数据记录的地址。索引文件与数据文件分离,这样的索引称为非聚簇索引。MyISAM的主索引与辅助索引区别并不大,只是主键索引不能有重复的关键字。

在MyISAM中,索引(含叶子节点)存放在单独的.myi文件中,叶子节点存放的是数据的物理地址偏移量(通过偏移量访问就是随机访问,速度很快)。

主索引是指主键索引,键值不可能重复;辅助索引则是普通索引,键值可能重复。

通过索引查找数据的流程:先从索引文件中查找到索引节点,从中拿到数据的文件指针,再到数据文件中通过文件指针定位了具体的数据。辅助索引类似。

InnoDB主键索引与辅助索引的结构

InnoDB引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录(对于主索引,此处会存放表中所有的数据记录;对于辅助索引此处会引用主键,检索的时候通过主键到主键索引中找到对应数据行),或者说,InnoDB的数据文件本身就是主键索引文件,这样的索引被称为“聚簇索引”,一个表只能有一个聚簇索引。

主键索引:

我们知道InnoDB索引是聚集索引,它的索引和数据是存入同一个.idb文件中的,因此它的索引结构是在同一个树节点中同时存放索引和数据,如下图中最底层的叶子节点有三行数据,对应于数据表中的id、stu_id、name数据项。

在Innodb中,索引分叶子节点和非叶子节点,非叶子节点就像新华字典的目录,单独存放在索引段中,叶子节点则是顺序排列的,在数据段中。Innodb的数据文件可以按照表来切分(只需要开启innodb_file_per_table),切分后存放在xxx.ibd中,默认不切分,存放在xxx.ibdata中。

辅助(非主键)索引:

这次我们以示例中学生表中的name列建立辅助索引,它的索引结构跟主键索引的结构有很大差别,在最底层的叶子结点有两行数据,第一行的字符串是辅助索引,按照ASCII码进行排序,第二行的整数是主键的值。

这就意味着,对name列进行条件搜索,需要两个步骤:

①在辅助索引上检索name,到达其叶子节点获取对应的主键;

②使用主键在主索引上再进行对应的检索操作

这也就是所谓的“回表查询”

InnoDB索引结构需要注意的点

数据文件本身就是索引文件表数据文件本身就是按B+Tree组织的一个索引结构文件聚集索引中叶节点包含了完整的数据记录InnoDB表必须要有主键,并且推荐使用整型自增主键正如我们上面介绍InnoDB存储结构,索引与数据是共同存储的,不管是主键索引还是辅助索引,在查找时都是通过先查找到索引节点才能拿到相对应的数据,如果我们在设计表结构时没有显式指定索引列的话,MySQL会从表中选择数据不重复的列建立索引,如果没有符合的列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,并且这个字段长度为6个字节,类型为整型。

那为什么推荐使用整型自增主键而不是选择UUID?

UUID是字符串,比整型消耗更多的存储空间;在B+树中进行查找时需要跟经过的节点值比较大小,整型数据的比较运算比字符串更快速;自增的整型索引在磁盘中会连续存储,在读取一页数据时也是连续;UUID是随机产生的,读取的上下两行数据存储是分散的,不适合执行whereid5id20的条件查询语句。在插入或删除数据时,整型自增主键会在叶子结点的末尾建立新的叶子节点,不会破坏左侧子树的结构;UUID主键很容易出现这样的情况,B+树为了维持自身的特性,有可能会进行结构的重构,消耗更多的时间。为什么非主键索引结构叶子节点存储的是主键值?

保证数据一致性和节省存储空间,可以这么理解:商城系统订单表会存储一个用户ID作为关联外键,而不推荐存储完整的用户信息,因为当我们用户表中的信息(真实名称、手机号、收货地址···)修改后,不需要再次维护订单表的用户数据,同时也节省了存储空间。

Hash索引

主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。检索算法:在检索查询时,就再次对待查关键字再次执行相同的Hash算法,得到Hash值,到对应Hash表对应位置取出数据即可,如果发生Hash碰撞,则需要在取值时进行筛选。目前使用Hash索引的数据库并不多,主要有Memory等。MySQL目前有Memory引擎和NDB引擎支持Hash索引。full-text全文索引

全文索引也是MyISAM的一种特殊索引类型,主要用于全文索引,InnoDB从MYSQL5.6版本提供对全文索引的支持。它用于替代效率较低的LIKE模糊匹配操作,而且可以通过多字段组合的全文索引一次性全模糊匹配多个字段。同样使用B-Tree存放索引数据,但使用的是特定的算法,将字段数据分割后再进行索引(一般每4个字节一次分割),索引文件存储的是分割前的索引字符串集合,与分割后的索引信息,对应Btree结构的节点存储的是分割后的词信息以及它在分割前的索引字符串集合中的位置。R-Tree空间索引

空间索引是MyISAM的一种特殊索引类型,主要用于地理空间数据类型

为什么Mysql索引要用B+树不是B树?

用B+树不用B树考虑的是IO对性能的影响,B树的每个节点都存储数据,而B+树只有叶子节点才存储数据,所以查找相同数据量的情况下,B树的高度更高,IO更频繁。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。其中在MySQL底层对B+树进行进一步优化:在叶子节点中是双向链表,且在链表的头结点和尾节点也是循环指向的。

面试官:为何不采用Hash方式?

因为Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。而B+Tree是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描。

哈希索引不支持多列联合索引的最左匹配规则,如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题。

哪些情况需要创建索引

主键自动建立唯一索引频繁作为查询条件的字段查询中与其他表关联的字段,外键关系建立索引单键/组合索引的选择问题,高并发下倾向创建组合索引查询中排序的字段,排序字段通过索引访问大幅提高排序速度查询中统计或分组字段哪些情况不要创建索引

表记录太少经常增删改的表数据重复且分布均匀的表字段,只应该为最经常查询和最经常排序的数据列建立索引(如果某个数据类包含太多的重复数据,建立索引没有太大意义)频繁更新的字段不适合创建索引(会加重IO负担)where条件里用不到的字段不创建索引MySQL高效索引

覆盖索引(CoveringIndex),或者叫索引覆盖,也就是平时所说的不需要回表操作

就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据,当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含(覆盖)满足查询结果的数据就叫做覆盖索引。判断标准使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为usingindex,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询五、MySQL查询

count(*)和count(1)和count(列名)区别ps:这道题说法有点多

执行效果上:

count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULLcount(1)包括了所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULLcount(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。执行效率上:

列名为主键,count(列名)会比count(1)快列名不为主键,count(1)会比count(列名)快如果表多个列并且没有主键,则count(1)的执行效率优于count(*)如果有主键,则selectcount(主键)的执行效率是最优的如果表只有一个字段,则selectcount(*)最优。MySQL中in和exists的区别?

exists:exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录;反之,如果exists里的条件语句不能返回记录行,则当前loop到的这条记录被丢弃,exists的条件就像一个bool条件,当能返回结果集则为true,不能返回结果集则为falsein:in查询相当于多个or条件的叠加SELECT*FROMAWHEREA.idIN(SELECTidFROMB);SELECT*FROMAWHEREEXISTS(SELECT*fromBWHEREB.id=A.id);如果查询的两个表大小相当,那么用in和exists差别不大。

如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:

UNION和UNIONALL的区别?

UNION和UNIONALL都是将两个结果集合并为一个,两个要联合的SQL语句字段个数必须一样,而且字段类型要“相容”(一致);

UNION在进行表连接后会筛选掉重复的数据记录(效率较低),而UNIONALL则不会去掉重复的数据记录;UNION会按照字段的顺序进行排序,而UNIONALL只是简单的将两个结果合并就返回;SQL执行顺序

手写SELECTDISTINCTselect_listFROMleft_tablejoin_typeJOINright_tableONjoin_conditionWHEREwhere_conditionGROUPBYgroup_by_listHAVINGhaving_conditionORDERBYorder_by_conditionLIMITlimit_number机读FROMleft_tableONjoin_conditionjoin_typeJOINright_tableWHEREwhere_conditionGROUPBYgroup_by_listHAVINGhaving_conditionSELECTDISTINCTselect_listORDERBYorder_by_conditionLIMITlimit_number总结

mysql的内连接、左连接、右连接有什么区别?什么是内连接、外连接、交叉连接、笛卡尔积呢?

Join图

六、MySQL事务

事务的隔离级别有哪些?MySQL的默认隔离级别是什么?什么是幻读,脏读,不可重复读呢?MySQL事务的四大特性以及实现原理MVCC熟悉吗,它的底层原理?

MySQL事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

ACID—事务基本要素

事务是由一组SQL语句组成的逻辑处理单元,具有4个属性,通常简称为事务的ACID属性。

A(Atomicity)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样C(Consistency)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏I(Isolation)隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰D(Durability)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚并发事务处理带来的问题

更新丢失(LostUpdate):事务A和事务B选择同一行,然后基于最初选定的值更新该行时,由于两个事务都不知道彼此的存在,就会发生丢失更新问题脏读(DirtyReads):事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据不可重复读(Non-RepeatableReads):事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。幻读(PhantomReads):幻读与不可重复读类似。它发生在一个事务A读取了几行数据,接着另一个并发事务B插入了一些数据时。在随后的查询中,事务A就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。幻读和不可重复读的区别:

不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)并发事务处理带来的问题的解决办法:

“更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决:一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。另一种是数据多版本并发控制(MultiVersionConcurrencyControl,简称MVCC或MCC),也称为多版本数据库:不用加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。事务隔离级别

数据库事务的隔离级别有4种,由低到高分别为

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

showvariablesliketx_isolation下面通过事例一一阐述在事务的并发操作中可能会出现脏读,不可重复读,幻读和事务隔离级别的联系。

数据库的事务隔离越严格,并发副作用越小,但付出的代价就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。

Readun

1
查看完整版本: 面试100问,吊打面试官绰绰有余