综上,MyISAM引擎适合读多写少的情况,而大量的写操作则需要用InnoDB来保证性能和数据的准确性。而InnoDB也是MySQL5.5之后的默认引擎
SELECT DISTINCT
FROM tbleName1
[INNER|LEFT|RIGHT] JOIN tableName2
ON
[连接条件]
WHERE
[筛选条件]
GROUP BY
[分组条件]
HAVING
[分组后筛选条件]
ORDER BY [排序字段] [ASC|DESC]
LIMIT
[起始索引数,每页条目数]
[size*(page-1),size]
MySQL执行SQL的顺序从From开始,以下是执行的顺序流程
FROM->WHERE->GROUP BY->HAVING->SELECT->DISTINCT->ORDER BY->LIMIT
所有的查询语句都是从from开始执行的,在执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。
SQL慢是指执行时间长或等待时间长,原因是
排好序的、用于快速查找的数据结构。在数据之外,数据库还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。对于我们Java程序员来说,这种索引就是B TREE索引(多路搜索树)
索引的优点
索引的缺点
索引的种类
#创建索引
#对于创建索引时如果是blob和text类型,必须指定length
CREATE [UNIQUE] INDEX indexName ON tableName(columnName1,columnName2)
ALTER TABLE tableName ADD [UNIQUE] INDEX [indexName] ON (columnName)
#删除索引
DROP INDEX indexName ON tableName
#查看索引
SHOW INDEX FROM tableName
左连接建右表
多表建立连接的时候,左连接将索引建在右表的主外键上,右连接将索引建立在左表的主外键上。因为左连接的时候左表的数据全部都有,而右表的数据需要筛选,也就是说主表是一定需要全表扫描的,而子表不需要,所以索引建在子表上
哪些情况要创建索引
哪些情况不要创建索引
约定一般来说一张表不要建立超过5个索引
使用EXPLAIN来模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析查询语句的性能瓶颈
#使用方法 EXPLAIN + 目标SQL
EXPLAIN SELECT * FROM human
id
SELECT的查询序号,表示查询中执行的SELECT子句或者操作表的顺序。id如果相同的话,自上而下执行;id不同的话,id越大越优先执行
select type
table
显示这一行数据是关于那一个表的
type
重要参数,主要根据这个来判断一条SQL的好坏
possible_keys
可能用到的索引,一个或者多个,查询涉及到的字段只要存在索引就会被列出,但不一定会被使用
key
实际使用的索引,不一定在possible_keys中存在,如果是null则没有使用索引
key_len
表示使用的索引的长度,可以用来判断多列索引中的哪些部分被使用到了。在不损失精确性的情况下,越短越好
ref
匹配时使用到的列或者常数,如果值是func,表示匹配时用到了由函数生成的值(±、*/也算)。如果要看使用到了哪些函数,可以在explain语句执行后使用show warnings
rows
检查的行数,Innodb引擎是个估计值
Extra
包含不适合在其它列中显示但十分重要的信息
我们在Java中写两个嵌套的循环,循环次数是两个循环的乘积,所以内层循环和外层循环的顺序没有关系。但是在MySQL中,每执行一次循环就需要建立一次连接,所以要求的是外层循环次数要比内层循环次数要小,也就是我们常说的小表驱动大表,大表指的是数据多的表,查询相当于是遍历这个表,数据越多遍历次数越多
SELECT * FROM A WHERE ID IN(SELECT ID FROM B);
这种情况会先去查询B表,再去查询A表,也就是说相当于先遍历B再遍历A,那么当B表的数据绝对小于A表的时候使用这种方式
SELECT * FROM A EXISTS( SELECT existsFlag FROM B WHERE A.ID=B.ID)
这种情况会先去查询A表,再去查询B,相当于先遍历A再去遍历B,当A表的数据绝对小于BB表的时候使用这种方式
exists关键字是将主查询的数据放在子查询中进行验证,根据验证结果来决定主查询的数据是否保留。exists只会返回一个boolean值所以select后面写什么没关系
MySQL支持两种排序方式Filesort和Index,Index的效率高,它是指MySQL扫描索引本身完成排序,Filesort的效率低ORDER BY尽量使用Index方式进行排序,避免使用FileSort的方式,如果不在索引列上,FileSort有两种算法,双路排序和单路排序
但是因为单路排序需要更大的磁盘空间,如果sort_buffer_size的大小不足,可能会导致两次甚至更多次的IO次数
使用单路排序的条件
当我们只能使用FileSort的时候我们可以通过增大sort_buffer和增大max_length_for_sort_data这两个属性来提高SQL效率max_length_for_sort_data是指查询列中除去text和blob类型,其他字段大小的和,所以我们当使用了排序的时候,尽量不要使用SELECT *,只查询需要用到的字段
如何保证尽量使用Index排序
GROUP BY的本质是先排序,后分组,所以遵循和ORDER BY一样的原则,只需要注意一点,当可以在WHERE中添加的限定条件,不要写在HAVING中
MySQL的慢查询日志是MySQL提供的一种日志记录,默认关闭,用来记录MySQL中响应时间超过阈值的语句,具体时间超过long_query_time则被记录到慢查询日志中,long_query_time的默认值是10,意思是运行10秒以上的语句
#查询慢查询日志是否开启
SHOW VARIABLES LIKE '%slow_query_log%'
#开启慢查询日志,只对当前数据库生效,重启后失效,想要永久开启需要修改配置文件(my.cnf)
SET GLOBAL slow_query_log=1
#为慢日志文件指定位置和文件名
SET GLOBAL slow_query_log_file='C:\ProgramData\MySQL\MySQL Server 5.5\Data\slow20190901.log'
Variable_name | Value |
---|---|
slow_query_log | OFF |
slow_query_log_file | C:\ProgramData\MySQL\MySQL Server 5.5\Data\ZJZL-0190630XJ-slow.log |
系统会给一个默认的host_name_slow.log,可以通过slow_query_log_file来设置
#查看当前阈值,多少秒会被记录到慢日志中。
#判断逻辑是大于,刚好等于阈值不会记录到慢日志中
SHOW VARIABLES LIKE '%long_query_time%'
#修改慢SQL阈值
SET GLOBAL long_query_time=3
Variable_name | Value |
---|---|
long_query_time | 10.000000 |
#查看当前有多少条慢SQL
SHOW GLOBAL STATUS LIKE '%slow_queries%'
mysqldumpslow–help查看帮助信息
得到返回记录数最多的前十条SQL
mysqldumpslow -s r -t 10/慢日志路径
得到按时间排序最长的且有左连接查询的前十条数据
mysqldumpslow -s t -t 10 -g"left join"/慢日志路径
是MySQL提供的可以用来分析当前会话中语句执行的资源消耗情况,默认关闭,并保存最近15次的运行结果
#查看是否开启show profile
SHOW VARIABLES LIKE'%profiling%'
#开启show profile
SET GLOBAL profiling=1
#使用show profiles
SHOW profiles
我们可以看到每一条被记录的SQL的执行时间
QUERY_ID | Duration | Query |
---|---|---|
1 | 0.00190025 | show variables like’%profiling%’ |
通过下面语句,在字段QUERY后面写上对应的QUERY ID可以得到具体的每一个执行流程的时间
show profile主要参数
SHOW PROFILE CPU,BLOCK IO FOR QUERY 1
主要关注下面这些参数的时间,出现任意一个都是必须要进行优化的
可以记录所有的SQL语句,并存储在系统表中,永远不要再生产环境开启这个功能
#开启全局查询日志
SET GLOBAL general_log =1
#以表的形式
SET GLOBAL log_output='TABLE'
#查看全局查询日志
SELECT * FROM mysql.general_log
对数据的操作类型划分
对数据操作的粒度划分
偏向MyISAM存储引擎,开销小,加锁快,不会出现死锁,但是锁的粒度大,发生锁冲突的概率高,并发度很低
#手动添加表锁(读锁|写锁)
LOCK TABLE [tableName [READ|WRITE]] , [ ] , [ ]……
#查看表上加过的锁
SHOW OPEN TABLES
#释放表锁
UNLOCK TABLES
表读锁
加锁session可以读加锁表,不可以写(报错),不可以读写其他表(报错),只读不可写
其他session可以读加锁表,不可以写(阻塞),可以读写其他未加锁表,可读写阻塞
表写锁
加锁session 可以读加锁表,可以写加锁表,不可以读写其他表,可读可写
其他session 不可以读(阻塞),不可以写,可以读写其他表,读阻塞不可写
偏向InnoDB存储引擎,开销大,加锁慢,可能会出现死锁,但是锁的粒度最小,发生锁冲突的概率最小,并发度最高
InnoDB存储引擎默认的事务隔离级别是不可重复读,这种隔离级别下的行锁有以下特点
索引失效会导致行锁升级为表锁
间隙锁
当我们用范围条件而不是等值条件来给数据加锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但是并不存在的数据被称为间隙。InnoDB也会对这个间隙加锁,这种锁机制就叫间隙锁。
间隙锁会导致范围内,不存在的键值也被锁定,所以在锁定后无法插入键值范围内的任何数据,某些情况会影响性能
如何锁定一行
使用关键字FOR UPDATE,直到提交才会释放锁
SELECT * FROM tableName WHERE id=1 FOR UPDATE
slave会从master读取binlog来进行数据同步,master将改变记录到二进制文件中,这些记录过程叫做二进制日志事件,slave将maser的二进制文件拷贝到它的中继日志中,slave重做中继日志中的事件,将改变应用到自己的数据库中。MySQL的主从复制是,异步且串行化,所以会有延迟的问题。注意:
从机只需要配置服务器ID即可
配置完成后使用SHOW SLAVE STATUS命令,如果SLAVE_IO_RUNNING和
SLAVE_SQL_RUNNING同为YES则表示配置成功