https://blog.csdn.net/weixin_38003389/article/details/83746223mysql
字段请使用utf8mb4字符集linux
3.对mysql优化时一个综合性的技术,主要包括(硬件、配置、设计、执行、规模)sql
SQL级docker
a: 表的设计合理化(符合3NF)数据库
b: 添加适当索引(index) [四种: 普通索引、主键索引、惟一索引unique、全文索引]编程
e: 存储过程 [模块化编程,预编译,能够提升速度]缓存
服务器级安全
c: 分库分表(水平分割、垂直分割)bash
d: 主从复制,读写分离 [写: update/delete/add]
f: 对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小,默认为100,max_connection=1000 ]
部分数据库链接池会与mysql保持长链接不放,因此分布式/集群时注意保证数据库有充足的连接
g: mysql服务器硬件升级
h: 定时的去清除不须要的数据,定时进行碎片整理(MyISAM)
I: 将历史数据转移
4. 3NF
1NF: 即表的列的具备原子性,不可再分解
2NF: 表中的记录是惟一的, 就知足2NF
3NF: 即表中不要有冗余数据
反3NF : 可是,没有冗余的数据库未必是最好的数据库,有时为了提升运行效率,就必须下降范式标准,适当保留冗余数据,减小多表关联。
5 show status/variables like 'name' 检查配置
show status like 'uptime' ; (mysql数据库启动了多长时间)
show stauts like 'com_select' show stauts like 'com_insert' ...类推 update delete(显示数据库的查询,更新,添加,删除的次数)
show [session|global] status like .... 若是你不写 [session|global] 默认是session 会话,指取出当前窗口的执行,若是你想看全部(从mysql 启动到如今,则应该 global)
show status like 'connections';//显示到mysql数据库的链接数
show status like 'slow_queries';//显示慢查询次数 默认10s以上是慢查询 推荐缩小
show global variables like 'long_query_time'; //能够显示当前慢查询时间
set global slow_query_log='ON' //(临时开启慢查询日志,mysql重启时失效)
set long_query_time=1 ;//能够修改慢查询时间s(重启失效)
set global max_connections=500 ;//修改最大连接数
show variables like 'slow_query%' :slow_query_log是否开启慢查询日志,1表示开启,0表示关闭。
slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。能够不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log
log-slow-queries :旧版(5.6如下版本)MySQL数据库慢查询日志存储路径。能够不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log
log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。
log_output:日志存储方式。log_output=
'FILE'
表示将日志存入文件,默认值是
'FILE'
。log_output=
'TABLE'
表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。MySQL数据<br>库支持同时两种日志存储方式,配置的时候以逗号隔开便可,如:log_output=
'FILE,TABLE'
。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,所以对于须要启用慢查询日志,又须要可以得到更高的系统性能,那么建议优先记录到文件。
6 慢查询日志
记录超过long_query_time时间的sql语句
在默认状况下,咱们的mysql不会记录慢查询,需启动mysql时候,指定记录慢查询日志 启动指令 bin\mysqld.exe - -safe-mode - -slow-query-log [mysql5.5 能够在my.ini指定](安全模式启动,数据库将操做写入日志,以备恢复);bin\mysqld.exe –log-slow-queries=d:/abc.log [低版本mysql5.0能够在my.ini指定]
推荐临时开启:set global slow_query_log =1 (mysql重启时失效)
先关闭mysql服务,再启动, 若是启用了慢查询日志,默认把这个文件放在my.ini 文件中记录的位置
#Path to the database root
datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.5/Data/"
mysqldumpslow 慢日志分析工具
命令:
-s 按照那种方式排序 c:访问计数 l:锁定时间 r:返回记录 al:平均锁定时间 ar:平均访问记录数 at:平均查询时间 -t 是top n的意思,返回多少条数据。 -g 能够跟上正则匹配模式,大小写不敏感。
举例:
获得返回记录最多的20个sql::mysqldumpslow -s r -t 20 /database/mysql/mysql_slow.log(日志路径)
获得平均访问次数最多的20条sql:mysqldumpslow -s ar -t 20 sqlslow.log
获得平均访问次数最多,而且里面含有ttt字符的20条sql:mysqldumpslow -s ar -t 20 -g "ttt" sqldlow.log
https://blog.csdn.net/sunyuhua_keyboard/article/details/81204020
http://www.javashuo.com/article/p-ervhibgs-k.html(包含win/linux下使用)
执行结果格式
命令解析:排序(-s)按执行次数(c)倒序(-a) C:\Program Files\MySQL\MySQL Server 5.5\bin>perl mysqldumpslow.pl -s c -a c:\mysql\log\mysqlslowquery.log Reading mysql slow query log from c:\mysql\log\mysqlslowquery.log 执行次数 Count: 3 执行时间Time=0.27s (0s) 锁定时间Lock=1.67s (1s) 发送行数Rows=1.0 (1), 执行地址root[root]@localhost 内容:select count(*) from lm_d_plan Count: 1 Time=0.00s (0s) Lock=0.00s (0s) Rows=0.0 (0), 0users@0hosts C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqld, Version: 5.5.22-log (MySQL Community Server (GPL)). started with: TCP Port: 3306, Named Pipe: (null)
SQL解析顺序
影响索引的最左命中。必定是解析顺序在前的字段,排在索引左边。
解析顺序 : from..on..join..where..group by..having..seletc distinct..order by limit
http://www.javashuo.com/article/p-qaiuyzym-cn.html
解析过程简述:
一、肯定主驱动表
有A,B,C三个表作join查询。Mysql先要经过where条件推断结果集,没有条件则按照全表计算,从小到大依次解析表。
假设推断后,A=10行,B=5行,C=15行 ,中间MySQL还会有一些缓存或join顺序优化,同行数的看where可否命中索引等。
假定优化后解析顺序 :B>A>C。肯定主驱动表=B。
二、开始解析执行,索引生效
B:做为最小驱动表,执行where条件取得结果集1。此时where条件断定索引命中。
A:与结果集1执行join on生成临时表(断定索引),where条件过滤取得结果集2。
C:与A相同,与结果集2执行join on生成临时表,where条件过滤取得结果集3。
能够看到索引顺序,除主驱动表外都是先on再where。下面会有具体测试案例。
7 索引 (主键索引/惟一索引/全文索引/普通索引)
primary key 有两个做用,一是约束做用(constraint),用来规范一个存储主键和惟一性,但同时也在此key上创建了一个主键索引;
PRIMARY KEY 约束:惟一标识数据库表中的每条记录;主键必须包含惟一的值;主键列不能包含 NULL 值;每一个表都应该有一个主键,而且每一个表只能有一个主键。(PRIMARY KEY 拥有自动定义的 UNIQUE 约束)
primary key(name,age) #复合主键
unique key 也有两个做用,一是约束做用(constraint),规范数据的惟一性,但同时也在这个key上创建了一个惟一索引;
UNIQUE 约束:惟一标识数据库表中的每条记录。UNIQUE 和 PRIMARY KEY 约束均为列或列集合提供了惟一性的保证。(每一个表能够有多个 UNIQUE 约束,可是每一个表只能有一个 PRIMARY KEY 约束)
unique index(`userId` ,`taskId`,`date`) #复合惟一索引
惟一索引注意 null 是能够重复的。例如:unique index(`userId` ,`phone`),能够重复插入不报错:('A00001',null)。
所以在建立属于惟一索引的列时,最好指定字段值不能为空,在已有值为NULL的状况下,建立的字段不容许为空,且默认值为空字符。若是已经建立了默认值为NULL的字段,则先将其update为空字符,而后再修改成NOT NULL DEFAULT ‘’。
foreign key也有两个做用,一是约束做用(constraint),规范数据的引用完整性,但同时也在这个key上创建了一个index;
索引的代价:占用磁盘空间,dml操做变慢
适合建索引列:
a:字段在where,on,order,group常用
b: 该字段的内容多样化,惟一性高(容易缩小范围)
c: 字段内容不是频繁变化.
使用索引的注意事项:
a.单表查询一次只能命中一个索引。影响:查找,排序
多表查询时经过EXPLAN能够看到各个表的子查询命中的索引状况和执行顺序
b.unique字段能够为NULL,并能够有多NULL, 可是若是是具体内容,则不能重复
c.复合索引从左边按顺序开始匹配,注意sql解析顺序,避免跨列引发索引失效。
把可能索引失效的列尽量放在KEY最后
使用最频繁、惟一性高的,解析顺序靠前的字段 放在最左。
建立key(a,b,c)后,不用再建立(a,b),(a)索引
d.BTREE全覆盖索引,查询时不用回表
i. join on 条件字段索引,= 左右哪一个字段循环频率高,创建索引 。
定位驱动表: 左外联左表驱动,右外联右表驱动,内联自动小表驱动。内联能够经过表自身大小和EXPLAN解析select扫描的行数来判断那个表为驱动表。
通常状况:被驱动表on条件必定要加索引(条件容许也可两表都创建索引)。由于被驱动表通常是主表循环的内嵌套循环,执行频率高。
且where条件中尽可能可以过滤一些行将驱动表变得小一点
l.主键自带索引,会自动加到复合索引的最右侧
J.TEXT,长字符串,若须要查询则能够创建前缀索引。创建前先观察不一样长度前缀的区分度,肯定前缀长度
索引失效总结: 命中不连续,类型转换,列计算,null值判断,负向(否)条件,in/or,like ‘%a’,范围查询右边key(自身也可能失效)。某些状况是几率事件,不必定百分百触发。
e.对于使用like的查询,查询若是是 ‘%aaa’ 不会使用到索引 (索引失效)
f.某列是范围查询,其索引右边列没法使用索引。(索引失效,多范围条件注意)
例如:key(a,b,c) => where a=1 and b>0 and c<0 => 只会用到key(a,b)或key(a) 有不肯定性
g.若是条件中有or,要求or包含的全部字段都必须创建索引,其中一个有索引也无效 (索引失效)
h.避免在where条件中进行 null值判断、in、负向条件(!=、<>、not in、not like、not exists ) 这会使索引失效,因此把这些列放在复合索引最后位置,或是优化为其余形式(例如:用left join + id=null 代替not in )
j.不要在索引字段作操做(计算,函数,类型转换等),会引发索引失效。例如:where a+1=1
k.显式/隐式类型转换,会引发索引失效 例如:where name=1
H.可使用全覆盖索引挽救索引失效
例如:select * from user where name like '%a%',其必然索引失效。
能够创建key(name,id),改变SQL为: select name,id from user where name like '%a%' 这样虽然索引失效,可是能够挽救为use index。
8.sql语句的优化
避免索引失效
select:
a.可使用关联来替代子查询。由于使用join,MySQL不须要在内存中建立临时表。(5.5及如下)
b.确保关联查询 on/using 条件命中索引,内联顺序的第二个表建索引,左外联建左表,右外联建右表
c.left join 尽量小表在左驱动大表,join至关于for循环嵌套,次数少的循环放在最外层。inner join mysql会自动优化。也能够尝试用straight join 代替inner join,强制驱动表。
条件 “=” 小表字段在左边
d.避免使用select *,减小IO负担。(测试字段多时,时间消耗有可能成倍增加)
e.发现扫描行数远大于结果,则考虑优化查询:使用覆盖索引/修改表结构/复杂查询分割多个小查询
f.尽量使用索引覆盖扫描select
g.select ..from table where in/exists (子查询),主查询结果ROW大用IN反之用exist
h.left join where id != null 能够代替 not in () 。或是先插入临时表再delete
join、between 能够代替 in ()。in中元素不要太多,不然必须被代替。
i.合理运用临时表,好比有一张整年表,如今只须要一个月数据,且这个数据后边要反复查询/处理,则先插入临时表,之后就不用处理整年表啦
j.避免使用过多的join 关联表。于Mysql来讲,是存在关联缓存的,缓存的大小能够由join_buffer_size参数进行设置
在Mysql中,对于同一个SQL多关联(join)一个表,就会多分配一个关联缓存,若是在一个SQL中关联的表越多,
所占用的内存也就越大。若是程序中大量的使用了多表关联的操做,同时join_buffer_size设置的也不合理的状况下,就容易形成服务器内存溢出的状况,就会影响到服务器数据库性能的稳定性同时对于关联操做来讲,会产生临时表操做,影响查询效率
Mysql最多容许关联61个表,建议不超过5个
k.拆分大sql变为小sql。大SQL:逻辑上比较复杂,须要占用大量CPU进行计算的SQL。MySQL 一个SQL只能使用一个CPU进行计算。SQL拆分后能够经过并行执行来提升处理效率
L.不会有重复值时使用UNION ALL 。UNION 会把两个结果集的全部数据放到临时表中后再进行去重操做, UNION ALL 不会再对结果集进行去重操做
group:
a.在使用group by 分组查询是默认分组后,还会排序,可能会下降速度。
在group by 后面增长 order by null 就能够防止排序.
b.group by/order by 只涉及一个表的字段,才能使用索引。group by 能够拆分红子查询,从而使其只涉及一个表。
c.用join关联条件作group by条件,效率更高
limit:
a.尽量使用索引覆盖扫描select
b.order by 命中索引
c.当只须要一条结果时手动limit 1,能够有效减小扫描量。
d.大表分页优化。例如:表有主键ID自增,每页有10条数据,想显示第二页数据,传统思路limit 11,10。优化思路 where id>10 limit 10。
order by :
a.避免使用select *
b.选择单路(4以后默认)/双路排序方法,设置max_length_for_sort_data扩大排序缓冲区
c.复合索引,按照解析顺序不要跨列
d,排序字段,同升同降 order by a desc,b desc
优化举例:
例1:
order by 举例对比:有索引 key(a,b,c,d)
where +order by 从左命中不要跨列
select a,b,c,d from table where a=1 and d=1 and c=1 and b=1
(using index全覆盖查询, 实际用到 key(a,b,c,d))
select a,b,c,d from table where a=1 and b=1 and d=1 order by c
(using index,using where 回表查询 ,由于从左命中d跨列致使d没法用到索引 ,实际使用 key(a,b))
select a,b,c,d from table where a=1 and d=1 order by c
(using index,using where,using fliesort 回表查询+文件排序:由于从左命中orderby跨列 key(a))
select a,b,c,d from table where a=1 and d=1 order by b,c
(using index,using where :对比上个例子没有using fliesort,由于where +orderby没有索引跨列,可是由key_len可知道,实际使用只有 key(a),只是避免了文件排序)
例2:
Sql解析顺序影响索引命中,in()可能出现索引失效
select b from table where a=1 and c in (1,2) order by c
key(a,c,b) 效果最好,受Sql解析顺序影响,b放在where条件以后
索引中将c放在a以后,即便c in ()索引失效, a 索引仍是有效
例3:
多表JOIN 解析对比索引命中。请先阅读上面的"sql解析顺序"
表user_zhw: 列( userid,zhwid,beizhu ) union_key(userid,zhwid) 当前 7 rows 表zhwdoc: 列(id ,name) KEY(id) 当前 3 rows select * from user_zhw join zhwdoc on zhwdoc.id=user_zhw.zhwid where zhwdoc.name ='prouser' and user_zhw.userid=1
只执行前两行:解析
同ID由上至下执行,sql解析顺序规则(见上面),先锁定结果可能最少的表,由于只执行前两行无where,可是zhwdoc总行数少因此其为主驱动表。zhwdoc先执行where过滤由于没where全部ALL扫描。以后on条件关联user_zhw表没有命中索引ALL扫描。扫描行数3+7。
执行前三句:解析
同ID由上至下执行。经过where条件肯定zhwdoc结果集较小做为主驱动表。
zhwdoc先执行where过滤,没有命中索引ALL扫描。以后on条件关联user_zhw表没有命中索引ALL扫描。扫描行数3+7。
所有执行:解析
同ID由上至下执行。 经过where条件肯定user_zhw命中索引其结果集较小做为主驱动表。
user_zhw先执行where过滤且命中部分索引(len)。以后on条件关联zhwdoc表索引命中。扫描行数1+1。
例4: 误区
表user_zhw: 列( userid,zhwid,beizhu ) union_key(userid,zhwid) 当前 7 rows select userid,zhwid from user_zhw where zhwid=1
上面where条件应该没法命中索引,可是解释结果命中了所有索引union_key长度,可是看扫描行数为全表扫描。 此处命中的索引是由于Using index,既索引全覆盖,select字段不用回表查询。where条件并无命中,因此仍是全表扫描。多表join时也要特别注意,不要只看key列,必定要以rows为准。可是也说明在where没法命中状况下 ,全覆盖索引能稍微弥补
9.EXPLAN
a.EXPLAN是近似结果,可能与真相相差甚远,且只能解释select
b.id,type,key,row,extra几个字段比较关键
id:表示执行顺序,由大到小执行,id相同时由上到下执行
type:ALL/index/range/ref/eq_ref/const/system/null 效率由低到高,通常以range/ref为目标
ALL:按行顺序全表扫描。通常百万以上出现ALL则必须优化
index:按索引顺序全表扫描,避免了排序。缺点是承担按索引顺序读取的开销
range:范围扫描。有范围的索引扫描。(between,><,in,or 且命中索引)
ref:索引访问/查找。返回全部匹配当个索引列的结果。
例如:select name from T where name="a" (name命中索引)
key:实际使用索引(where\on\select),只能命中一个。5.0后有索引合并
多表查询时经过EXPLAN (id)能够看到各个表的子查询命中的索引状况和执行顺序
key_len:判断使用索引前几个字段,例如 KEY(A,B,C) 使用到(A,B,C)和(A,B)长度不一样
row:预估扫描的行数(直观检验)
extra:
using index :使用覆盖索引(不回表)
using where :可能部分受益于索引(需回表)
using temporary :需优化,排序使用临时表(group by 未命中索引)
using fliesort :需优化,文件排序 IO消耗(order by 未命中索引,where +orderby 从左命中不要跨列)
using join buffer : 需优化。
10 存储引擎
myisam,请必定记住要定时进行碎片整理
innoDB vs myisam:
InnoDB 支持事物+行级锁+支持外键+支持MVCC,不支持全文索引
11 锁
1 加行锁(命中索引):互斥锁X,其余线程不能再在此行上任何锁,其余线程可读不可写,直到当前线程提交事务。insert /update /select ...for update
共享锁S,全部可读不可写,共享锁能够叠加,以前的全部共享锁都解锁才能上互斥锁。select ...lock in share mode
commit 时解除锁
不管经过哪一个索引命中此行,只要此行被锁住,其余线程都不能更新此行
2 没有命中索引,行锁会转为表锁
坑1:update table set name='sw' where name=1 ,假设有KEY(name),可是name=1发生类型转换,致使索引失效,执 行update产生表锁
表锁要等待全部行锁+表锁解锁
3 间隙锁:mysql为范围内不存在的行加锁
例如:update table set name='hh' where id<=3; 有key(id)。即便当前表中尚未id=2的行,mysql也会锁住id in (1,2,3) 三行内容。在update未提交以前,读/写/插入id=3的数据,就会阻塞
4 乐观锁(版本号 or 时间戳 or 临界条件)
提升数据库并发能力,避免超买超减等问题。本质上是CAS,要保证比较和赋值是原子性操做
好比: 在事务A中使用 update spkc set shl=shl-1 where id=1 and shl>0,判断受影响rows决定是否扣减成功是否向下执行。事务A执行update会上锁(注意命中索引,不然会上表锁),其余事务须要等待事务A提交再执行update。
https://blog.csdn.net/shanchahua123456/article/details/86571038
5 避免死锁,加速事务提交
http://www.javashuo.com/article/p-osrltxbr-bn.html
https://825635381.iteye.com/blog/2339434
5.1 以固定的顺序访问表和行,这样就避免了交叉等待锁的情形
5.2 大事务拆小、缩短事务提交时间、避免事务中有复杂逻辑/远程调用。
必定要保证事务方法不要无休止的不提交事务。好比方法中等待http/远程响应,或复杂逻辑等状况致使不能提交。
5.3 在同一个事务中,尽量作到一次锁定所须要的全部资源。
5.4 为表添加合理的索引,命中索引上行锁。避免表锁,不容易死锁
5.5 避免gap锁
5.6 并发插入出现duplicate key重复键异常时,当前事务会默认加上S锁。这时当前事务再去申请X锁,就会死锁。
能够查看死锁日志
查看锁表
方法一 慢查询日志
方法二 MYSQL中执行
http://www.javashuo.com/article/p-oulolrez-ee.html
-- 查看那些表锁到了 show OPEN TABLES where In_use > 0; --In_use列表示有多少线程正在使用某张表,Name_locked表示表名是否被锁 -- 查看进程号 show processlist; --删除进程 kill 1085850; --查看正在锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; --查看等待锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
12 日志
mysql日志的种类,通常来讲,日志有五种,分别为:
错误日志:-log-err (记录启动,运行,中止mysql时出现的信息)
二进制日志:-log-bin (记录全部更改数据的语句,还用于复制,同步,恢复数据库用)
查询日志:-log (所有记录创建的客户端链接和执行的语句,量大,不建议生产中使用)
慢查询日志: -log-slow-queries (记录全部执行超过long_query_time秒的全部查询)
查询当前日志记录的情况:
mysql>show variables like 'log_%';(是否启用了日志)
mysql> show master status;(怎样知道当前的日志)
mysql> show master logs;(显示二进制日志的数目)
binlog
http://www.javashuo.com/article/p-yhnowzxb-m.html
http://www.javashuo.com/article/p-hgvzgbhe-be.html
show variables like 'log_%'; (查看日志开启和保存路径)
show binary logs;(现有的binlog名字)
show global variables like "binlog%";(查看模式)
经过上边指令锁定日志文件路径
在linux中mysql执行(解析时间较长,注意缩短datetime)
docker须要进入运行的mysql容器执行
mysqlbinlog --start-datetime='2019-01-01 00:00:00' --stop-datetime='2019-06-18 23:01:01' -d 库名 /var/lib/mysql/binlog.000003
若打印的SQL语句为乱码,则须要base64解析 --base64-output=decode-rows
mysqlbinlog --base64-output=decode-rows -v --start-datetime='2019-01-01 00:00:00' --stop-datetime='2019-06-18 23:01:01' -d 库名 /var/lib/mysql/binlog.000003
执行SQL:update user set beactive='n' where id=1 (user 表有三个字段)
能够看到update-log显示了全部字段的新旧状态。
13 安全
1 防止sql注入,因此最好使用预编译SQL执行 #{}
例如:注入 "or 1=1 " 做为条件欺骗
2 字符尽可能使用单引号。尤为是sql_mode='ANSI_QUOTES'时,双引号会当识别符处理,引发条件丢失/赋值错误
14 大量数据操做
过大数据的(100万)批量写操做要分批屡次操做 1.大批量操做可能会致使严重的主从延迟 2. binlog日志为row格式时会产生大量的日志 大批量写操做会产生大量日志,特别是对于row格式二进制数据而言,因为在row格式中会记录每一行数据的修改,咱们一次修改的数据越多, 产生的日志量也就会越多,日志的传输和恢复所须要的时间也就越长,这也是形成主从延迟的一个缘由 3. 避免产生大事务操做 大批量修改数据,必定是在一个事务中进行的,这就会形成表中大批量数据进行锁定,从而致使大量的阻塞,阻塞会对MySQL的性能产生很是大的影响 特别是长时间的阻塞会占满全部数据库的可用链接,这会使生产环境中的其余应用没法链接到数据库,所以必定要注意大批量写操做要进行分批 4.对于大表的修改使用pt-online-schema-change 1.原理: 会在原表的结构上建造一个新表 复制数据 2.避免延迟,修改时锁表 5.禁止super权限滥用 6.数据帐号链接最小
15 mysql缓存机制
http://www.javashuo.com/article/p-ecfshsol-bk.html
mysql自身也有缓存机制,能够很大提升重复查询的执行效率
表碎片整理
http://www.javashuo.com/article/p-tydujnyo-bc.html
OPTIMIZE TABLE table_name 会锁住整张表进行整理,且时间可能很长