查询语句总结:
select 5
...
from 1
...
where 2
...
group by 3 # 在 SQL 语句中若有 group by 语句,那么在 select 语句后面只能跟分组函数+参与分组的字段。
...
having 4
...
order by 6
...
limit 7
...;
以上语句的执行顺序为:
原则: 能在 where 中过滤的数据,尽量在 where 中过滤,效率较高。having 的过滤是专门对分组之后的数据进行过滤的。
MySQL 的分页语法:
使用 LIMIT 语句,后边有两个参数,第一个为行号,第二个为查询几个
-- 在所有的查询结果中,返回前5行记录。
SELECT prod_name FROM products LIMIT 5;
-- 在所有的查询结果中,从第5行开始,返回5行记录。
SELECT prod_name FROM products LIMIT 5,5;
优化 LIMIT 分页:
方法:使用覆盖索引,通过索引快速找到所搜的行数,然后再查找所有。
SELECT film.film_id,film.description
FROM sakila.film
INNER JOIN (
SELECT film_id FROM sakila.film ORDER BY title LIMIT 50,5 # 根据字段film_id创建的索引
) AS lim USING(film_id);
函数名 | 作用 |
---|---|
count() | 取得记录数 |
sum() | 求和 |
avg() | 取平均数 |
max() | 取最大的数 |
min() | 取最小的数 |
注意:分组函数自动忽略空值,不需要手动的加 where 条件排除空值。
在 SQL 语句中若有 group by 语句,那么在 select 语句后面只能跟分组函数+参与分组的字段。
总共两种方式:内连接和外连接。
内连接:
外连接:
常见的关联关系: 一对多关联、多对多关联、自关联。
两种方式:
CASE...WHEN...THEN
语句实现行转列IF()
函数实现行转列原理: 客户端通过将 SQL 代码伪装到输入参数中,传递到服务器解析,服务端在执行 SQL 操作时,会拼接对应参数,同时也将一些 SQL 注入攻击的 “SQL” 拼接起来,导致会执行一些预期之外的操作。
解决办法:
作用:可以节省 SQL 查询时间,以及 MySQL 服务器的资源。达到一次编译、多次执行的目的。
主要采用关联更新的方式,将一张表的部分数据,更新到另一张表内。
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: 是一个约束声明
HAVING: 是一个过滤声明
总结: 从性能角度看,当使用分组字段作为过滤条件时,应尽量使用 WHERE 子句,而不使用 HAVING 子句。
概念: 索引是一个单独的、存储在磁盘上的数据库结构,包含着对数据表里所有记录的引用指针。所有 MySQL 列类型都可以被索引,对相关列使用索引是提高查询操作速度的最佳途径。
索引与存储引擎: 索引是在存储引擎中实现的,因此,每种存储引擎的索引都不一定完全相同,并且每种存储引擎也不一定支持所有索引类型。MySQL 总共有四种存储引擎,支持两种索引类型(BTREE 和 HASH)。
索引的优点:
索引的缺点:
在创建表的时候创建索引:
CREATE TABLE table_name [colname data_type]
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name [length]) [ASC|DESC]
其中,UNIQUE、FULLTEXT 和 SPATIAL 为可选参数,分别表示唯一索引、全文索引和空间索引;INDEX 与 KEY 为同义词,两者作用相同,用来指定创建索引。
例如:
CREATE TABLE t1 (
id INT NOT NULL,
name CHAR(30) NOT NULL,
UNIQUE INDEX UniqIdx(id)
);
在已存在的表上创建索引: 总共两种方法 ALTER TABLE 或者 CREATE INDEX 语句
ALTER TABLE:
ALTER TABLE t_name ADD
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name [length]...) [ASC|DESC]
例如:
ALTER TABLE book ADD UNIQUE INDEX UniqidIdx (bookId);
CREATE INDEXS:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
ON t_name (col_name [length]...) [ASC|DESC]
例如:
CREATE UNIQUE INDEX UniqidIdx ON book (bookId);
不一定,会发挥索引失效问题,例如在使用组合索引的时候,如果没有遵从"最左前缀"的原则进行搜索,索引就会失效。
总结: 所有三种情况都是在第一个字段不相等的情况下,去查第二个字段,这样就会导致索引失效。
主要使用 EXPLAIN 语句来查看索引是否正在使用。
EXPLAIN 语句将为我们输出详细的 SQL 执行信息,其中:
索引并非越多越好,一个表中如果有大量的索引,不仅占用磁盘空间,还会影响 INSERT、DELETE、UPDATE 等语句的性能,因为在表中的数据更改时,索引也会进行调整和更新。
不是。
下列几种情况,是不适合创建索引的:
在 MySQL 中,索引是在存储引擎层实现的,不同存储引擎对索引的实现方式是不同的。
MyISAM 索引实现:
InnoDB 索引实现:
什么时候需要重建索引?
怎么判断索引是否应该重建?
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 index_name rebuild;
alter index index_name rebuild online;
rebuild 是快速创建索引的一种有效的办法,因为它是一种使用现有索引项来重建新索引的方法。如果重建索引时有其他用户在对这个表操作,尽量使用带 online 参数来最大限度的减少索引重建时将会出现的任何加锁问题。rebuild 重建索引的过程:
重建索引过程中的注意事项:
几个要明确的(假设):
两层:第一层一页,能存储 16*1024/14 = 1170 个,即有1170个叶子节点,每一页能能存储 16K/1K = 16条记录,则总共能存储 1170 * 16 = 18720 条记录。
三层:原理同第二层,则能存储 1170 * 1170 * 16 = 21902400 条记录,达到千万级。
它的键值数量不是 1,而是大于等于 2,实际上可以看成是多个特征的拼接。要遵循最左前缀原则。
Hash 索引: 进行查找时,调用一次 hash 函数就可以获取到相应的键值,之后进行回表查询获得实际数据。
B 树索引: 每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。
不同点:
总结: 归根结底还是 hash 表和 B+ Tree 底层数据结构的原因。
在 InnoDB 存储引擎中,可以将 B+ 树索引分为聚簇索引和辅助索引(非聚簇索引)。无论是何种索引,每个页的大小都为 16 KB,且不能更改。
聚簇索引: 根据主键创建的一棵 B+ 树,聚簇索引的叶子节点存放了完整的数据记录。
辅助索引: 根据索引键创建的一棵 B+ 树,其叶子结点金存放索引键以及该索引键指向的主键。所以查找时需要进行两次索引。(1) 检查辅助索引获得主键。(2) 用主键到主索引中检索获得记录。
索引是否起作用,主要取决于字段类型:
使用 like 非左缀(1%)的情况,右缀(%1)和中缀(%1%)都会失效
概念: 事务由一条或者一组复杂的 SQL 语句组成。在事务中的操作,要么都执行修改,要么都不执行,这就是事务的目的,也是事务模型区别于文件系统的重要特征之一。
事务的四大特性(ACID)
事务可以分为以下几种类型:
主要有五种类型,扁平事务、带有保存点的扁平事务、链事务、嵌套事务和分布式事务。
对于 MySQL 的 InnoDB 存储引擎来说,它支持扁平事务、带有保存点的扁平事务、链事务和分布式事务。对于嵌套事务,MySQL 数据库并不是原生的,因此对于有平行事务需求的用户来说 MySQL 就无能为力了,但是用户可以通过带有保存点的事务来模拟串行的嵌套事务。
原子性:
主要依靠undo.log日志实现,它会存储事务期间对数据库进行的DML操作,这样在事务失败进行回滚的时候,数据库就可以根据undo.log中的记录,进行方向回滚数据库状态。
持久性:
主要依靠redo.log日志实现。首先,mysql 持久化是通过缓存来提高访问速度,即在select时先查看缓存,再查磁盘;在update时先更新缓存,再更新磁盘。以此来减少磁盘 io 次数,提高访问速度,但这样有问题,就是当断电的时候缓存中的数据就没有了。 通过redo.log日志就可以解决这个问题,在执行update时,先写入到redo.log日志,再写入缓存,这样即使断电,也能保证数据不丢失,达到持久性。
为什么 redo.log(存在磁盘中)会比从缓冲区读取速度快?
隔离性:
一致性:
事务执行前后,数据库的完整性约束没有被破坏。可以说,一致性是事务追求的最终目标。前面提到的原子性、隔离性、持久性都是为了保证数据库状态的一致性。具体措施包括:
并发情况下,读操作可能出现三类问题:
SQL 标准定义了四种隔离级别,分别是:
事务隔离级别就是为了解决脏读、不可重复读和幻读,具体如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 可能 | 可能 | 可能 |
READ COMMITTED(RC) | 不可能 | 可能 | 可能 |
REPEATABLE READ(RE) | 不可能 | 不可能 | 可能 |
SERIALIZABLE | 不可能 | 不可能 | 不可能 |
InnoDB 支持的默认隔离级别是可重复读。使用的 Next-Key Lock 的锁算法,因此也避免了幻读的产生。
目的: 主要是为了提高数据库的并发性能。
最大的优点: 它最大的优点是读不加锁,因此读写不冲突,并发性能好。
总共有三部分组成。隐藏列、基于 undo log 的版本链和 ReadView。
总结:
隐藏列和版本链的目的就是能够找到每个记录不同版本的数据,能不能读主要看 ReadView 和当前数据的事务版本比较,比较的核心在于我当前事务执行的时候,这个版本的事务是不是已经提交了,如果提交就读取,否则去更早的版本。
可以,因为嵌套事务也是众多事务分类中的一种,它是一个层次结构框架。有一个顶层事务控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务,它控制每一个局部的变换。
在 MySQL 默认的配置下,事务都是自动提交和回滚的。当显式地开启一个事务时,可以使用 ROLLBACK 语句进行回滚。该语句有两种用法:
行锁:
假设现在有事务a和事务b,假设a修改了表中的一行数据,如果a还没有commit,这个时候如果b也去修改这条数据,那么就会发生阻塞,直到a提交之后,b才能修改,或者b的这条DML超时。
表锁:
假设现在有事务a和事务b,假设a修改了表中的一行数据,如果它这条DML导致了表索引失效,那么这个时候就会转换为表锁,事务b不能够对这个表进行任何修改。
间歇锁(解决幻读):
假设下现在有事务a和事务b,表t有字段tno编号是不连续的,例如1、3、5、7;如果a操作了t中tno的between 1 and 5,这个时候b如果插入tno = 4 的时候就会发生间歇锁,插入不进去。
概念: 锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。
锁的类型:
锁的算法:
InnoDB 存储引擎有三种行锁的算法,其分别是:
关于死锁:
锁的升级: 锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说,数据库可以把一个表的 1000 个行锁升级为一个页锁,或者将页锁升级为表锁。
间歇锁用于锁定一个范围,但不包含记录本身。它的作用是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生。
InnoDB 行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁。
MySQL 数据库优化是多方面的,原则是减少系统的瓶颈,减少资源的占用,增加系统的反应速度。
影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。
对于 MyISAM 引擎,常见的优化方法如下:
对于 InnoDB 引擎的表,常见的优化方法如下:
目的: EXPALIN 是用来分析查询语句的。
重点关注如下:
列名 | 备注 |
---|---|
type | 表示表的连接类型 |
key | 是 MySQL 实际选用的索引 |
key_len | 本次查询用于过滤的索引实际长度 |
rows | 这次查询从数据表里读出的数据行的个数 |
Extra | 提供了与关联操作有关的信息 |
type包含的结果,从上至下依次是由差到好:
类型 | 备注 |
---|---|
ALL | 执行full table scan,这是最差的一种方式。 |
index | 执行full index scan,并且可以通过索引完成结果扫描并且直接从索引中取的想要的结果数据,也就是可以避免回表,比ALL略好,因为索引文件通常比全部数据要来的小。 |
range | 利用索引进行范围查询,比index略好。 |
ref | 基于索引的等值查询,或者表间等值连接。 |
const | 基于主键或唯一索引唯一值查询,最多返回一条结果。 |
system | 查询对象表只有一行数据,这是最好的情况。 |
第一范式:
原子性:保证每一列不可再分
要求数据库表中的每一列都是不可分割的原子数据项。
第二范式:
前提:满足第一范式
每张表只满足一件事情。
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。
第三范式:
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
MySQL 提供了多个不同的存储引擎,包括处理事务安全表的引擎和处理非事务安全表的引擎。
MySQL 支持的引擎有:InnoDB、MyISAM、Memory、Merge、Archive等
InnoDB 存储引擎: MySQL 5.5.5 之后,作为默认存储引擎。
MyISAM 存储引擎:
binlog(Binary Log):
二进制日志文件就是常说的 binlog。二进制日志记录了 MySQL 所有修改数据库的操作,然后以二进制的形式记录在日志文件中,其中还包括每条语句所执行的时间和所消耗的资源,以及相关的事务信息。
redo log:
重做日志用来实现事务的持久性,即事务 ACID 中的 D。它由两部分组成:一是内存中的重做日志缓存(redo log buffer),其是易失的;二是重做日志文件(redo log file),它是持久的。
undo log:
重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要 undo。
redo log 和 undo log:
复制(replication)是 MySQL 数据库提供的一种高可用高性能的解决方案,一般用于建立大型的应用。总体来说,replication 的工作原理分为以下 3 个步骤:
要注意的是,复制不是完全实时地进行同步,而是异步实时
复制的工作原理是,从服务器有两个线程,一个是 I/O 线程,负责读取主服务器的二进制日志,并将其保存为中继日志;另一个是 SQL 线程,复制执行中继日志。
功能: 三者都可以用来删除数据。
执行速度: drop > truncate > delete
不同点:
总结: 一个很形象的比喻,对于一本书,delete 是把目录撕了,truncate 是把书的内容撕下来烧了,drop 是把书烧了。