重学MySQL笔记
Posted Simon格子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重学MySQL笔记相关的知识,希望对你有一定的参考价值。
操作环境:CentOS7、mysql5.5版本
一、MySQL架构
1、安装mysql
检查是否安装mysql:
rpm -qa | grep mysql
卸载mysql:
rpm -e -nodeps mysql包名
安装mysql:
rpm -ivh mysql.rpm
启动mysql:
service mysql start
设置密码:
mysqladmin -u root password '密码'
设置mysql开机自启动:
chkconfig mysql on
检查mysql是否设置开机自启动成功:
chkconfig --list|grep mysql
修改字符集,vim /etc/my.cnf修改如下:
[client]
default-character-set=utf8
[mysqld]
character_set_server=utf8
character_set_client=utf8
collation-server=utf8_general_ci
[mysql]
default-character-set=utf8
安装位置:
ps -ef | grep mysql
设置远程访问权限:
grant all privileges on *.* to '用户名'@'%' IDENTIFIED BY '密码';
myuser/mypassword 从任何主机连接到mysql服务器
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
myuser/mypassword 指定IP地址连接到mysql服务器
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'IP地址' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
刷新权限:
flush privileges;
开放MySQL端口:
firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --reload
重启MySQL:
service mysql restart
2、配置文件
二进制log-bin 主从复制
错误日志log-error 默认关闭,记录严重的警告和重大错误信息,每次启动的关闭的信息
查询日志log 默认关闭,记录查询SQL语句。如果开启会头减低mysql性能,记录日志需要消耗系统资源
数据文件:
(1)数据库存放位置,/var/lib/mysql
(2)frm文件 存放表结构
(3)myd文件 存放表数据
(4)myi文件 存放表索引
3、逻辑架构
连接层
Connectors(不同语言中与SQL的交互)
服务层:
(1)Connection Pool(管理缓冲用户连接,线程处理等需要缓存的需求)
负责监听对 MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。
每一个连接上 MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。
而连接线程的主要工作就是负责 MySQL Server 与客户端的通信,接受客户端的命令请求,传递Server端的结果信息等。
线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的cache等
(2)Management Serveices & Utilities(系统管理和控制工具,包括备份恢复、MySQL 复制、集群)
(3)SQL Interface(接受用户的SQL命令,并且返回用户需要查询的结果)
(4)Parser(SQL命令传递到解析器的时候会被解析器验证和解析)
a、将SQL语句进行语义和语法的分析,分解成数据结构,然后按照不同的操作类型进行分类,然后做出针对性的转发到后续步骤,以后SQL语句的传递和处理就是基于这个结构的;
b、如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的。
(5)Optimizer(查询优化器)
SQL语句在查询之前会使用查询优化器对查询进行优化,同时验证用户是否有权限进行查询,缓存中是否有可用的最新数据。
它使用“选取-投影-连接”策略进行查询,例如:
SELECT id,name FROM user WHERE gender = 0;
语句中,SELECT查询先根据WHERE语句进行选取,而不是将表全部查询出来以后再进行gender过滤。
SELECT查询先根据id和 name进行属性投影,而不是将属性全部取出以后再进行过滤,将这两个查询条件连接起来生成最终查询结果
(6)Cache&Buffer(查询缓存)
将客户端提交给MySQL的Select类query请求的返回结果集cache到内存中,与该query的一个hash值做一个对应。
该 query 所取数据的基表发生任何数据的变化之后, MySQL会自动使该 query 的Cache 失效。在读写比例非常高的应用系统中, Query Cache 对性能的提高是非常显著的。
当然它对内存的消耗也是非常大的。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等
Buffer是写缓存,Cache是读缓存
引擎层:
(1)提供各种存储引擎,主流MyISAM、InnoDB
(2)存储引擎是基于表的,而不是数据库
存储层
物理存储MySQL数据
MySQL工作流程:
建立TCP连接->验证用户->创建线程解析SQL->生成执行计划->开表->Buffer搜索数据页是否被缓存->从磁盘扫描数据->获取数据并写入Buffer Pool->返回数据给客户端->关闭表->关闭线程->关闭连接
4、存储引擎
查看mysql已提供有什么存储引擎:
show engines\\G;
查看mysql当前默认的存储引擎:
show variables like '%storage_engine%';
MyISAM和InnoDB区别:
MyISAM:
(1)不支持主外键、事务
(2)表锁,即使操作一条记录也会锁定整个表,不适合高并发
(3)只缓存索引,不缓存真实数据
(4)表空间小
(5)关注性能
InnoDB:
(1)支持主外键、事务
(2)行锁,操作时只锁定一行记录,适合高并发
(3)缓存索引和真实数据,对内存要求比较高,而且内存大小对性能有决定性的影响
(4)表空间大
(5)关注事务
查看表使用的存储引擎:
(1)show table status from 数据库名称 where name='表名称'\\G;
(2)show create table 表名称\\G;
修改表引擎方法:
alter table 表名称 engine=innodb;
二、索引优化分析
1.性能下降SQL慢、执行时间长、等待时间长
(1)查询语句写的差
(2)索引失效
a.建了索引没用上
b.没建索引
(3)关联查询太多join(设计缺陷或不得已的需求)
(4)服务器调优及参数配置(缓冲、线程数等)
2.常见通用的Join查询
(1)SQL执行顺序
from->on->join->where->group by->having->select->distinct->order by->limit
(2)SQL解析
from(笛卡尔积)->on(主表保留->join(不符合on也添加)->where(非聚集/非select别名)->group by(改变对表引用->having(只作用分组后)->select(distinct)->order by(可使用select别名)->limit(rows/offset)
(3)七种Join连接:
内连接(查询A/B共有部分):
select * from TableA a inner join TbaleB b on a.key = b.key
左连接(查询A/B共有部分和A独有部分,以A表为主.全A):
select * from TableA a left join TbaleB b on a.key = b.key
右连接(查询A/B共有部分和B独有部分,以B表为主,全B):
select * from TableA a right join TableB b on a.key = b.key
32左连接(查询A独有的部分):
select * from TableA a left join TableB b on a.key = b.key where b.key is null
右连接(查询B独有部分):
select * from TableA a right join TableB b on a.key = b.key where a.key is null
全连接(查询A/B都有的部分):
(1)Oracle语法
select * from TableA a full outer join TableB b on a.key = b.key
(2)MySQL语法
select * from TableA a left join TbaleB b on a.key = b.key
union
select * from TableA a right join TableB b on a.key = b.key
全连接(查询A/B各自独有的部分):
(1)Oracle语法
select * from TableA a full outer join TableB b on a.key = b.key where a.key is null and b.key is null
(2)MySQL语法
select * from TableA a left join TableB b on a.key = b.key where b.key is null
union
select * from TableA a right join TableB b on a.key = b.key where a.key is null
3.索引简介
(1)什么是索引
a.索引是帮助MySQL高效获取数据的数据结构,可以得到索引的本质:索引是数据结构。可以理解为:排好序的快速查找数据结构
b.索引会影响到where后面查询和order by后面的排序。索引对查找和排序都有影响
c.索引本身也很大,不可能存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
(2)优势和劣势
优势:
a.提高数据检索效率,降低IO成本
b.通过索引对数据进行排序,降低数据排序成本,降低CPU的消耗
劣势:
a.占用空间
b.降低更新速度
c.花时间研究、逐步优化
(3)索引分类
单值索引、唯一索引、复合索引
创建:
create index 索引名称 on 表名称(字段名称)
alter 表名称 add index 索引名称 on(字段名称)
删除:
drop index 索引名称 on 表名称
查看:
show index from 表名称\\G;
(4)索引结构
BTree索引、Hash索引、Full-text全文索引、R-Tree索引
(5)什么情况下需要创建索引和什么情况下不需要创建索引
需要创建索引:
a.主键自动创建索引
b.频繁作为查询条件的字段应该创建索引
c.查询中志其它表关联的字段,外键关系建立索引
d.频繁更新的字段不适合创建索引
e.Where条件里用不到的字段不创建索引
f.查询中排序的字段
g.查询中统计或者分组字段
不需要创建索引:
a.表记录太少
b.经常增删改的表
c.数据列包含许多重复内容
4.性能分析
(1)MySQL Query Optimizer
通过计算分析系统中收集到的统计信息,为客户端请求的Query提供它认为最优的执行计划
(2)MySQL常见瓶颈
CPU、IO、服务器硬件性能瓶颈
(3)Explain
能干啥:
a.表的读取顺序
b.数据读取操作的操作类型
c.哪些索引可以使用
d.哪些索引被实际使用
e.表之间的使用
f.每张表有多少行被优化器查询
语法:
Explain+SQL语句
包含的信息:
id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra
id:
a.id相同,执行顺序由上至下
b.id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
c.id相同,同时存在
select_type(查询类型):
SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
SIMPLE:查询中不包含子查询或者UNION
PRIMARY:包含任何复杂的子部分,最外层查询被标记为
SUBQUERY:select或where列表中包含子查询
DERIVED:包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表
UNION:第二个select出现在UNION之后,标记为UNION。若UNION包含在from子句的子查询中,外层select将被标记为DERIVED
UNION RESULT:从UNION表获取结果的select
type(类型):
a.从最好到最差顺序:system>const>eq_ref>ref>range>index>ALL
b.保证查询至少达到range级别,最好能达到ref
possible_keys(可能应用在这张表的索引)
key(实际使用的索引)
key_len(表示索引中使用的字节数,通过该列计算查询中使用的索引长度,在不损失精确性情况下,长度越短越好)
ref(索引的那一列被使用)
rows(根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数)越少越好
Extra(包含不适合在其它列中显示但十分重要的额外信息)
(1)Using filesort:文件内排序
(2)Using temporary:产生内部临时表
(3)Using index:使用了覆盖索引
(4)Using where:使用了where过滤
(5)Using join buffer:使用了连接缓存
(6)impossible where:where子句的值总是false,不能用来获取任何无组
(7)select tables optimized away:在没有group by子句的情况下,基于索引优化min/max操作或者MyISAM存储引擎优化count(*)操作
(8)distinct:优化distinct操作
5.索引优化
(1)索引分析:
a.单表加索引:range类型查询字段后面的索引无效。
b.多表加索引:左连接相反加(右表),右连接相反加(左表)。
c.Join语句优化:
1.尽量减少join语句中的循环次数,不要join过多和嵌套,"尽量用小表驱动大表"
2.优先优化嵌套循环的内层循环
3.保证join语句中驱动表上join条件字段已被索引
4.当无法保证驱动表的join字段被索引且内存资源充足的情况下,不要吝啬JoinBuffer的设置.
(2)索引失效(应该避免):
1.全值匹配我最爱
2.最佳左前缀法则(如果引用了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且"不跳过索引中的列")
(1)带头大哥不能死
(2)中间兄弟不能断
3.不在索引上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描(索引列上无计算)
4.存储引擎不能使用索引中范围条件右边的列(范围以后索引全失效)
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
6.mysql在使用不等于(!=或者<>)的时候无法使用索引会导致值班表扫描
7.is null,is not null也无法使用索引
8.like以通配符开头('%abc。。。')mysql索引失效会变成全表扫描操作(%like加右边,如果两边都有%覆盖索引)
9.字符串不加单引号索引失效(字符串里有引号)
10.少用or,用or来连接时会索引失效
(3)优化口决:
全值匹配我最爱,最左前缀要遵守。
带头大哥不能死,中间兄弟不能断。
索引列上少计算,范围之后全失效。
LIKE百分写最右,覆盖索引不写星。
不等空值还的or,索引失效要少用。
(4)案例分析:
1.定值、范围还是排序,一般Order by是给个范围
2.group by基本上都需要排序,会有临时表产生(分组之前必排序,如果错乱会产生临时表)
(5)一般性建议:
1.对于单键索引,尽量选择针对当前查询过滤性更好的索引
2.在选择组合索引的时候,当前查询中过滤性最好的字段在索引字段顺序中,位置越靠左最好
3.在选择组合索引的时候,尽量选择可以能够包含当前查询中的where字句中更多字段的索引
4.尽可能通过分析统计信息和调整查询的写法来达到选择合适索引的目的
三、查询截取分析
1.查询优化:
(1)小表驱动大表,即小的数据集驱动大的的数据集
a.当B表的数据集小于A表的数据集,用in优于exists
b.当A表的数据集小于B表的数据集时,用exists优于in
(2)order by关键字优化
order by字句,尽量使用Index(索引)方式排序,避免使用FileSort(文件内排序)方式排序。Order by满足以下情况,会使用索引进行排序:
a.order by语句使用索引最左前列
b.使用Where子句与order by子句条件列组合满足索引最左前列
(3)尽可能的在索引列上完成排序操作,遵照索引建的最佳左前缀
(4)如果不在索引列上,filesort有两种算法
a.双路算法 mysql4.1版本以前磁盘扫描2次,最终获得数据。
读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
从磁盘取排序字段,在buffer进行排序,再从磁盘取其它字段
b.单路算法 磁盘扫描1次,减少IO操作。单路算法引发的问题:
一次取不完的时候,多次去取反而更耗IO。为什么一次取不完呢?取的数据超过my.cnf配置文件里的sort_buffer_size参数的值
(5)优化策略
a.增大sort_buffer_size参数的配置
b.增大max_length_for_sort_data参数的配置
(6)group by关键字优化
a.group by实质是先排序后进行分组,遵照索引建的最佳左前缀
b.当无法使用索引时,增大sort_buffer_size参数的配置+max_length_for_sort_data参数的配置
c.where高于having,能写在where限定条件里就不要在having里限定
2.慢查询日志:
a.查看慢查询日志是否开启:show variables like '%slow_query_log%';
b.开启是查询日志:set global slow_query_log = 1;
c.查看系统默认慢查询时间:show variables like '%long_query_time%';
d.设置慢查询时间:set global long_query_time = 3;
3.Show profile:
a.检查mysql版本是否支持profile:show variables like 'profiling';
b.设置profiling:set profiling =on;
c.运行SQL
d.show profiles
e.诊断SQL:show profile cpu,block io for query profiles里边SQL运行的查询ID
f.日常开发要注意的结论:
(1)converting HEAD to MyISAM查询结果太大,内存不哆用搬到磁盘上
(2)Createing tmp table 创建临时表
(3)Copying to tmp table on disk 把内存中临时表复制到磁盘
(4)locked
4.全局查询日志(只在测试环境用):
a.开启全局日志:
set global general_log =1;
set global log_output='TABLE';
b.开启以后所有编写的sql都会记录在mysql.general_log表里
四、MySQL锁机制
1.锁的分类:
(1)数据操作类型分:
a.读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
b.写锁(排它锁):当前写操作没有完成前,它会阻塞其它写锁和读锁
(2)对数据操作粒度分:
a.表锁
b.行锁
2.三锁
(1)表锁(偏读):
特点:
偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低
结论:
对MyISAM表的读操作(加读锁),不会阻塞其它进程对同一表的读请求,但会阻塞对同一表的写请求,只有当读锁释放后,才会执行其它进程的写操作。
对MyISAM表的读操作(加写锁),会阻塞其它进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的写操作。
读锁会阻塞写,不会阻塞读。写锁则会把读写都阻塞。
表锁分析:
查看哪些表被锁:show open tables;
分析表锁定:show status like 'table%';
a.table_locks_waited:产生表级锁定次数,表示可以立即获取锁的查询次数,每次获取错值加1
b.table_locks_immediate:出现表级锁定争用而发生等待的次数
MyISAM的读写锁调度写优先,这也是MyISAM不适合做写为主表的引擎。因为写锁后,其它线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。
(2)行锁(偏写):
1.特点:
a.偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突概率最低,并发度最高
b.InnoDB与MyISAM最大不同有两点:一是支持事务(Transaction),二是行级锁。
2.事务的ACID属性:
a.原子性(Atomicity):事务是一个原子操作单位,其对数据的修改,要么全都执行,要么全都不执行。
b.一致性(Consistency):事务的开始和完成时,数据必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表),也都必须是正确的。
c.隔离性(Isolation):数据啼系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行,这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然
d.持久性(Durability):事务完成后,它对数据的改变就是永久性的,即使数据库发生故障也不会对其有任何影响
3.并发事务带来的问题:
a.更新丢失(Lost Update)
b.脏读(Dirty Reads)
c.不可重复读(Non-Repeatable Reads)
d.幻读(Phantom Reads)
4.事务隔离级别:
脏读、不可重复读、幻读,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决
a.未提交读:最低级别,只能保证不读取物理上损坏的数据,会脏读、会不可重复读、会幻读
b.已提交读:语句级,不会脏读、会不可重复读、会幻读
c.可重复读:事务级,不会脏读、不会不可重复读、会幻读
d.可序列化:最高级别,事务级。不会脏读、不会不可重复读、不会幻读
e.查看当前数据库的事务隔离级别:show variables like 'tx_isolation';
f.数据库默认是可重复读
5.索引失效行锁升级为表锁:
多个会话修改记录时,导致索引失效情况下,行锁瞬间升级为表锁
6.间隙锁的危害:
当我们使用范围条件而不是相等条件检索数据,并请求共享或者排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但并不存的记录,叫做间隙(GAP)
InnoDB也会对这个间隙进行加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)
例如:
id:1、3、4、5、6
会话A更新一个区间范围的数据(update table set name='abcd' where id > 1 and id < 6);
会话B插入一条id范围内里不存在的记录(比如:insert into table(id,name) values(2,'efg'));
回车之后会话B无法插入记录,因为会话A锁定了id范围内的id(1、2、3、4、5),原本行锁升级会间隙锁,阻塞了会话B的插入数据操作。
7.锁定一行:
begin
select * from table where id = 1 for updata;
8.行锁分析
a.查询行级锁:show status like 'innodb_row_lock%';
b.比较重要的是:
innodb_row_lock_time_avg 等待平均时长
innodb_row_lock_waits 等待总次数
innodb_row_lock_time 等待总时长
9.优化建议:
a.尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
b.合理设计索引,尽量缩小锁的范围
c.尽可能较少检索条件,避免间隙锁
d.尽量控制事务大小,减少锁定资源量和时间长度
e.尽可能低级别事务隔离
(3)页锁
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
五、主从复制
1.master主机配置
(1)编辑/etc/my.cnf文件,在[mysqld]下增加如下两行设置
[mysqld]
log-bin=mysql-bin # 非必需
server-id=1 # 必需
(2)创建用于数据同步的账户
GRANT REPLICATION SLAVE ON *.* to 'slave'@'从机IP地址' identified by '123456';
FLUSH PRIVILEGES;
(3)查看master状态
a.执行命令:show master status;
b.记录File名字和Position数值
2.slave主机配置
(1)编辑/etc/my.cnf文件,在[mysqld]下编辑server-id
[mysqld]
server-id=2
(2)执行同步命令
change master to master_host='主机IP地址', master_user='slave', master_password='123456', master_log_file='File名字', master_log_pos=Position值;
(3)启动slave,查看slave状态
a.start slave
b.show slave status\\G;
c.Slave_IO_Running:Yes/Slave_SQL_Running:Yes
(4)登录slave主机查看数据
以上是关于重学MySQL笔记的主要内容,如果未能解决你的问题,请参考以下文章