1.连接查询(必会)
1.左外连接
(左外连接)以左表为基准进行查询,左表数据会全部显示出来,右表 如果和左表匹配 的数据则显示相应字段的数据,如果不匹配,则显示为 NULL;
2.右外连接
(右外连接)以右表为基准进行查询,右表数据会全部显示出来,右表 如果和左表匹配的数据则显示相应字段的数据,如果不匹配,则显示为 NULL;
2.聚合函数(必会)
聚合函数 | SQL中提供的聚合函数可以用来统计、求和、求最值等等。 | |
---|---|---|
分类 | COUNT:统计行数量 | | | | SUM:获取单个列的求和 | | | | AVG:计算某个列的平均值 | | | | MAX:计算列的最大值 | | | | MIN:计算列的最小值 | |
3.SQL关键字(必会)
|
1.分页 | MySQL的分页关键词limit
SELECT FROM student3 LIMIT 2,6; 查询学生表中数据,从第三条开始显示,显示6条 | |
| —- | —- | —- |
| 2.分组 | MySQL的分组关键字:group by
SELECT sex, count() FROM student3 GROUP BY sex; | |
| 3. 去重 | 去重关键字:distinct
select DISTINCT NAME FROM student3; | |
4.SQL Select 语句完整的执行顺序: (必会)
查询中用到的关键词主要包含如下展示,并且他们的顺序依次为from…on…left join…where…group by…avg()/sum()…having..select…order by…asc/desc…limit… | ||
---|---|---|
from: | 需要从哪个数据表检索数据 | |
where: | 过滤表中数据的条件 | |
group by: | 如何将上面过滤出的数据分组算结果 | |
order by : | 按照什么样的顺序来查看返回的数据 | |
limit | 返回的数据分页 |
5.数据库三范式(必会)
第一范式: | 1NF 原子性,列或者字段不能再分,要求属性具有原子性,不可再分解; | |
---|---|---|
第二范式: | 2NF 唯一性,一张表只说一件事,是对记录的惟一性约束,要求记录有惟一标识, | |
第三范式: | 3NF直接性,数据不能存在传递关系,即每个属性都跟主键有直接关系,而不是间接关系。 |
6.存储引擎 (高薪常问)*
MyISAM存储引擎 | InnoDB存储引擎 | |
---|---|---|
版本默认 | MySQL5.5版本之前的默认存储引擎 | MySQL5.5版本之后的默认存储引擎 |
锁支持 | 支持表级锁(表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁); | 支持行级锁(行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁); |
事务和外键 | 不支持 | 支持 |
缓存 | 缓存索引 | 缓存索引和数据 |
全文索引 | 支持 | 不支持,5.6之后是支持的 |
索引实现 | 非聚簇索引 | 聚簇索引 |
总结: 需要事务: 那肯定用innoDB 不需要事务: myisam的查询效率高,内存要求低, 但因为采用表锁, 不适合并发写操作, 读多写少选它 innoDB采用行锁,适合处理并发写操作, 写多读少选它 |
||
7.数据库事务(必会)*
事务的ACID四大特性:
原子性: | 即不可分割性,事务要么全部被执行,要么就全部不被执行。 | |
---|---|---|
一致性: | 事务的执行使得数据库从一种正确状态转换成另一种正确状态 | |
隔离性: | 在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务 | |
持久性: | 事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。 |
事务的隔离级别:
读未提交(read Uncommited) | 在该隔离级别,所有的事务都可以读取到别的事务中未提交的数据,会产生脏读问题,在项目中基本不怎么用,安全性太差; | |
---|---|---|
读已提交(read commited) | 这是大多数数据库默认的隔离级别,解决了脏读的问题,但存在不可重复读和幻读的问题。 | |
可重复读(Repeatable read) | MySQL的默认隔离级别,解决了不可重复读问题,但存在幻读的问题。InnoDB和Falcon存储引擎通过多版本并发控制机制(MVCC)解决了该问题 | |
串行化(serializable) | 事务的最高级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。 | |
可重复读:一个事务中多个实例在并发读取数据的时候会读取到一样的数据 幻读:当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行 |
8.索引介绍(高薪必问)*
索引的分类(必会):
单值索引 | 即一个索引只包含单个列,一个表可以有多个单列索引 | ||
---|---|---|---|
唯一索引 | 索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。 | ||
主键索引 | 它是一种特殊的唯一索引,用于唯一标识数据表中的某一条记录,不允许有空值,一般用primary key 来约束 | ||
复合索引 | 多个字段上建立的索引,能够加速复合查询条件的检索。 | ||
全文索引 | 老版本 MySQL 自带的全文索引只能用于数据库引擎为 MyISAM 的数据表,新版本 MySQL 5.6 的 InnoDB 支持全文索引。默认 MySQL 不支持中文全文检索,可以通过扩展 MySQL,添加中文全文检索或为中文内容表提供一个对应的英文索引表的方式来支持中文。 | | | | 聚簇索引 | 索引结构和数据一起存放的索引。主键索引属于聚簇索引。查询快,但是更新代价大且依赖有序的数据 | | | | 非聚簇索引 | 索引结构和数据分开存放的索引。更新代价小,但是可能会有二次查询, | | | | 覆盖索引 | 一个索引包含所需要查询的字段的值。 | | |
索引的底层实现原理(高薪常问):*
1)MySQL存在哪些索引?
InnoDB引擎 | MyISAM引擎 | Memory引擎 | |||
---|---|---|---|---|---|
B+Tree索引 | 最常见的索引类型, 大部分索引都支持B+树索引 | 支持 | 支持 | 支持 | |
Hash索引 | 只有Memory引擎支持, 使用场景简单 | 不支持 | 不支持 | 支持 | |
R-Tree索引 | R- 空间索引是MyISAM引擎的一个特殊索引类型, 主要地理空间数据, 使用也很少. | 不支持 | 支持 | 不支持 | |
S-Full-Text(全文索引) | 全文索引也是MyISAM的一个特殊索引类型, 主要用于全文索引, InnoDB从Mysql5.6版本开始支持全文索引 | 5.6之后支持 | 支持 | 不支持 |
2)请简单谈谈B+Tree底层原理
根节点:没有父节点的节点。
子节点:有父节点和子节点的节点。
叶子节点:有父节点,但是没有子节点的节点。
数据结构:二叉树。
索引的本质也是一张表。
BTree的实现原理:
BTree又叫多路平衡搜索树,一颗m叉的BTree特性如下:
● 树中每个节点最多包含m个孩子。
● 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子。
● 若根节点不是叶子节点,则至少有两个孩子。
● 所有的叶子节点都在同一层。
● 每个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <= m-1
以5叉BTree为例,key的数量:公式推导[ceil(m/2)-1] <= n <= m-1。所以 2 <= n <=4 。当n>4时,中间节点分裂到父节点,两边节点分裂。
插入 C N G A H E K Q M F W L T Z D P R X Y S 数据为例。
演变过程如下:
1). 插入前4个字母 C N G A
2). 插入H,n>4,中间元素G字母向上分裂到新的节点
3). 插入E,K,Q不需要分裂
4). 插入M,中间元素M字母向上分裂到父节点G
5). 插入F,W,L,T不需要分裂
6). 插入Z,中间元素T向上分裂到父节点中
7). 插入D,中间元素D向上分裂到父节点中。然后插入P,R,X,Y不需要分裂
8). 最后插入S,NPQR节点n>5,中间节点Q向上分裂,但分裂后父节点DGMT的n>5,中间节点M向上分裂
到此,该BTREE树就已经构建完成了, BTREE树 和 二叉树 相比, 查询数据的效率更高, 因为对于相同的数据量来说,BTREE的层级结构比二叉树小,因此搜索速度快。
B+Tree和BTree的区别:
1). n叉B+Tree最多含有n个key,而BTree最多含有n-1个key。
2). B+Tree的叶子节点保存所有的key信息,依key大小顺序排列。
3). 所有的非叶子节点都可以看作是key的索引部分。
MySql索引数据结构对经典的B+Tree进行了优化, 在原B+Tree的基础上, 增加了一个指向相邻叶子节点的链表指针(可以叫双向链表), 就形成了带有顺序指针的B+Tree, 提高区间访问的性能。
索引创建语法:**create index idx_表名_列名 on 表名(列名);**
9.MySql优化(高薪常问)*
索引设计原则:
1.对查询频率较高, 且数据量比较大的表, 建立索引。 | ||
---|---|---|
2.索引字段的选择, 最佳候选列应当从where子句的条件中提取, 如果where子句中的组合比较多, 那么应当挑选最常用, 过滤效果最好的列的组合。 | ||
3.使用唯一索引, 区分度越高, 使用索引的效率越高。 | ||
4.索引并非越多越好, 如果该表增删改操作较多, 慎重选择建立索引, 过多索引会降低表维护效率。 | ||
5.使用短索引, 提高索引访问时的I/O效率, 因此也相应提升了Mysql查询效率。 | ||
6.如果where后有多个条件经常被用到, 建议建立复合索引, 复合索引需要遵循最左前缀法则, N个列组合而成的复合索引, 相当于创建了N个索引。复合索引命名规则: index表名列名1列名2列名3 |
SQL优化的步骤:*
定位和分析问题:
1)查看SQL的执行频率:**通过 show [session|global] status 命令来定位;**
session(当前连接)global(自数据库上次启动至今),Com_xxx 表示每个 xxx 语句执行的次数
Com : 这些参数对于所有存储引擎的表操作都会进行累计。
Innodb : 这几个参数只是针对InnoDB 存储引擎的。
2)定位慢查询的SQL:通过慢查询日志和**show processlist**
命令(推荐);
1) id列,用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
2) user列,显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
3) host列,显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
4) db列,显示这个进程目前连接的是哪个数据库
5) command列,显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
6) time列,显示这个状态持续的时间,单位是秒
7) state列,显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成
8) info列,显示这个sql语句,是判断问题语句的一个重要依据.
3)分析SQL效率低的原因:使用**explain关键字**
字段 | 说明 | ||
---|---|---|---|
id | select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序 | ||
select_type | 表示SELECT的类型,常见的取值有SIMPLE(简单表,没用表连接或子查询)、PRIMARY(外层的查询)、UNION(UNION中的第二个查询语句)、SUBQUERY(子查询中的第一个SELECT)等 | ||
table | 输出结果集的表 | ||
type | 表示表的连接类型,性能由好到差依次是:system->const->eq_ref->ref->ref_or_null->index_merge->index_subquery->range->index->all( 记忆) | ||
possible_keys | 表示查询时,可能使用的索引 | ||
key | 表示实际使用的索引 | ||
key_len | 索引实际使用的长度 | ||
rows | 扫描行的数量 | ||
extra | 执行情况的说明 |
一般来说, 我们需要保证查询至少达到 range 级别, 最好达到ref 。
4)使用**show profile命令**
分析SQL语句,分析时间都耗费到哪里了。
tip:Sending data 状态表示MySQL线程开始访问数据行并把结果返回给客户端,而不仅仅是返回客户端。由于在Sending data状态下,MySQL线程往往需要做大量的磁盘读取操作,所以经常是整个查询中耗时最长的状态。
5)通过trace分析优化器的执行计划,进一步了解SQL优化器的为毛选择A不选择B;
如何避免索引失效?*
1)范围查询, 右边的列不能使用索引, 否则右边的索引会失效 | ||
---|---|---|
3)字符串不加引号, 造成索引失效。如果索引列是字符串类型的整数, 条件查询的时候不加引号会造成索引失效. Mysql内置的优化会有隐式转换. | ||
4)尽量使用覆盖索引, 避免select *。如果索引列完全包含查询列, 那么查询的时候把要查的列写出来, 不使用select * | ||
5)应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描 | ||
6)如果在查询的时候, 使用了复合索引, 要遵循最左前缀法则, 也就是查询从索引的最左列开始, 并且不能跳过索引中的列。 | ||
7)尽量不要在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。 | ||
8)应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。 | ||
9)不做列运算where age + 1 = 10,任何对列的操作都将导致全表扫描,它包括数据库教程函数。计算表达式等, 都会是索引失效. | ||
10)模糊查询 like,如果是 ‘%aaa’ 也会造成索引失效。 | ||
11)全值匹配 ,对索引中所有列都指定具体值。 | ||
12)如果MySQL评估使用索引比全表更慢,则不使用索引。 | ||
13)单列索引和复合索引。尽量使用复合索引,而少使用单列索引 。 | ||
14) in 走索引, not in 索引失效。 |
如何进行SQL优化?*
情形一:大批量插入数据
a.主键顺序插入。把导入的数据按照主键的顺序排列;(推荐)
b.关闭唯一性校验。插入前执行**set UNIQUE_CHECKS = 0**
,导入结束一定要恢复。
c.手动提交事务。插入前关闭自动提交事务,导入结束再开启。
情形二:优化insert语句
a.如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗
b.在事务中进行数据插入。
c.数据有序插入。
情形三:优化order by语句
a.第一种是通过对返回数据进行排序,也就是通常说的 filesort 排序,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
b.第二种通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。(推荐)
情形四:优化group by语句
由于GROUP BY 实际上也同样会进行排序操作,而且与ORDER BY 相比,GROUP BY 主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引。总结:和order by优化一样的方案
情形五:优化嵌套查询
使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中,即使用子查询。特殊情形下可以join可能效率更高。
情形六:优化or条件
对于包含OR的查询子句,如果要利用索引,则OR之间的每个条件列都必须用到索引 , 而且不能使用到复合索引; 如果没有索引,则应该考虑增加索引。建议使用union替换or,union语句的type值是ref,or语句的type值是rang,差距非常明显。union联合查询(有去重效果),union all(不去重),可用来消除笛卡尔积
情形七:优化分页查询
a.在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。(推荐使用)
b.该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询 。
合理的数据库设计:
1.根据数据库三范式来进行表结构的设计。
注意:没有最好的设计,只有最合适的设计,所以不要过分注重理论。三范式可以作为一个基本依据,不要生搬硬套。有时候可以根据场景合理地反规范化:
A:保留冗余字段。当两个或多个表在查询中经常需要连接时,可以在其中一个表上增加若干冗余的字段,以 避免表之间的连接过于频繁,一般在冗余列的数据不经常变动的情况下使用。
B:增加派生列。派生列是由表中的其它多个列的计算所得,增加派生列可以减少统计运算,在数据汇总时可以大大缩短运算时间, 前提是这个列经常被用到, 这也就是反第三范式。
C:分割表。
数据表拆分:主要就是垂直拆分和水平拆分。
水平切分:将记录散列到不同的表中,各表的结构完全相同,每次从分表中查询, 提高效率。
垂直切分:将表中大字段单独拆分到另外一张表, 形成一对一的关系。
D: 字段设计
1. 表的字段尽可能用NOT NULL
2. 字段长度固定的表查询会更快
3. 把数据库的大表按时间或一些标志分成小表
冗余场景:一般数据改动的可能性少,而查询多的场景会使用冗余,例如淘宝的店铺名称,淘宝商家中心会有这个字段,可能里面的商家论坛也有,再假设聚划算这种独立的大业务自己也存一份,再来个垂直频道电器城的后台管理也独立存一份,这种场景是由于对查询性能要求高产生的,所以必须要冗余,在业务的取舍上,肯定是对让用户更快看到信息,那么不可避免的是带来维护成本的增加,对于数据一致性问题,只要做到最终一致就可以了,分布式的CAP原则的实际应用基本都是通过牺牲数据一致性(C)来保证高可用(A)和高可靠(P), 因为这种场景大部分都是可以接受短暂的数据不一致的,对业务的影响及其微小。
在餐掌柜的项目我们遵循的原则:
项目全部采用逻辑关联,没有采用主外键约束
尽可能少使用多表关联查询。冗余是为了效率,减少join
尽可能服务独立化,查询单表化,例如:查询用户信息又需要用户的头像,处理的原则是调用【用户服务】和【附件服务】在dubbo层组装数据
10.数据库锁(高薪常问)*
行锁 | 访问数据库的时候,锁定整个行数据,防止并发错误。开销大,加锁慢,会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高 | ||
---|---|---|---|
表锁 | 访问数据库的时候,锁定整个表数据,防止并发错误。开销小,加锁快,不会出现死锁;锁定粒度大,发生锁冲突概率高,并发度最低 | ||
悲观锁 | 每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 | ||
乐观锁 | 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。适用于多读的应用类型,write_condition机制 | ||
页面锁 | 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般 | ||
间隙锁 | 当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁; 对于键值在条件范围内但并不存在的记录,叫做 “间隙(GAP)” , InnoDB也会对这个 “间隙” 加锁,这种锁机制就是所谓的 间隙锁(Next-Key锁),尽力避免 |
11.线上如果出现死锁如何解决?
- 在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。在下面的例子中,由于两个session访问两个表的顺序不同,发生死锁的机会就非常高!但如果以相同的顺序来访问,死锁就可以避免。
- 在程序以批量方式处理数据的时候,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能。
- 在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁(行锁),而不应先申请共享锁(表锁),更新时再申请排他锁,因为当用户申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。
- 在REPEATABLE-READ隔离级别下,如果两个线程同时对相同条件记录用SELECT…FOR UPDATE加排他锁,在没有符合该条件记录情况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁。这种情况下,将隔离级别改成READ COMMITTED,就可避免问题。
- 当隔离级别为READ COMMITTED时,如果两个线程都先执行SELECT…FOR UPDATE,判断是否存在符合条件的记录,如果没有,就插入记录。此时,只有一个线程能插入成功,另一个线程会出现锁等待,当第1个线程提交后,第2个线程会因主键冲突出错,但虽然这个线程出错了,却会获得一个排他锁!这时如果有第3个线程又来申请排他锁,也会出现死锁。对于这种情况,可以直接做插入操作,然后再捕获主键重复异常,或者在遇到主键重复错误时,总是执行ROLLBACK释放获得的排他锁
数据库的MVCC机制工作过程?
undo_log多版本链
ReadView“快照读”:SQL执行时,MVCC提取数据的依据。是一个数据结构,包含4个字段:m_ids(活跃的事务编号集合)、min_trx_id(最小活跃事务编号)、max_trx_id(预分配事务编号)、creator_trx_id(创建者的事务编号)
读已提交(RC):每一次执行快照读时生成ReadView
可重复读(RR):仅在第一次执行快照读时生成ReadView,后续快照读复用。RR级别下使用MVCC不能完全解决幻读的问题。在连续多次快照读,ReadView会产生复用,没有幻读问题。但是当两次快照读之间存在当前读,ReadView会重新生成,导致产生幻读