MySQL的分页语法:
在MySQL中,SELECT语句默认返回所有匹配的行,它们可能是指定表中的每个行。为了返回第一行或前几行,可使用LIMIT子句,以实现分页查询。LIMIT子句的语法如下:
-- 在所有的查询结果中,返回前5行记录。
SELECT prod_name FROM products LIMIT 5;
-- 在所有的查询结果中,从第5行开始,返回5行记录。
SELECT prod_name FROM products LIMIT 5,5;
总之,带一个值的LIMIT总是从第一行开始,给出的数为返回的行数。带两个值的LIMIT可以指定从行号为第一个值的位置开始。
优化LIMIT分页:
在偏移量非常大的时候,例如 LIMIT 10000,20 这样的查询,这时MySQL需要查询10020条记录然后只返回最后20条,前面的10000条记录都将被抛弃,这样的代价是非常高的。如果所有的页面被访问的频率都相同,那么这样的查询平均需要访问半个表的数据。要优化这种查询,要么是在页面中限制分页的数量,要么是优化大偏移量的性能。
优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是查询所有的列,然后根据需要做一次关联操作再返回所需的列。对于偏移量很大的时候,这样做的效率会提升非常大。考虑下面的查询:
SELECT film_id,description FROM sakila.film ORDER BY title LIMIT 50,5;
如果这个表非常大,那么这个查询最好改写成下面的样子:
SELECT film.film_id,film.description
FROM sakila.film
INNER JOIN (
SELECT film_id FROM sakila.film ORDER BY title LIMIT 50,5
) AS lim USING(film_id);
这里的“延迟关联”将大大提升查询效率,它让MySQL扫描尽可能少的页面,获取需要访问的记录后再根
据关联列回原表查询需要的所有列。这个技术也可以用于优化关联查询中的LIMIT子句。有时候也可以将LIMIT查询转换为已知位置的查询,让MySQL通过范围扫描获得对应的结果。例如,如果在一个位置列上有索引,并且预先计算出了边界值,上面的查询就可以改写为:
SELECT film_id,description FROM skila.film
WHERE position BETWEEN 50 AND 54 ORDER BY position;
对数据进行排名的问题也与此类似,但往往还会同时和GROUP BY混合使用,在这种情况下通常都需要预先计算并存储排名信息。
LIMIT和OFFSET的问题,其实是OFFSET的问题,它会导致MySQL扫描大量不需要的行然后再抛弃掉。如果可以使用书签记录上次取数的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用OFFSET。例如,若需要按照租赁记录做翻页,那么可以根据最新一条租赁记录向后追溯,这种做法可行是因为租赁记录的主键是单调增长的。首先使用下面的查询获得第一组结果:
SELECT * FROM sakila.rental ORDER BY rental_id DESC LIMIT 20;
假设上面的查询返回的是主键16049到16030的租赁记录,那么下一页查询就可以从16030这个点开始:
SELECT * FROM sakila.rental
WHERE rental_id < 16030 ORDER BY rental_id DESC LIMIT 20;
该技术的好处是无论翻页到多么后面,其性能都会很好。
常用的聚合函数有COUNT()、AVG()、SUM()、MAX()、MIN(),下面以MySQL为例,说明这些函数的作用。
表与表之间常用的关联方式有两种:内连接、外连接,下面以MySQL为例来说明这两种连接方式。
内连接:
内连接通过INNER JOIN来实现,它将返回两张表中满足连接条件的数据,不满足条件的数据不会查询出来。
外连接:
外连接通过OUTER JOIN来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。外连接有两种形式:左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)。
左外连接:可以简称为左连接(LEFT JOIN),它会返回左表中的所有记录和右表中满足连接条件的记录。
右外连接:可以简称为右连接(RIGHT JOIN),它会返回右表中的所有记录和左表中满足连接条件的记录。
除此之外,还有一种常见的连接方式:等值连接。这种连接是通过WHERE子句中的条件,将两张表连接在一起,它的实际效果等同于内连接。出于语义清晰的考虑,一般更建议使用内连接,而不是等值连接。
以上是从语法上来说明表与表之间关联的实现方式,而从表的关系上来说,比较常见的关联关系有:一对多关联、多对多关联、自关联。
一对多关联:这种关联形式最为常见,一般是两张表具有主从关系,并且以主表的主键关联从表的外键来实现这种关联关系。另外,以从表的角度来看,它们是具有多对一关系的,所以不再赘述多对一关联了。
多对多关联:这种关联关系比较复杂,如果两张表具有多对多的关系,那么它们之间需要有一张中间表来作为衔接,以实现这种关联关系。这个中间表要设计两列,分别存储那两张表的主键。因此,这两张表中的任何一方,都与中间表形成了一对多关系,从而在这个中间表上建立起了多对多关系。
自关联:自关联就是一张表自己与自己相关联,为了避免表名的冲突,需要在关联时通过别名将它们当做两张表来看待。一般在表中数据具有层级(树状)时,可以采用自关联一次性查询出多层级的数据。
外连接通过OUTER JOIN来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。常见的外连接有两种形式:左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)。
左外连接:可以简称为左连接(LEFT JOIN),它会返回左表中的所有记录和右表中满足连接条件的记录。
右外连接:可以简称为右连接(RIGHT JOIN),它会返回右表中的所有记录和左表中满足连接条件的记录。
实际上,外连接还有一种形式:完全外连接(FULL OUTER JOIN),但MySQL不支持这种形式。
外连接通过OUTER JOIN来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。常见的外连接有两种形式:左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)。
我们以MySQL数据库为例,来说明行转列的实现方式。
首先,假设我们有一张分数表(tb_score),表中的数据如下图:

然后,我们再来看一下转换之后需要得到的结果,如下图:
可以看出,这里行转列是将原来的subject字段的多行内容选出来,作为结果集中的不同列,并根据userid进行分组显示对应的score。通常,我们有两种方式来实现这种转换。
SELECT userid,
SUM(CASE `subject` WHEN '语文' THEN score ELSE 0 END) as '语文',
SUM(CASE `subject` WHEN '数学' THEN score ELSE 0 END) as '数学',
SUM(CASE `subject` WHEN '英语' THEN score ELSE 0 END) as '英语',
SUM(CASE `subject` WHEN '政治' THEN score ELSE 0 END) as '政治'
FROM tb_score
GROUP BY userid
注意,SUM() 是为了能够使用GROUP BY根据userid进行分组,因为每一个userid对应的subject="语文"的记录只有一条,所以SUM() 的值就等于对应那一条记录的score的值。假如userid=‘001’ and subject=‘语文’ 的记录有两条,则此时SUM() 的值将会是这两条记录的和,同理,使用Max()的值将会是这两条记录里面值最大的一个。但是正常情况下,一个user对应一个subject只有一个分数,因此可以使用SUM()、MAX()、MIN()、AVG()等聚合函数都可以达到行转列的效果。
2. 使用 IF() 函数实现行转列,参考如下代码:
SELECT userid,
SUM(IF(`subject`='语文',score,0)) as '语文',
SUM(IF(`subject`='数学',score,0)) as '数学',
SUM(IF(`subject`='英语',score,0)) as '英语',
SUM(IF(`subject`='政治',score,0)) as '政治'
FROM tb_score
GROUP BY userid
注意, IF(subject=‘语文’,score,0) 作为条件,即对所有subject='语文’的记录的score字段进行SUM()、MAX()、MIN()、AVG()操作,如果score没有值则默认为0。
SQL注入的原理是将SQL代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对SERVER端发起的请求参数中植入一些SQL代码,SERVER端在执行SQL操作时,会拼接对应参数,同时也将一些SQL注入攻击的“SQL”拼接起来,导致会执行一些预期之外的操作。
举个例子:
比如我们的登录功能,其登录界面包括用户名和密码输入框以及提交按钮,登录时需要输入用户名和密码,然后提交。此时调用接口/user/login/ 加上参数username、password,首先连接数据库,然后后台对请求参数中携带的用户名、密码进行参数校验,即SQL的查询过程。假设正确的用户名和密码为ls和123456,输入正确的用户名和密码、提交,相当于调用了以下的SQL语句。
SELECT * FROM user WHERE username = 'ls' AND password = '123456'
SQL中会将#及–以后的字符串当做注释处理,如果我们使用 ’ or 1=1 # 作为用户名参数,那么服务端构建的SQL语句就如下:
select * from user where username='' or 1=1 #' and password='123456'
而#会忽略后面的语句,而1=1属于常等型条件,因此这个SQL将查询出所有的登录用户。其实上面的SQL注入只是在参数层面做了些手脚,如果是引入了一些功能性的SQL那就更危险了,比如上面的登录功能,如果用户名使用这个 ’ or 1=1;delete * from users; # ,那么在";"之后相当于是另外一条新的SQL,这个SQL是删除全表,是非常危险的操作,因此SQL注入这种还是需要特别注意的
可以采用关联更新的方式,将一张表的部分数据,更新到另一张表内。参考如下代码:
update b set b.col=a.col from a,b where a.id=b.id;
update b set col=a.col from b inner join a on a.id=b.id;
update b set b.col=a.col from b left Join a on b.id = a.id;
WHERE是一个约束声明,使用WHERE约束来自数据库的数据,WHERE是在结果返回之前起作用的,WHERE中不能使用聚合函数。
HAVING是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在HAVING中可以使用
聚合函数。另一方面,HAVING子句中不能使用除了分组字段和聚合函数之外的其他字段。
从性能的角度来说,HAVING子句中如果使用了分组字段作为过滤条件,应该替换成WHERE子句。因为WHERE可以在执行分组操作和计算聚合函数之前过滤掉不需要的数据,性能会更好。
索引是一个单独的、存储在磁盘上的数据库结构,包含着对数据表里所有记录的引用指针。使用索引可以快速找出在某个或多个列中有一特定值的行,所有MySQL列类型都可以被索引,对相关列使用索引是提高查询操作速度的最佳途径。
索引是在存储引擎中实现的,因此,每种存储引擎的索引都不一定完全相同,并且每种存储引擎也不一定支持所有索引类型。MySQL中索引的存储类型有两种,即BTREE和HASH,具体和表的存储引擎相关。MyISAM和InnoDB存储引擎只支持BTREE索引;MEMORY/HEAP存储引擎可以支持HASH和BTREE
索引。
索引的优点主要有以下几条:
增加索引也有许多不利的方面,主要表现在如下几个方面:
MySQL的索引可以分为以下几类:
MySQL支持多种方法在单个或多个列上创建索引:
在创建表的时候创建索引:
使用CREATE TABLE创建表时,除了可以定义列的数据类型,还可以定义主键约束、外键约束或者唯一性约束,而不论创建哪种约束,在定义约束的同时相当于在指定列上创建了一个索引。创建表时创建索引的基本语法如下:
CREATE TABLE table_name [col_name data_type]
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name [length])
[ASC|DESC]
其中,UNIQUE、FULLTEXT和SPATIAL为可选参数,分别表示唯一索引、全文索引和空间索引;INDEX与KEY为同义词,两者作用相同,用来指定创建索引。
例如,可以按照如下方式,在id字段上使用UNIQUE关键字创建唯一索引:
CREATE TABLE t1 (
id INT NOT NULL,
name CHAR(30) NOT NULL,
UNIQUE INDEX UniqIdx(id)
);
在已存在的表上创建索引:
在已经存在的表中创建索引,可以使用ALTER TABLE语句或者CREATEINDEX语句。
ALTER TABLE创建索引的基本语法如下:
ALTER TABLE table_name ADD
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name[length],...)
[ASC|DESC]
例如,可以按照如下方式,在bookId字段上建立名称为UniqidIdx的唯一索引:
ALTER TABLE book ADD UNIQUE INDEX UniqidIdx (bookId);
CREATE INDEX创建索引的基本语法如下:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
ON table_name (col_name [length],...) [ASC|DESC]
例如,可以按照如下方式,在bookId字段上建立名称为UniqidIdx的唯一索引:
CREATE UNIQUE INDEX UniqidIdx ON book (bookId);
建议按照如下的原则来创建索引:
不一定。
比如,在使用组合索引的时候,如果没有遵从“最左前缀”的原则进行搜索,则索引是不起作用的。
举例,假设在id、name、age字段上已经成功建立了一个名为MultiIdx的组合索引。索引行中按id、name、age的顺序存放,索引可以搜索id、(id,name)、(id, name, age)字段组合。如果列不构成索引最左面的前缀,那么MySQL不能使用局部索引,如(age)或者(name,age)组合则不能使用该索引查询。
可以使用EXPLAIN语句查看索引是否正在使用。
举例,假设已经创建了book表,并已经在其year_publication字段上建立了普通索引。执行如下语句:
EXPLAIN SELECT * FROM book WHERE year_publication=1990;
EXPLAIN语句将为我们输出详细的SQL执行信息,其中:
possible_keys行给出了MySQL在搜索数据记录时可选用的各个索引。
key行是MySQL实际选用的索引。
如果possible_keys行和key行都包含year_publication字段,则说明在查询时使用了该索引。
建议按照如下的原则来设计索引:
索引并非越多越好,一个表中如有大量的索引,不仅占用磁盘空间,还会影响INSERT、DELETE、UPDATE等语句的性能,因为在表中的数据更改时,索引也会进行调整和更新。
可以采用以下几种方式,来避免索引失效:
不是。
下列几种情况,是不适合创建索引的:
在MySQL中,索引是在存储引擎层实现的,不同存储引擎对索引的实现方式是不同的,下面我们探讨一下MyISAM和InnoDB两个存储引擎的索引实现方式。
MyISAM索引实现:
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,MyISAM索引的原理图如下。这里假设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。

如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示。同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。

InnoDB索引实现:
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此
InnoDB表数据文件本身就是主索引。
下图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。下图为定义在Col3上的一个辅助索引。这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索
需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
什么时候需要重建索引呢?
怎么判断索引是否应该重建?
analyze index index_name validate structure;
select height,DEL_LF_ROWS/LF_ROWS from index_stats;
当查询的height>=4(索引的深度,即从根到叶节点的高度)或DEL_LF_ROWS/LF_ROWS>0.2的情况下,就应该考虑重建该索引。
如何重建索引?
drop index index_name;
create index index_name on table_name (index_column);
这种方式相当耗时,一般不建议使用!
直接重建索引:
alter index indexname rebuild;
alter index indexname rebuild online;
此方法较快,建议使用。
rebuild是快速重建索引的一种有效的办法,因为它是一种使用现有索引项来重建新索引的方法。如果重建索引时有其他用户在对这个表操作,尽量使用带online参数来最大限度的减少索引重建时将会出现的任何加锁问题。由于新旧索引在建立时同时存在,因此,使用这种重建方法需要有额外的磁盘空间可供临时使用,当索引建完后把老索引删除,如果没有成功,也不会影响原来的索引。利用这种办法可以用来将一个索引移到新的表空间。
rebuild重建索引的过程:
重建索引过程中的注意事项:
4. 执行rebuild操作时,需要检查表空间是否足够;
5. 虽然说rebuild online操作允许DML操作,但还是建议在业务不繁忙时间段进行;
6. Rebuild操作会产生大量Redo Log;
B+树由B树和索引顺序访问方法演化而来,它是为磁盘或其他直接存取辅助设备设计的一种平衡查找树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点,各叶子节点通过指针进行链接。如下图:

B+树索引在数据库中的一个特点就是高扇出性,例如在InnoDB存储引擎中,每个页的大小为16KB。在数据库中,B+树的高度一般都在2~4层,这意味着查找某一键值最多只需要2到4次IO操作,这还不错。因为现在一般的磁盘每秒至少可以做100次IO操作,2~4次的IO操作意味着查询时间只需0.02~0.04
秒。
从本质上来说,联合索引还是一棵B+树,不同的是联合索引的键值数量不是1,而是大于等于2,参考下图。另外,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,所以使用联合索引时遵循最左前缀集合。

hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。B+树底层实现是多路平衡查找树,对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。它们有以下的不同:
因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引!
聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。聚集索引存储记录是物理上连续存在,物理存储按照索引排序,而非聚集索引是逻辑上的连续,物理存储并不连续,物理存储不按照索引排序。
平时习惯逛图书馆的童鞋可能比较清楚,如果你要去图书馆借一本书,最开始是去电脑里面查书名然后根据书名来定位藏书在那个区,哪个书柜,哪一行,第多少本。。。清晰明确,一目了然,因为藏书的结构与图书室的位置,书架的顺序,书本的摆放顺序与书籍的编号都是从大到小一致的顺序摆放的,所以很容易找到。比如,你的目标藏书在C区2柜3排5仓,那么你走到B区你就很快知道前面就快到了C区了,你直接奔着2柜区就能找到了。 这就是雷同于聚簇索引的功效了,聚簇索引,实际存储的循序结构与数据存储的物理机构是一致的,所以通常来说物理顺序结构只有一种,那么一个表的聚簇索引也只能有一个,通常默认都是主键,设置了主键,系统默认就为你加上了聚簇索引,当然有人说我不想拿主键作为聚簇索引,我需要用其他字段作为索引,当然这也是可以的,这就需要你在设置主键之前自己手动的先添加上唯一的聚簇索引,然后再设置主键,这样就木有问题啦。
总而言之,聚簇索引是顺序结构与数据存储物理结构一致的一种索引,并且一个表的聚簇索引只能有唯一的一条;
同样的,如果你去的不是图书馆,而是某城市的商业性质的图书城,那么你想找的书就摆放比较随意了,由于商业图书城空间比较紧正,藏书通常按照藏书上架的先后顺序来摆放的,所以如果查询到某书籍放在C区2柜3排5仓,但你可能要绕过F区,而不是A.B.C.D…连贯一致的,也可能同在C区的2柜,书柜上第一排是计算机类的书记,也可能最后一排就是医学类书籍;
那么对照着来看非聚簇索引的概念就比较好理解了,非聚簇索引记录的物理顺序与逻辑顺序没有必然的联系,与数据的存储物理结构没有关系;一个表对应的非聚簇索引可以有多条,根据不同列的约束可以建立不同要求的非聚簇索引;
联合索引是指对表上的多个列进行索引,联合索引的创建方法与单个索引创建的方法一样,不同之处仅在于有多个索引列。从本质上来说,联合索引还是一棵B+树,不同的是联合索引的键值数量不是1,而是大于等于2,参考下图。另外,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,所以使用联合索引时遵循最左前缀集合!

索引是否起作用,主要取决于字段类型:
在MySQL中模糊查询 mobile like ‘%8765’ ,这种情况是不能使用 mobile 上的索引的,那么如果需
要根据手机号码后四位进行模糊查询,可以用一下方法进行改造。
我们可以加入冗余列(MySQL5.7之后加入了虚拟列,使用虚拟列更合适,思路相同),比如
mobile_reverse,内部存储为 mobile 的倒叙文本,如 mobile为17312345678,那么 mobile_reverse
存储 87654321371,为 mobile_reverse 列建立索引,查询中使用语句 mobile_reverse like
reverse(’%5678’) 即可。reverse 是 MySQL 中的反转函数,这条语句相当于 mobile_reverse like ‘8765%’ ,这种语句是可以使用索引的。
事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。在事务中的操作,要么都执行修改,要么都不执行,这就是事务的目的,也是事务模型区别于文件系统的重要特征之一。
事务需遵循ACID四个特性:
A(atomicity),原子性。原子性指整个数据库事务是不可分割的工作单位。只有使事务中所有的
数据库操作都执行成功,整个事务的执行才算成功。事务中任何一个SQL语句执行失败,那么已经
执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。
C(consistency),一致性。一致性指事务将数据库从一种状态转变为另一种一致的状态。在事务
开始之前和事务结束以后,数据库的完整性约束没有被破坏。
I(isolation),隔离性。事务的隔离性要求每个读写事务的对象与其他事务的操作对象能相互分
离,即该事务提交前对其他事务都不可见,这通常使用锁来实现。
D(durability) ,持久性。事务一旦提交,其结果就是永久性的,即使发生宕机等故障,数据库
也能将数据恢复。持久性保证的是事务系统的高可靠性,而不是高可用性。
事务可以分为以下几种类型:
对于MySQL的InnoDB存储引擎来说,它支持扁平事务、带有保存点的扁平事务、链事务、分布式事
务。对于嵌套事务,MySQL数据库并不是原生的,因此对于有并行事务需求的用户来说MySQL就无能为力了,但是用户可以通过带有保存点的事务来模拟串行的嵌套事务。
SQL 标准定义了四种隔离级别,这四种隔离级别分别是:
读未提交(READ UNCOMMITTED);
读提交 (READ COMMITTED);
可重复读 (REPEATABLE READ);
串行化 (SERIALIZABLE)
事务隔离是为了解决脏读、不可重复读、幻读问题,下表展示了 4 种隔离级别对这三个问题的解决程
度:

上述4种隔离级别MySQL都支持,并且InnoDB存储引擎默认的支持隔离级别是REPEATABLE READ,但是与标准SQL不同的是,InnoDB存储引擎在REPEATABLE READ事务隔离级别下,使用Next-Key Lock的锁算法,因此避免了幻读的产生。所以,InnoDB存储引擎在默认的事务隔离级别下已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE隔离级别。
并发情况下,读操作可能存在的三类问题:


可以,因为嵌套事务也是众多事务分类中的一种,它是一个层次结构框架。有一个顶层事务控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务,它控制每一个局部的变换。
需要注意的是,MySQL数据库不支持嵌套事务。
为了实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。
我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,事务 ID 记为
transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。如下图,一行记录现在有 3 个版本,每一个版本都记录这使其产生的事务 ID,比如事务A的transactionid 是100,那么版本1的row trx_id 就是 100,同理版本2和版本3。

可重复读是在事务开始的时候生成一个当前事务全局性的快照。对于一个快照来说,它能够读到那些版
本数据,要遵循以下规则:
在MySQL默认的配置下,事务都是自动提交和回滚的。当显示地开启一个事务时,可以使用ROLLBACK语句进行回滚。该语句有两种用法:
锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。下面我们以MySQL数据库的InnoDB引擎为例,来说明锁的一些特点。
锁的类型:
InnoDB存储引擎实现了如下两种标准的行级锁:
如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为读取并没有改变行r的数据,称这种情况为锁兼容。但若有其他的事务T3想获得行r的排他锁,则其必须等待事务T1、T2释放行r上的共享锁,这种情况称为锁不兼容。下图显示了共享锁和排他锁的兼容性,可以发现X锁与任何的锁都不兼容,而S锁仅和S锁兼容。需要特别注意的是,S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。

InnoDB存储引擎有3种行锁的算法,间隙锁(Gap Lock)是其中之一。间隙锁用于锁定一个范围,但不包含记录本身。它的作用是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生。
InnoDB行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
当表中锁定其中的某几行时,不同的事务可以使用不同的索引锁定不同的行。另外,不论使用主键索
引、唯一索引还是普通索引,InnoDB都会使用行锁来对数据加锁。
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力作用,事务都将无法推进下去。下图演示了死锁的一种经典的情况,即A等待B、B等待A,这种死锁问题被称为AB-BA死锁。

解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息:
MySQL数据库优化是多方面的,原则是减少系统的瓶颈,减少资源的占用,增加系统的反应速度。- - 例如,通过优化文件系统,提高磁盘I\O的读写速度;通过优化操作系统调度策略,提高MySQL在高负荷情况下的负载能力;优化表结构、索引、查询语句等使查询响应更快。
如果查询时没有使用索引,查询语句将扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。如果使用索引进行查询,查询语句可以根据索引快速定位到待查询记录,从而减少查询的记录数,达到提高查询速度的目的。
索引可以提高查询的速度,但并不是使用带有索引的字段查询时索引都会起作用。有几种特殊情况,在这些情况下有可能使用带有索引的字段查询时索引并没有起作用。
优化子查询:
使用子查询可以进行SELECT语句的嵌套查询,即一个SELECT查询的结果作为另一个SELECT语句的条件。子查询可以一次性完成很多逻辑上需要多个步骤才能完成的SQL操作。子查询虽然可以使查询语句很灵活,但执行效率不高。
执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。因此,子查询的速度会受到一定的影响。如果查询的数据量比较大,这种影响就会随之增大。
在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引,性能会更好。
影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。针对这些情况,可以分别进行优化。
对于MyISAM引擎的表,常见的优化方法如下:
对于InnoDB引擎的表,常见的优化方法如下:
建议按照如下顺序进行优化:
优化MySQL的慢查询,可以按照如下步骤进行:
开启慢查询日志:
MySQL中慢查询日志默认是关闭的,可以通过配置文件my.ini或者my.cnf中的log-slow-queries选项打
开,也可以在MySQL服务启动的时候使用 --log-slow-queries[=file_name] 启动慢查询日志。
启动慢查询日志时,需要在my.ini或者my.cnf文件中配置long_query_time选项指定记录阈值,如果某
条查询语句的查询时间超过了这个值,这个查询过程将被记录到慢查询日志文件中。
分析慢查询日志:
直接分析mysql慢查询日志,利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句。
MySQL中提供了EXPLAIN语句和DESCRIBE语句,用来分析查询语句,EXPLAIN语句的基本语法如下:
EXPLAIN [EXTENDED] SELECT select_options
使用EXTENED关键字,EXPLAIN语句将产生附加信息。执行该语句,可以分析EXPLAIN后面SELECT语句的执行情况,并且能够分析出所查询表的一些特征。下面对查询结果进行解释:
id:SELECT识别符。这是SELECT的查询序列号。
select_type:表示SELECT语句的类型。
table:表示查询的表。
type:表示表的连接类型。
possible_keys:给出了MySQL在搜索数据记录时可选用的各个索引。
key:是MySQL实际选用的索引。
key_len:给出索引按字节计算的长度,key_len数值越小,表示越快。
ref:给出了关联关系中另一个数据表里的数据列名。
rows:是MySQL在执行这个查询时预计会从这个数据表里读出的数据行的个数。
Extra:提供了与关联操作有关的信息
DESCRIBE语句的使用方法与EXPLAIN语句是一样的,分析结果也是一样的,并且可以缩写成DESC。DESCRIBE语句的语法形式如下:
DESCRIBE SELECT select_options


目前关系数据库有六种范式,一般来说,数据库只需满足第三范式(3NF)就行了。
MySQL提供了多个不同的存储引擎,包括处理事务安全表的引擎和处理非事务安全表的引擎。在MySQL中,不需要在整个服务器中使用同一种存储引擎,针对具体的要求,可以对每一个表使用不同的存储引擎。MySQL 8.0支持的存储引擎有InnoDB、MyISAM、Memory、Merge、Archive、Federated、CSV、BLACKHOLE等。其中,最常用的引擎是InnoDB和MyISAM。
InnoDB存储引擎:
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键。MySQL 5.5.5之后,InnoDB作为默认存储引擎,主要特性如下:
MyISAM存储引擎:
MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务。MyISAM的主要特性如下:
6. 在支持大文件(达63位文件长度)的文件系统和操作系统上被支持。
7. 当把删除和更新及插入操作混合使用的时候,动态尺寸的行产生更少碎片。这要通过合并相邻被删
除的块以及若下一个块被删除则扩展到下一块来自动完成。
8. 每个MyISAM表最大的索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16个。
9. 最大的键长度是1000B,这也可以通过编译来改变。对于键长度超过250B的情况,一个超过1024B
的键将被用上。
10. BLOB和TEXT列可以被索引。
11. NULL值被允许在索引的列中,这个值占每个键的0~1个字节。
12. 所有数字键值以高字节优先被存储,以允许一个更高的索引压缩。
13. 每个表一个AUTO_INCREMENT列的内部处理。MyISAM为INSERT和UPDATE操作自动更新这一列,这使得AUTO_INCREMENT列更快(至少10%)。在序列顶的值被删除之后就不能再利用。
14. 可以把数据文件和索引文件放在不同目录。
15. 每个字符列可以有不同的字符集。
16. 有VARCHAR的表可以固定或动态记录长度。
17. VARCHAR和CHAR列可以多达64KB。
InnoDB默认的隔离级别是RR(REPEATABLE READ),RR解决脏读、不可重复读、幻读等问题,使用的是MVCC。MVCC全称Multi-Version Concurrency Control,即多版本的并发控制协议。它最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC,多个版本的数据可以共存,主要基于以下技术及数据结构:
复制(replication)是MySQL数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。总体来说,replication的工作原理分为以下3个步骤:
复制的工作原理并不复杂,其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是,复制不是完全实时地进行同步,而是异步实时。这中间存在主从服务器之间的执行延时,如果主服务器的压力很大,则可能导致主从服务器延时较大。复制的工作原理如下图所示,其中从服务器有2个线程,一个是I/O线程,负责读取主服务器的二进制日志,并将其保存为中继日志;另一个是SQL线程,复制执行中继日志。
