目录
无过滤不索引编辑using filesort 说明进行了手工排序!原因在于没有 where 作为过滤条件!编辑
行锁(偏写)
SQL的执行流程
七种join
但是mysql中不支持full outer join的语法
对于
代替如下,可以用left join 和 right join结合来实现
select * from tbl_1 a left join tbl_2 b on a.deptId=b.id
union
select * from tbl_1 a right join tbl_2 b on a.deptId=b.id
对于
select * from tbl_1 a left join tbl_2 b on a.deptId=b.id where b.id is null
union
select * from tbl_1 a right join tbl_2 b on a.deptId=b.id where a.deptId is null
索引
1、概念
优缺点
- 提高数据检索的效率,降低数据库的IO成本。
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
- 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
2. Mysql 的索引
2.1 Btree 索引
2.2 B+tree 索引
3、索引的分类
3.1单值索引
一个索引质值包含但个列,一个表可以有多个单值索引
3.2唯一索引
索引列的值必须唯一,但允许为空值
3.3复合索引
一个索引包含了多个列
3.4基本语法
创建 |
CREATE [UNIQUE ] INDEX [indexName] ON table_name(column))
|
删除 |
DROP INDEX [indexName] ON mytable;
|
查看 |
SHOW INDEX FROM table_name\G
|
使用alter命令 |
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list)
:
该语句添加一个主键,这意味着索引值必须是唯一
的,且不能为
NULL
。
|
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list)
| |
ALTER TABLE tbl_name ADD INDEX index_name (column_list):
添加普通索引,索引值可出现多次。
| |
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):
该语句指定了索引为
FULLTEXT
,用于全文索引。
|
4. 索引的创建时机
4.1 适合创建索引的情况
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引
- 查询中与其它表关联的字段,外键关系建立索引
- 单键/组合索引的选择问题, 组合索引性价比更高
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
- 查询中统计或者分组字段
4.2 不适合创建索引的情况
- 表记录太少
- 经常增删改的表或者字段
- Where 条件里用不到的字段不创建索引
- 过滤性不好的不适合建索引
- 重复且平均分配
Explain 性能分析
1. 概念
2.mysql常见瓶颈
- CPU:CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
- IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
- 服务器硬件瓶颈:top,free,iostat和vmstat来查看系统的性能状态
3.Explain
3.1 id
id相同,执行顺序由上至下
id 不同,id 不同,如果是子查询,id 的序号会递增,id 值越大优先级越高,越先被执行
有相同也有不同
id 如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id 值越大,优先级越高,越先执行衍生 = DERIVED
i d 号每个号码,表示一趟独立的查询。一个 sql 的查询趟数越少越好。
3.2select_type
3.3type
system > const > eq_ref > ref > range > index >ALL
(1)system
表只有一行记录(等于系统表),这是 const 类型的特列,平时不会出现,这个也可以忽略不计
(2)const
(3)eq_ref
(4)ref
没用索引前:
建立索引后:
(5)range
(6)index
(7)ALL
3.4possible_keys
3.5key
查询中若使用了覆盖索引,则该索引仅出现在key列表中
3.6key_len
显示的是索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索得出的
3.7ref
3.8rows
3.8extra
(1)Using filesort
优化后:order by字段最好和所建立的索引的包含列的个数和顺序一致
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
(2)Using temporary
优化后:group by字段最好和所建立的索引的包含列的个数和顺序一致
(3)Using index
where 子句的值总是 false,不能用来获取任何元组。
(7)select tables optimized away
索引优化
单表优化案例
type:all,全表扫描,且有文件排序
优化1:为id,comment,views建立一个复合索引
type:range,但是文件类排序还存在,范围以后索引会失效
先排序id,遇到相同的id再排序comment,遇到相同的comment再排序views,由于comment处于中间位置,且条件是一个range,mysql无法利用索引再对后面的views进行检索
优化2:为id,views及建立一个复合索引
两表优化案例
left join查询,左表全有,一定要为右表建立索引
right也是一样的
全值匹配
三条语句
查询的字段按照顺序在索引中都可以匹配到
SQL 中查询字段的顺序,跟使用索引中字段的顺序,没有关系。优化器会在不影响 SQL 执行结果的前提下,给你自动地优化。
最佳左前缀法则
查询字段与索引字段顺序的不同会导致,索引无法充分使用,甚至索引失效!原因:使用复合索引,需要遵循最佳左前缀法则,即如果索引了多列,要遵守最左前缀法则。指的是查询从索引的 最左前列开始并且不跳过索引中的列 。结论:过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无 法被使用。
不要在索引列上做任何计算
1、在查询列上使用了函数
结论:等号左边无计算!
2、在查询列上做了转换
结论:等号右边无转换!
字符串类型不能失去单引号
索引列上不能有范围查询
建议:将可能做范围查询的字段的索引顺序放在最后
尽量使用覆盖索引
使用不等于(!= 或者<>)的时候
字段的 is not null 和 is null
is not null 用不到索引,is null 可以用到索引
like 的前后模糊匹配
前缀不能出现模糊匹配!
如果非要用%*%查询,可以用覆盖索引,建的索引和查的字段个数、顺序最好一致
建立一个name,age的复合索引,下面的索引都不会失效
减少使用 or
使用 union all 或者 union 来替代
口诀
全值匹配我最爱,最左前缀要遵守;带头大哥不能死,中间兄弟不能断;索引列上少计算,范围之后全失效;LIKE 百分写最右,覆盖索引不写 * ;不等空值还有 OR ,索引影响要注意;VAR 引号不可丢, SQL 优化有诀窍
小表驱动大表
小的数据集驱动大的数据集
select * from A where id in (select id from B)
等价于:
for select id from B
for select * from A where A.id=B.id
当B的数据集小于A的数据集时,用in优于exists
select * from A where exists (select 1 from B where B.id=A.id)
等价于:
for select * from A
for select * from B where B.id=A.id
当A的数据集小于B的数据集时,用exists优于in
exists:将主查询的数据放到子查询做条件验证,根据验证结果(TRUE或FALSE)来决定主查询的数据结果是否得以保留
排序分组优化
orderby 优化
无过滤不索引
using filesort 说明进行了手工排序!原因在于没有 where 作为过滤条件!
结论: 无过滤,不索引。 where , limt 都相当于一种过滤条件,所以才能使用上索引!
顺序错,必排序
首先为没有建立索引的字段排序,必然会触发filesort
deptid 作为过滤条件的字段,无法使用索引,因此排序没法用上索引
方向反,必排序
如果可以用上索引的字段都使用正序或者逆序,实际上是没有任何影响的,无非将结果集调换顺序。
如果排序的字段,顺序有差异,就需要将差异的部分,进行一次倒置顺序,因此还是需要手动排序的!
索引的选择
发现 using filesort 依然存在。原因: empno 是范围查询,因此导致了索引失效,所以 name 字段无法使用索引排序。所以,三个字段的符合索引,没有意义,因为 empno 和 name 字段只能选择其一!
解决: 鱼与熊掌不可兼得,因此,要么选择 empno,要么选择 name
那么具体选择哪个索引呢
所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。 相对的 empno<101000 这个条件如果没有用到索引的话,要对几万条的数据进行扫描,这是非常消耗性能的,使用 empno 字段的范围查询,过滤性更好
结论: 当范围条件和 group by 或者 order by 的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。
using filesort
使用覆盖索引
group by
截取查询分析
1、慢查询日志
1.1是什么
1.2 怎么用
SQL语句 | 描述 | 备注 |
---|---|---|
SHOW VARIABLES LIKE '%slow_query_log%';
|
查看慢查询日志是否开启
|
默认情况下
slow_query_log
的值为
OFF
,
表示慢查询日志是禁用的
|
set global slow_query_log=1;
|
开启慢查询日志
| |
SHOW VARIABLES LIKE 'long_query_time%';
|
查看慢查询设定阈值
|
单位秒
|
set long_query_time=1
|
设定慢查询阈值
|
单位秒
|
[mysqld]slow_query_log=1slow_query_log_file=/var/lib/mysql/atguigu-slow.loglong_query_time=3log_output=FILE
1.3日志分析工具 mysqldumpslow
(2)查看mysqldumpslow的帮助信息
得到返回记录集最多的 10 个 SQLmysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log得到访问次数最多的 10 个 SQLmysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log得到按照时间排序的前 10 条里面含有左连接的查询语句mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log另外建议在使用这些命令时结合 | 和 more 使用 ,否则有可能出现爆屏情况mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
show profile
开启 profile
使用 profile
注意这些问题
mysql 的查询流程大致是:mysql 客户端通过协议与 mysql 服务器建连接,发送查询语句,先检查查询缓存,如果命中,直接返回结果,否则进行语句解析, 也就是说,在解析查询之前,服务器会先访问查询缓存 (query cache) ——它存储 SELECT 语句以及相应的查询结果集。如果某个查询结果已经位于缓存中,服务器就不会再对查询进行解析、优化、以及执行。它仅仅将缓存中的结果返回给用户即可,这将大大提高系统的性能。语法解析器和预处理:首先 mysql 通过关键字将 SQL 语句进行解析,并生成一颗对应的“解析树”。 mysql 解析器将使用 mysql 语法规则验证和解析查询;预处理器则根据一些 mysql 规则进一步检查解析数是否合法。查询优化器当解析树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。然后,mysql 默认使用的 BTREE 索引,并且一个大致方向是 : 无论怎么折腾 sql ,至少在目前来说, mysql 最多只用到表中的一个索引。
批量数据脚本
编写随机函数
1、随机产生字符串
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END $$
如果要删除函数:drop function rand_string;
2、随机产生部门编号
#用于随机产生多少到多少的编号
DELIMITER $$
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num -from_num+1)) ;
RETURN i;
END$$
创建存储过程
创建往 emp 表中插入数据的存储过程
DELIMITER $$
CREATE PROCEDURE insert_emp( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
--set autocommit =0 把 autocommit 设置成 0
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6) , rand_num(30,50),rand_num(1,10000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END$$
删除:drop PROCEDURE insert_emp;
调用存储过程
DELIMITER ;
CALL insert_emp(100,10);
锁的机制
从数据操作的类型可分为:
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行不会相互影响
- 写锁(排它锁):当前写操作没有完成之前,它会阻断其他写锁和读锁
从对数据操作的粒度可分为:
- 表锁
- 行锁
1、三锁
表锁(偏读)
偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁粒度大,发生锁冲突的概率高,并发最低
(1)加锁
LOCK TABLE t1 READ, Book WRITE;
查看锁:SHOW OPEN TABLES
SHOW STATUS LIKE 'table%'
Table_locks_immediate:产生表级锁的次数,表示可以立即获得锁的查询次数,每立即获得锁值加1
Table_locks_waited:出现表级锁争用二发生等待时间的次数(不能立即释放锁的次数,每等待一次锁值加1),此值高则说明存在较为严重的表级锁争用情况
(2)解锁
UNLOCK TABLES
1.1加读锁
当前线程为表A加了表读锁,当前线程可以读表,但是不能写表,且不能读其他表;其他线程可以读表A,但不能写表A(会被阻塞,只有锁释放了才会放行)
1.2加写锁
当前线程为表A 加了表写锁,当前线程可以写表,读表,但是不能读其他表;其他线程不可以读/写表
行锁(偏写)
偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁粒度最小,发生锁冲突的概率最低,并发也最高
InnoDB相比MyISAM,一方面支持事务,另一方面采用行级锁
事务的ACID属性
并发事务处理带来的问题:
- 更新丢失
多个事务,更新同一行,相互之间不知道彼此的存在,最后更新的事务就会覆盖其他事务所做的更新 - 脏读
事务A读到了事务B已修改但未提交的数据,若B回滚,则出现脏读 - 不可重复读
事务A可能多次读取哦同一个数据,可能数据被修改,此时所读的数据与之前不一致 - 幻读
行锁案例
(1)基本演示
关闭事务自动提交SET autocommit=0
当前事务对锁住的行所做的修改对自己是可见的
对其他事务是不可见的
且对该行的修改会被阻塞
(2)索引失效行锁变表锁
varchar类型去掉单引号使索引失效
其他事务对该表其他行的操作也会被阻塞
(3)间隙锁的危害
当用范围条件而不是相等条件检索数据,并请求共享或排它锁是,InnoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但并不存在的记录,叫做 间隙(GAP)
InnoDB会给这个间隙加锁,称为间隙锁
此时其他事务想要插入一条该区间的数据会被阻塞
如何锁定一行
其他事务对改行的修改会被阻塞