2024年Java最全MySQL数据库面试题(2024最新版)_mysql面试(1),java数据库面试问题

最后

小编利用空余时间整理了一份《MySQL性能调优手册》,初衷也很简单,就是希望能够帮助到大家,减轻大家的负担和节省时间。

关于这个,给大家看一份学习大纲(PDF)文件,每一个分支里面会有详细的介绍。

image

这里都是以图片形式展示介绍,如要下载原文件以及更多的性能调优笔记(MySQL+Tomcat+JVM)!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

information text,
KEY name (first_name, last_name),
FULLTEXT KEY (information),
UNIQUE KEY (id_card)

);


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



ALTER TABLE table_name ADD INDEX index_name (column_list);


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


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


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


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



CREATE INDEX index_name ON table_name (column_list);


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


删除索引


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



alter table user_index drop KEY name;
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;


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


![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOS8yLzE5LzE2OTA0NTk2YjIxZTIwOWM?x-oss-process=image/format,png)


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



alter table user_index
– 重新定义字段
MODIFY id int,
drop PRIMARY KEY


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


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


* 非空字段:应该指定列为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树的叶子节点各自独立。

 ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOC85LzIxLzE2NWZiNjgyZTc1OWNmMTI?x-oss-process=image/format,png)


#### 使用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中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值


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


![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMDE1NDQ5OS1kNTNhNWNlOWNlY2YyMmYzLnBuZw?x-oss-process=image/format,png)


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


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


举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行`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规则,具体内容如下:


![事务的特性](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOC81LzIwLzE2MzdiMDhiOTg2MTk0NTU?x-oss-process=image/format,png)


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级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突


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


### 触发器


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


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


使用场景


* 可以通过数据库中的相关表实现级联更改。
* 实时监控某张表中的某个字段的更改而需要做出相应的处理。
* 例如可以生成某些业务的编号。
* 注意不要滥用,否则会造成数据库及应用程序的维护困难。
* 大家需要牢记以上基础知识点,重点是理解数据类型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)



SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)#没有任何关联条件,结果是笛卡尔积,结果集会很大,没有意义,很少使用内连接(INNER JOIN)SELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id多表中同时符合某种条件的数据记录的集合,INNER JOIN可以缩写为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)



SELECT * FROM A UNION SELECT * FROM B UNION …


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


全连接(FULL JOIN)


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



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


表连接面试题


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


R表




| A | B | C |
| --- | --- | --- |
| a1 | b1 | c1 |
| a2 | b2 | c2 |
| a3 | b3 | c3 |


S表




| C | D |
| --- | --- |
| c1 | d1 |
| c2 | d2 |
| c4 | d3 |


1. 交叉连接(笛卡尔积):


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




| A | B | C | C | D |
| --- | --- | --- | --- | --- |
| a1 | b1 | c1 | c1 | d1 |
| a2 | b2 | c2 | c1 | d1 |
| a3 | b3 | c3 | c1 | d1 |
| a1 | b1 | c1 | c2 | d2 |
| a2 | b2 | c2 | c2 | d2 |
| a3 | b3 | c3 | c2 | d2 |
| a1 | b1 | c1 | c4 | d3 |
| a2 | b2 | c2 | c4 | d3 |
| a3 | b3 | c3 | c4 | d3 |


2. 内连接结果:

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




| A | B | C | C | D |
| --- | --- | --- | --- | --- |
| a1 | b1 | c1 | c1 | d1 |
| a2 | b2 | c2 | c2 | d2 |


3. 左连接结果:

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




| A | B | C | C | D |
| --- | --- | --- | --- | --- |
| a1 | b1 | c1 | c1 | d1 |
| a2 | b2 | c2 | c2 | d2 |
| a3 | b3 | c3 |  |  |


4. 右连接结果:

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




|  |  |  |  |  |
| --- | --- | --- | --- | --- |
| A | B | C | C | D |
| a1 | b1 | c1 | c1 | d1 |
| a2 | b2 | c2 | c2 | d2 |
|  |  |  | c4 | d3 |


5. 全表连接的结果(MySql不支持,Oracle支持):

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




| A | B | C | C | D |
| --- | --- | --- | --- | --- |
| a1 | b1 | c1 | c1 | d1 |
| a2 | b2 | c2 | c2 | d2 |
| a3 | b3 | c3 |  |  |
|  |  |  | c4 | d3 |


#### 什么是子查询


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


#### 子查询的三种情况


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



– 查询工资最高的员工是谁?
select * from employee where salary=(select max(salary) from employee);


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



– 查询工资最高的员工是谁?
select * from employee where salary=(select max(salary) from employee);


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



– 1) 查询出2011年以后入职的员工信息
– 2) 查询所有的部门信息,与上面的虚拟表中的信息比对,找出所有部门ID相等的员工。
select * from dept d, (select * from employee where join_date > ‘2011-1-1’) e where e.dept_id = d.id;

– 使用表连接:
select d.*, e.* from dept d inner join employee e on d.id = e.dept_id where e.join_date > ‘2011-1-1’


#### 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的区别


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




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


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


#### UNION与UNION ALL的区别?


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


### SQL优化


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


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


![在这里插入图片描述](https://img-blog.csdnimg.cn/20200310171131582.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RoaW5rV29u,size_16,color_FFFFFF,t_70)


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


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


**select\_type** 每个子查询的查询类型,一些常见的查询类型。




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


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



create table tmp (
id int unsigned not null AUTO_INCREMENT,
name varchar(255),
PRIMARY KEY (id)
) engine = innodb
partition by key (id) partitions 5;


**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性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。


#### SQL的生命周期?


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

 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200310170936478.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RoaW5rV29u,size_16,color_FFFFFF,t_70)


#### 大表数据查询,怎么优化


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并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。

正例:先快速定位需要获取的id段,然后再关联:

SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id


#### mysql 分页


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



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


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



mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.


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



mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行


换句话说,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 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:



select id from t where num is null
– 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=


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



select id from t where num=10 or num=20
– 可以这样查询:
select id from t where num=10 union all select id from t where num=20


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



select id from t where num in(1,2,3)
– 对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3


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



select id from t where num=@num
– 可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num


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



select id from t where num/2=100
– 应改为:
select id from t where num=100*2


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



select id from t where substring(name,1,3)=’abc’
– name以abc开头的id应改为:
select id from t where name like ‘abc%’


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


### 数据库优化


#### 为什么要优化


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


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


#### 数据库结构优化


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


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


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


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


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


**增加中间表**


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


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


**增加冗余字段**


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


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


**注意:**


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


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



## 最后的话

**无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!**

部分截图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/befc88d6fe954822af02d70b15a4d75d.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

@num

  • 8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
-- 应改为:
select id from t where num=100\*2

  • 9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’
-- name以abc开头的id应改为:
select id from t where name like ‘abc%’

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

数据库优化

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

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

数据库结构优化

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

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

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

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

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

增加中间表

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

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

增加冗余字段

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

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

注意:

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

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

最后的话

无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!

部分截图:
[外链图片转存中…(img-S3Tp0LvA-1714867343956)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值