导图社区 MySQL思维导图
不了解MySQL?没关系,看这里,有这一张思维导图就够啦!MySQL是一种开放源代码的关系型数据库管理系统,使用结构化查询语言进行数据库管理。下图包括了MySQL的架构介绍、性能因素、查询与索引优化分析等七大内容。
编辑于2019-08-12 13:13:37MySQL高级 讲师:尚硅谷周阳2016.3V1.3
1. mysql的架构介绍
Mysql简介
概述
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司。 MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。 Mysql是开源的,所以你不需要支付额外的费用。 Mysql支持大型的数据库。可以处理拥有上千万条记录的大型数据库。 MySQL使用标准的SQL数据语言形式。 Mysql可以允许于多个系统上,并且支持多种语言。这些编程语言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。 Mysql对PHP有很好的支持,PHP是目前最流行的Web开发语言。 MySQL支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位系统支持最大的表文件为8TB。 Mysql是可以定制的,采用了GPL协议,你可以修改源码来开发自己的Mysql系统。
高级MySQL
mysql内核
sql优化攻城狮
mysql服务器的优化
各种参数常量设定
查询语句优化
主从复制
软硬件升级
容灾备份
sql编程
完整的mysql优化需要很深的功底, 大公司甚至有专门的DBA写上述
MysqlLinux版的安装
mysql5.5
下载地址
官网下载地址:http://dev.mysql.com/downloads/mysql/   MySQL Server  MySQL Client   http://downloads.skysql.com/archives/mysql-5.5/MySQL-server-5.5.16-1.rhel4.i386.rpm http://downloads.skysql.com/archives/mysql-5.5/MySQL-client-5.5.16-1.rhel4.i386.rpm http://downloads.skysql.com/archives/mysql-5.5/MySQL-devel-5.5.16-1.rhel4.i386.rpm
检查当前系统是否安装过mysql
查询命令:rpm -qa|grep -i mysql 删除命令:rpm -e RPM软件包名(该名字是上一个命令查出来的名字) 没装过:  已装过: 
安装mysql服务端(注意提示)

安装mysql客户端

查看MySQL安装时创建的mysql用户和mysql组

mysql服务的启+停

mysql服务启动后,开始连接
首次连接成功

注意这里,因为MySQL默认没有密码,所以这里我们没有输入密码就直接连上了。
按照安装Server中的提示修改登录密码

自启动mysql服务
 
修改配置文件位置
拷贝 当前5.5版本: cp /usr/share/mysql/my-huge.cnf /etc/my.cnf  5.6版本 cp /usr/share/mysql/my-default.cnf /etc/my.cnf
修改字符集和数据存储路径
1 查看字符集 show variables like 'character%'; show variables like '%char%'; 看看出现的结果:   默认的是客户端和服务器都用了latin1,所以会乱码。 2 修改  [client]#password = your_passwordport = 3306socket = /var/lib/mysql/mysql.sockdefault-character-set=utf8# The MySQL server[mysqld]port = 3306character_set_server=utf8character_set_client=utf8collation-server=utf8_general_cisocket = /var/lib/mysql/mysql.sockskip-external-lockingkey_buffer_size = 384Mmax_allowed_packet = 1Mtable_open_cache = 512sort_buffer_size = 2Mread_buffer_size = 2Mread_rnd_buffer_size = 8Mmyisam_sort_buffer_size = 64Mthread_cache_size = 8query_cache_size = 32M# Try number of CPU's*2 for thread_concurrencythread_concurrency = 8[mysql]no-auto-rehashdefault-character-set=utf8 #password = your_password port = 3306 socket = /var/lib/mysql/mysql.sock default-character-set=utf8 # The MySQL server [mysqld] port = 3306 character_set_server=utf8 character_set_client=utf8 collation-server=utf8_general_ci socket = /var/lib/mysql/mysql.sock skip-external-locking key_buffer_size = 384M max_allowed_packet = 1M table_open_cache = 512 sort_buffer_size = 2M read_buffer_size = 2M read_rnd_buffer_size = 8M myisam_sort_buffer_size = 64M thread_cache_size = 8 query_cache_size = 32M # Try number of CPU's*2 for thread_concurrency thread_concurrency = 8 [mysql] no-auto-rehash default-character-set=utf8 3 重启mysql service mysql stop; service mysql start; 4 重新连接后重新create databse并使用新建库,然后再重新建表试试 5 还是乱码的话就设值init_connect='SET NAMES utf8' ##设定连接mysql是UTF8编码
MySQL的安装位置
在linux下查看安装目录 ps -ef|grep mysql
路径 解释 备注 /var/lib/mysql/ mysql数据库文件的存放路径 /var/lib/mysql/atguigu.cloud.pid /usr/share/mysql 配置文件目录 mysql.server命令及配置文件 /usr/bin 相关命令目录 mysqladmin mysqldump等命令 /etc/init.d/mysql 启停相关脚本 在linux下查看安装目录 ps -ef|grep mysql 
Mysql配置文件
主要配置文件
二进制日志log-bin
主从复制

错误日志log-error
默认是关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息等。
查询日志log
默认关闭,记录查询的sql语句,如果开启会减低mysql的整体性能,因为记录日志也是需要消耗系统资源的
数据文件
两系统
windows
D:\devSoft\MySQLServer5.5\data目录下可以挑选很多库
linux
看看当前系统中的全部库后再进去

默认路径:/var/lib/mysql
frm文件
存放表结构
myd文件
存放表数据
myi文件
存放表索引
如何配置
windows
my.ini文件
Linux
/etc/my.cnf文件
Mysql逻辑架构介绍
总体概览
和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上, 插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。  1.连接层 最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。 2.服务层 第二层架构主要完成大多少的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存。如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。 3.引擎层 存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。后面介绍MyISAM和InnoDB 4.存储层 数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
查询说明
首先,mysql的查询流程大致是: mysql客户端通过协议与mysql服务器建连接,发送查询语句,先检查查询缓存,如果命中,直接返回结果,否则进行语句解析 有一系列预处理,比如检查语句是否写正确了,然后是查询优化(比如是否使用索引扫描,如果是一个不可能的条件,则提前终止),生成查询计划,然后查询引擎启动,开始执行查询,从底层存储引擎调用API获取数据,最后返回给客户端。怎么存数据、怎么取数据,都与存储引擎有关。 然后,mysql默认使用的BTREE索引,并且一个大方向是,无论怎么折腾sql,至少在目前来说,mysql最多只用到表中的一个索引。
Mysql存储引擎
查看命令
1 如何用命令查看 #看你的mysql现在已提供什么存储引擎: mysql> show engines; #看你的mysql当前默认的存储引擎: mysql> show variables like '%storage_engine%'; 
MyISAM和InnoDB
对比项 MyISAM InnoDB 主外键 不支持 支持 事务 不支持 支持 行表锁 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作 适合高并发的操作 缓存 只缓存索引,不缓存真实数据 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响 表空间 小 大 关注点 性能 事务 默认安装 Y Y
阿里巴巴、淘宝用哪个
 Percona 为 MySQL 数据库服务器进行了改进,在功能和性能上较 MySQL 有着很显著的提升。该版本提升了在高负载情况下的 InnoDB 的性能、为 DBA 提供一些非常有用的性能诊断工具;另外有更多的参数和命令来控制服务器行为。 该公司新建了一款存储引擎叫xtradb完全可以替代innodb,并且在性能和并发上做得更好, 阿里巴巴大部分mysql数据库其实使用的percona的原型加以修改。
2. mysql性能因素
影响mysql的性能因素
1. 业务需求对mysql的影响(合适合度)
买家卖家评论帖子实时统计???
【要求】 统计对该产品的评价帖子,要实时的! 【问题】 假如这个数据量很小OK,可以实时查询,假如越来越多(之前讲解过母婴的淘宝店),积累了好多万客户评论帖子 1 select count(*)对于刚开始数量很小时可以,如果数据增大已经有千万级的,一个查询就哭吧,成为系统性能瓶颈。 2 你正在统计时候也有买家卖家修改增删评论,你不能要求用户说我统计的时候你们不许动。 【解决】 单独一个表,单独一个字段来保存这个帖子数目。 每一个买家新增一条评论就是需要更新一个这个数字,各买家是多个(高并发)不定时的在发布帖子,实时修改更新后再统计select count(*)。 就算我们使用的是Innodb存储引擎,一个update数字他就是行锁,高并发的瓶颈出现了 【结论】 很多的统计信息都是准实时的而不是实时统计,网站的一些数量信息、分页信息、排序信息、点击率信息等等一般都不是实时的而是准实时的。 携程网络订机票看看。
2. 存储定位对mysql的影响
不适合放进mysql的数据
二进制多媒体数据
流水队列数据
超大文本数据
需要放进缓存的数据
系统各种配置及规则数据
活跃用户的基本信息数据
活跃用户的个性化定制信息数据
准实时的统计信息数据
其他一些访问频繁但变更较少的数据
3. Schema设计对系统的性能影响
尽量减少对数据库访问的请求
尽量减少无用数据的查询请求
p2p中user_main和我的账户金额冗余问题

4. 硬件环境对系统性能的影响
典型OLTP应用系统
什么是OLTP:OLTP即联机事务处理,就是我们经常说的关系数据库,意即记录即时的增、删、改、查,就是我们经常应用的东西,这是数据库的基础 对于各种数据库系统环境中大家最常见的OLTP系统,其特点是并发量大,整体数据量比较多,但每次访问的数据比较少,且访问的数据比较离散,活跃数据占总体数据的比例不是太大。对于这类系统的数据库实际上是最难维护,最难以优化的,对主机整体性能要求也是最高的。因为不仅访问量很高,数据量也不小。 针对上面的这些特点和分析,我们可以对OLTP的得出一个大致的方向。 虽然系统总体数据量较大,但是系统活跃数据在数据总量中所占的比例不大,那么我们可以通过扩大内存容量来尽可能多的将活跃数据cache到内存中; 虽然IO访问非常频繁,但是每次访问的数据量较少且很离散,那么我们对磁盘存储的要求是IOPS表现要很好,吞吐量是次要因素; 并发量很高,CPU每秒所要处理的请求自然也就很多,所以CPU处理能力需要比较强劲; 虽然与客户端的每次交互的数据量并不是特别大,但是网络交互非常频繁,所以主机与客户端交互的网络设备对流量能力也要求不能太弱。
典型OLAP应用系统
用于数据分析的OLAP系统的主要特点就是数据量非常大,并发访问不多,但每次访问所需要检索的数据量都比较多,而且数据访问相对较为集中,没有太明显的活跃数据概念。 什么是OLAP:OLAP即联机分析处理,是数据仓库的核心部心,所谓数据仓库是对于大量已经由OLTP形成的数据的一种分析型的数据库,用于处理商业智能、决策支持等重要的决策信息;数据仓库是在数据库应用到一定程序之后而对历史数据的加工与分析 基于OLAP系统的各种特点和相应的分析,针对OLAP系统硬件优化的大致策略如下: 数据量非常大,所以磁盘存储系统的单位容量需要尽量大一些; 单次访问数据量较大,而且访问数据比较集中,那么对IO系统的性能要求是需要有尽可能大的每秒IO吞吐量,所以应该选用每秒吞吐量尽可能大的磁盘; 虽然IO性能要求也比较高,但是并发请求较少,所以CPU处理能力较难成为性能瓶颈,所以CPU处理能力没有太苛刻的要求; 虽然每次请求的访问量很大,但是执行过程中的数据大都不会返回给客户端,最终返回给客户端的数据量都较小,所以和客户端交互的网络设备要求并不是太高; 此外,由于OLAP系统由于其每次运算过程较长,可以很好的并行化,所以一般的OLAP系统都是由多台主机构成的一个集群,而集群中主机与主机之间的数据交互量一般来说都是非常大的,所以在集群中主机之间的网络设备要求很高。
3. 查询与索引优化分析
性能下降SQL慢 执行时间长 等待时间长
查询语句写的烂
索引失效

单值
复合
关联查询太多join(设计缺陷或不得已的需求)
服务器调优及各个参数设置(缓冲、线程数等)
常见通用的Join查询
SQL执行顺序
手写

机读

总结

Join图

建表SQL
CREATE TABLE `tbl_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `locAdd` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `tbl_emp` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `deptId` INT(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_dept_id` (`deptId`) #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `tbl_dept` (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11); INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12); INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13); INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14); INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15); INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1); INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1); INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1); INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2); INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2); INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3); INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4); INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51);
7种JOIN
1 A、B两表共有 select * from tbl_emp a inner join tbl_dept b on a.deptId = b.id; 2 A、B两表共有+A的独有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id; 3 A、B两表共有+B的独有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id; 4 A的独有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id where b.id is null; 5 B的独有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id where a.deptId is null; #B的独有 6 AB全有 #MySQL Full Join的实现 因为MySQL不支持FULL JOIN,下面是替代方法 #left join + union(可去除重复数据)+ right join SELECT * FROM tbl_emp A LEFT JOIN tbl_dept B ON A.deptId = B.id UNION SELECT * FROM tbl_emp A RIGHT JOIN tbl_dept B ON A.deptId = B.id 7 A的独有+B的独有 SELECT * FROM tbl_emp A LEFT JOIN tbl_dept B ON A.deptId = B.id WHERE B.`id` IS NULL UNION SELECT * FROM tbl_emp A RIGHT JOIN tbl_dept B ON A.deptId = B.id WHERE A.`deptId` IS NULL;
索引简介
是什么
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。 可以得到索引的本质:索引是数据结构。
索引的目的在于提高查询效率,可以类比字典, 如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。 如果没有索引,那么你可能需要a----z,如果我想找到Java开头的单词呢?或者Oracle开头的单词呢? 是不是觉得如果没有索引,这个事情根本无法完成?
你可以简单理解为“排好序的快速查找结构”。
详解(重要)
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:  左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址 为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上
我们平常所说的索引,如果没有特别指明,都是指B+树结构组织的索引。其中聚集索引,次要索引,覆盖索引, 复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈稀索引(hash index)等。
优势
类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
劣势
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。 因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段, 都会调整因为更新所带来的键值变化后的索引信息
索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句
mysql索引分类
单值索引
即一个索引只包含单个列,一个表可以有多个单列索引
唯一索引
索引列的值必须唯一,但允许有空值
复合索引
即一个索包含多个列
基本语法
创建
CREATE [UNIQUE ] INDEX indexName ON mytable(columnname(length));
如果是CHAR,VARCHAR类型,length可以小于字段实际长度; 如果是BLOB和TEXT类型,必须指定length。
ALTER mytable ADD [UNIQUE ] INDEX [indexName] ON (columnname(length))
删除
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 UNIQUE index_name (column_list): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。 ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。 ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
mysql索引结构
BTree索引
检索原理
 【初始化介绍】 一颗b+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示), 如磁盘块1包含数据项17和35,包含指针P1、P2、P3, P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。 真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。 非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。 【查找过程】 如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。 真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
Hash索引
full-text全文索引
R-Tree索引
哪些情况需要创建索引
1. 主键自动建立唯一索引
2. 频繁作为查询条件的字段应该创建索引
3. 查询中与其它表关联的字段,外键关系建立索引
4. 频繁更新的字段不适合创建索引
因为每次更新不单单是更新了记录还会更新索引,加重了IO负担
5. Where条件里用不到的字段不创建索引
6. 单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
7. 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
8. 查询中统计或者分组字段
哪些情况不要创建索引
1. 表记录太少
2. 经常增删改的表
Why:提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。 因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件
3. 数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。 注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

性能分析
MySql Query Optimizer
1 Mysql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间) 2 当客户端向MySQL 请求一条Query,命令解析器模块完成请求分类,区别出是 SELECT 并转发给MySQL Query Optimizer时,MySQL Query Optimizer 首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对 Query 中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、结构调整等。然后分析 Query 中的 Hint 信息(如果有),看显示Hint信息是否可以完全确定该Query 的执行计划。如果没有 Hint 或Hint 信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据 Query 进行写相应的计算分析,然后再得出最后的执行计划。
MySQL常见瓶颈
CPU:CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候
IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
服务器硬件的性能瓶颈:top,free, iostat和vmstat来查看系统的性能状态
Explain
是什么(查看执行计划)
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是 如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈
能干嘛
表的读取顺序
数据读取操作的操作类型
哪些索引可以使用
哪些索引被实际使用
表之间的引用
每张表有多少行被优化器查询
怎么玩
Explain + SQL语句
执行计划包含的信息
各字段解释
id
select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况
id相同,执行顺序由上至下
 id相同,执行顺序由上至下
id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
id相同不同,同时存在
 id如果相同,可以认为是一组,从上往下顺序执行; 在所有组中,id值越大,优先级越高,越先执行 衍生 = DERIVED
select_type
有哪些
查询的类型,主要是用于区别 普通查询、联合查询、子查询等的复杂查询
1. SIMPLE
简单的 select 查询,查询中不包含子查询或者UNION
2. PRIMARY
查询中若包含任何复杂的子部分,最外层查询则被标记为
3. SUBQUERY
在SELECT或WHERE列表中包含了子查询
4. DERIVED
在FROM列表中包含的子查询被标记为DERIVED(衍生) MySQL会递归执行这些子查询, 把结果放在临时表里。
5. UNION
若第二个SELECT出现在UNION之后,则被标记为UNION; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
6. UNION RESULT
从UNION表获取结果的SELECT
table
显示这一行的数据是关于哪张表的
type
访问类型排列
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是: system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL , 一般来说,得保证查询至少达到range级别,最好能达到ref。
显示查询使用了何种类型, 从最好到最差依次是: system>const>eq_ref>ref>range>index>ALL
system
表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计
const

表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快 如将主键置于where列表中,MySQL就能将该查询转换为一个常量
eq_ref

唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
ref

非唯一性索引扫描,返回匹配某个单独值的所有行. 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而, 它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
range
 
只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引 一般就是在你的where语句中出现了between、<、>、in等的查询 这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。
index

Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。 (也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
all

Full Table Scan,将遍历全表以找到匹配的行
备注:一般来说,得保证查询至少达到range级别,最好能达到ref。
possible_keys
显示可能应用在这张表中的索引,一个或多个。 查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
key
实际使用的索引。如果为NULL,则没有使用索引
查询中若使用了覆盖索引,则该索引仅出现在key列表中

key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
 
ref
显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值
 由key_len可知t1表的idx_col1_col2被充分使用,col1匹配t2表的col1,col2匹配了一个常量,即 'ac'
rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

Extra
包含不适合在其他列中显示但十分重要的额外信息
1. Using filesort
 
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。 MySQL中无法利用索引完成的排序操作称为“文件排序”
2. Using temporary
 
使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
3. USING index

表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错! 如果同时出现using where,表明索引被用来执行索引键值的查找; 如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
覆盖索引(Covering Index)
覆盖索引(Covering Index),一说为索引覆盖。 理解方式一:就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。 理解方式二:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。 注意: 如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *, 因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
4. Using where
表明使用了where过滤
5. using join buffer
使用了连接缓存:
6. impossible where

where子句的值总是false,不能用来获取任何元组
7. select tables optimized away
在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者 对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算, 查询执行计划生成的阶段即完成优化。
8. distinct
优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
热身Case
 第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表 示该查询为外层查询,table列被标记为,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name......】 第二行(执行顺序2):id为3,是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。【select id,name from t1 where other_column=''】 第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】 第四行(执行顺序1):select_type为union,说明第四个select是union里的第二个select,最先执行【select name,id from t2】 第五行(执行顺序5):代表从union的临时表中读取行的阶段,table列的表示用第一个和第四个select的结果进行union操作。【两个结果union操作】
索引优化
索引分析
单表
建表SQL
CREATE TABLE IF NOT EXISTS `article` ( `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, `author_id` INT(10) UNSIGNED NOT NULL, `category_id` INT(10) UNSIGNED NOT NULL, `views` INT(10) UNSIGNED NOT NULL, `comments` INT(10) UNSIGNED NOT NULL, `title` VARBINARY(255) NOT NULL, `content` TEXT NOT NULL ); INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES (1, 1, 1, 1, '1', '1'), (2, 2, 2, 2, '2', '2'), (1, 1, 3, 3, '3', '3'); SELECT * FROM article;
案例
#查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id。 EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; #结论:很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。 #开始优化: # 1.1 新建索引+删除索引 #ALTER TABLE `article` ADD INDEX idx_article_ccv ( `category_id` , `comments`, `views` ); create index idx_article_ccv on article(category_id,comments,views); DROP INDEX idx_article_ccv ON article # 1.2 第2次EXPLAIN EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments >1 ORDER BY views DESC LIMIT 1; EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments =3 ORDER BY views DESC LIMIT 1 #结论: #type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。 #但是我们已经建立了索引,为啥没用呢? #这是因为按照 BTree 索引的工作原理, # 先排序 category_id, # 如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。 #当 comments 字段在联合索引里处于中间位置时, #因comments > 1 条件是一个范围值(所谓 range), #MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。 # 1.3 删除第一次建立的索引 DROP INDEX idx_article_ccv ON article; # 1.4 第2次新建索引 #ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ; create index idx_article_cv on article(category_id,views); # 1.5 第3次EXPLAIN EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; #结论:可以看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果非常理想。 DROP INDEX idx_article_cv ON article;
两表
建表SQL
CREATE TABLE IF NOT EXISTS `class` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE IF NOT EXISTS `book` ( `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`bookid`) ); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
案例
# 下面开始explain分析 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; #结论:type 有All # 添加索引优化 ALTER TABLE `book` ADD INDEX Y ( `card`); # 第2次explain EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; #可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。 #这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有, #所以右边是我们的关键点,一定需要建立索引。 # 删除旧索引 + 新建 + 第3次explain DROP INDEX Y ON book; ALTER TABLE class ADD INDEX X (card); EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; # 然后来看一个右连接查询: #优化较明显。这是因为 RIGHT JOIN 条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引。 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; DROP INDEX X ON class; ALTER TABLE book ADD INDEX Y (card); # 右连接,基本无变化 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;
三表
建表SQL
CREATE TABLE IF NOT EXISTS `phone` ( `phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`phoneid`) ) ENGINE = INNODB; INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
案例
ALTER TABLE `phone` ADD INDEX z ( `card`); ALTER TABLE `book` ADD INDEX Y ( `card`);#上一个case建过一个同样的 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card; # 后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。 ================================================================================== 【结论】 Join语句的优化 尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。 优先优化NestedLoop的内层循环; 保证Join语句中被驱动表上Join条件字段已经被索引; 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;
索引失效(应该避免)
建表SQL
CREATE TABLE staffs ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR (24) NOT NULL DEFAULT '' COMMENT '姓名', age INT NOT NULL DEFAULT 0 COMMENT '年龄', pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '职位', add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间' ) CHARSET utf8 COMMENT '员工记录表' ; INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW()); INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW()); SELECT * FROM staffs; ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);
案例(索引失效)
1. 全值匹配我最爱
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'; EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25; EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev'; 
2. 最佳左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev'; EXPLAIN SELECT * FROM staffs WHERE pos = 'dev'; 
3. 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
 EXPLAIN SELECT * FROM staffs WHERE left(NAME,4) = 'July'; 索引列上使用了表达式,如where substr(a, 1, 3) = 'hhh',where a = a + 1,表达式是一大忌讳,再简单mysql也不认。 有时数据量不是大到严重影响速度时,一般可以先查出来,比如先查所有有订单记录的数据,再在程序中去筛选
4. 存储引擎不能使用索引中范围条件右边的列

5. 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *

6. mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描

7. is null ,is not null 也无法使用索引

8. like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作

9. 字符串不加单引号索引失效

10. 少用or,用它来连接时会索引失效

11. 小总结
假设index(a,b,c) Where语句 索引是否被使用 where a = 3 Y,使用到a where a = 3 and b = 5 Y,使用到a,b where a = 3 and b = 5 and c = 4 Y,使用到a,b,c where b = 3 | where b = 3 and c = 4 | where c = 4 N where a = 3 and c = 5 使用到a, 但是C不可以,中间断了 where a = 3 and b > 4 and c = 7 使用到a和b, c在范围之后,断了 where a = 3 and b like 'kk%' and c = 4 同上
面试题讲解
题目SQL
【建表语句】 create table test03( id int primary key not null auto_increment, c1 char(10), c2 char(10), c3 char(10), c4 char(10), c5 char(10) ); insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5'); insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5'); insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5'); insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5'); insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5'); select * from test03; 【建索引】 create index idx_test03_c1234 on test03(c1,c2,c3,c4); show index from test03; 问题:我们创建了复合索引idx_test03_c1234 ,根据以下SQL分析下索引使用情况? explain select * from test03 where c1='a1'; explain select * from test03 where c1='a1' and c2='a2'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 1) explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 2) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3'; 3) explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4'; 4) explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3'; 5) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3; c3作用在排序而不是查找 6) explain select * from test03 where c1='a1' and c2='a2' order by c3; 7) explain select * from test03 where c1='a1' and c2='a2' order by c4; 出现了filesort 8) 8.1 explain select * from test03 where c1='a1' and c5='a5' order by c2,c3; 只用c1一个字段索引,但是c2、c3用于排序,无filesort 8.2 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; 出现了filesort,我们建的索引是1234,它没有按照顺序来,3 2 颠倒了 9) explain select * from test03 where c1='a1' and c2='a2' order by c2,c3; 10) explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c2,c3; 用c1、c2两个字段索引,但是c2、c3用于排序,无filesort explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c3,c2; 本例有常量c2的情况,和8.2对比 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; filesort 11) explain select * from test03 where c1='a1' and c4='a4' group by c2,c3; 12) explain select * from test03 where c1='a1' and c4='a4' group by c3,c2; Using where; Using temporary; Using filesort
定值、范围还是排序,一般order by是给个范围
group by 基本上都需要进行排序,会有临时表产生
Show Profile
是什么:是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量
默认情况下,参数处于关闭状态,并保存最近15次的运行结果
分析步骤
1. 是否支持,看看当前的mysql版本是否支持
Show variables like 'profiling'; 默认是关闭,使用前需要开启 
2. 开启功能,默认是关闭,使用前需要开启

3. 运行SQL
4. 查看结果,show profiles;

5. 诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL数字号码;

6. 日常开发需要注意的结论
converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
create tmp table 创建临时表,这个要注意
Copying to tmp table on disk 把内存临时表复制到磁盘
locked
一般性建议
对于单键索引,尽量选择针对当前query过滤性更好的索引
在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的
少用Hint强制索引
查询优化
永远小表驱动大表 类似嵌套循环Nested Loop
Case

order by关键字优化
ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序
建表SQL
CREATE TABLE tblA( #id int primary key not null auto_increment, age INT, birth TIMESTAMP NOT NULL ); INSERT INTO tblA(age,birth) VALUES(22,NOW()); INSERT INTO tblA(age,birth) VALUES(23,NOW()); INSERT INTO tblA(age,birth) VALUES(24,NOW()); CREATE INDEX idx_A_ageBirth ON tblA(age,birth); SELECT * FROM tblA;
Case
1

2

MySQL支持二种方式的排序,FileSort和Index,Index效率高. 它指MySQL扫描索引本身完成排序。FileSort方式效率较低。
ORDER BY满足两情况,会使用Index方式排序:
ORDER BY 语句使用索引最左前列
使用Where子句与Order BY子句条件列组合满足索引最左前列
尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀
如果不在索引列上,filesort有两种算法: mysql就要启动双路排序和单路排序
双路排序
MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据, 读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。
单路排序
从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出, 它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间, 因为它把每一行都保存在内存中了。
结论及引申出的问题
由于单路是后出的,总体而言好过双路
但是用单路有问题
在sort_buffer中,方法B比方法A要多占用很多空间,因为方法B是把所有字段都取出, 所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而多次I/O。 本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。
优化策略
增大max_length_for_sort_data参数的设置
增大sort_buffer_size参数的设置
Why
提高Order By的速度 1. Order by时select * 是一个大忌只Query需要的字段, 这点非常重要。在这里的影响是: 1.1 当Query的字段大小总和小于max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 类型时,会用改进后的算法——单路排序, 否则用老算法——多路排序。 1.2 两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I/O,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size。 2. 尝试提高 sort_buffer_size 不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的 3. 尝试提高 max_length_for_sort_data 提高这个参数, 会增加用改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率.
小总结

GROUP BY关键字优化
group by实质是先排序后进行分组,遵照索引建的最佳左前缀
当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置
where高于having,能写在where限定的条件就不要去having限定了。
4. 大数据量处理理论
大数据量处理和大数据分析
大数据的问题
本质问题:就是要操作的基数太大,我们后续的处理思路就是将基数变小
影响结果
系统性能变慢
在处理过程中,用户没有耐心,再反复点击,多次叠加操作......
处理大数据量思路
1. 分
用和不用

“热”数据和“冷”数据
分页前100
论坛置顶
数据库
分区
分库
分表
文件日志存放的数据,想想我们按每天生成的日志
分批处理,分批提交
减少数据操作的基数
2. 缓存
3. 数据库优化
合理简洁高效的数据库表结构设计
索引
数据库集群
4. 牛X算法
5. NoSQL
MongoDB
HBase
Redis
Tair
6. 数据存储方面采用分布式文件系统
HDFS
GFS
7. 海量数据计算分析
Hadoop
Spark
Storm
5. 分区分库分表简介
Mysql分区
是什么
PARTITION 如果一张表的数据量太大的话,那么myd,myi就会变的很大,查找数据就会变的很慢,这个时候我们可以利用mysql的分区功能,在物理上将这一张表对应的三个文件,分割成许多个小块,这样呢,我们查找一条数据时,就不用全部查找了,只要知道这条数据在哪一块,然后在那一块找就行了。如果表的数据太大,可能一个磁盘放不下,这个时候,我们可以把数据分配到不同的磁盘里面去。
能干嘛
逻辑数据分割
提高单一的写和读应用速度
提高分区范围读查询的速度
分割数据能够有多个不同的物理文件路径
高效的保存历史数据
怎么玩
首先查看当前数据库是否支持分区
SHOW VARIABLES LIKE '%partition%';
show plugins;
见后分区类型演示
分区类型及操作
RANGE分区
原理
mysql将会根据指定的拆分策略,,把数据放在不同的表文件上。相当于在文件上,被拆成了小块.但是, 对外给客户的感觉还是一张表,透明的。
案例SQL
建表
CREATE TABLE tbl_new( id INT NOT NULL PRIMARY KEY, title VARCHAR(20) NOT NULL DEFAULT '' )ENGINE MYISAM CHARSET utf8 PARTITION BY RANGE(id)( PARTITION t0 VALUES LESS THAN(10), PARTITION t1 VALUES LESS THAN(20), PARTITION t2 VALUES LESS THAN(MAXVALUE) ); INSERT INTO tbl_new VALUES(1,'z3'); INSERT INTO tbl_new VALUES(2,'z4'); INSERT INTO tbl_new VALUES(3,'z5'); INSERT INTO tbl_new VALUES(4,'z6');
查看分区

查看分区2次

List分区
原理
MySQL中的LIST分区在很多方面类似于RANGE分区。和按照RANGE分区一样,每个分区必须明确定义。它们的主要区别在于, LIST分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中的一个值, 而RANGE分区是从属于一个连续区间值的集合。
案例SQL
建表SQL
create table area( id INT NOT NULL PRIMARY KEY, region varchar(20) )engine myisam charset utf8; insert into area values(1,'bj'); insert into area values(2,'sh'); insert into area values(3,'gz'); insert into area values(4,'sz'); create table user ( uid int not null, userName varchar(20), area_id int )engine myisam charset utf8 partition by list(area_id) ( partition bj values in (1), partition sh values in (2), partition gz values in (3), partition sz values in (4) ); insert into user(uid,userName,area_id) values(1,'z3',1); insert into user(uid,userName,area_id) values(2,'z4',2); insert into user(uid,userName,area_id) values(3,'z5',3);
查看分区

其它
Hash分区
Key分区
子分区
对NULL值的处理
原理
MySQL中的分区在禁止空值NULL上没有进行处理,无论它是一个列值还是一个用户定义表达式的值, 一般而言,在这种情况下MySQL把NULL当做零。如果你不希望出现类似情况,建议在设计表时声明该列“NOT NULL”
Mysql分库
是什么
为什么分库
为什么要分库分库 数据库集群环境后都是多台slave,基本满足了读取操作; 但是写入或者说大数据、频繁的写入操作对master性能影响就比较大, 这个时候,单库并不能解决大规模并发写入的问题。 优点 减少增量数据写入时的锁对查询的影响。 由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘IO,时延变短。 但是它无法解决单表数据量太大的问题。
是什么
是什么? 一个库里表太多了,导致了海量数据,系统性能下降,把原本存储于一个库的表拆分存储到多个库上, 通常是将表按照功能模块、关系密切程度划分出来,部署到不同库上。
Mysql分表
是什么
垂直拆分
垂直分表, 通常是按照业务功能的使用频次,把主要的、热门的字段放在一起做为主要表; 然后把不常用的,按照各自的业务属性进行聚集,拆分到不同的次要表中;主要表和次要表的关系一般都是一对一的。
水平拆分(数据分片)
mysql单表的容量不超过500W,否则建议水平拆分
能干嘛
怎么玩
切分策略
导航路由
是否有一些开源方案
MySQL Fabric
网址:http://www.mysql.com/products/enterprise/fabric.html MySQL Fabric 是一个用于管理 MySQL 服务器群的可扩展框架。该框架实现了两个特性 — 高可用性 (HA) 以及使用数据分片的横向扩展 官方推荐,但是2014年左右才推出,是真正的分表,不是代理的(不同于mysql-proxy)。 未来很有前景,目前属于测试阶段还没大规模运用于生产,期待它的升级。 
Atlas
Atlas是由 Qihoo 360, Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。 它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了大量bug,添加了很多功能特性。目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平台,每天承载的读写请求数达几十亿条。 主要功能: * 读写分离 * 从库负载均衡 * IP过滤 * SQL语句黑白名单 * 自动分表,只支持单库多表,不支持分布式分表,同理,该功能我们可以用分库来代替,多库多表搞不定。 网址: https://github.com/Qihoo360/Atlas
TDDL
江湖外号:头都大了 淘宝根据自己的业务特点开发了TDDL(Taobao Distributed Data Layer )框架,主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制,它是一个基于集中式配置的 jdbc datasource实现,具有主备,读写分离,动态数据库配置等功能。 TDDL所处的位置(tddl通用数据访问层,部署在客户端的jar包,用于将用户的SQL路由到指定的数据库中):  淘宝很早就对数据进行过分库的处理, 上层系统连接多个数据库,中间有一个叫做DBRoute的路由来对数据进行统一访问。DBRoute对数据进行多库的操作、数据的整合,让上层系统像操作 一个数据库一样操作多个库。但是随着数据量的增长,对于库表的分法有了更高的要求,例如,你的商品数据到了百亿级别的时候,任何一个库都无法存放了,于是 分成2个、4个、8个、16个、32个……直到1024个、2048个。好,分成这么多,数据能够存放了,那怎么查询它?这时候,数据查询的中间件就要能 够承担这个重任了,它对上层来说,必须像查询一个数据库一样来查询数据,还要像查询一个数据库一样快(每条查询在几毫秒内完成),TDDL就承担了这样一 个工作。在外面有些系统也用DAL(数据访问层) 这个概念来命名这个中间件。  系出名门,淘宝诞生。功能强大,阿里开源(部分) 主要优点: 1.数据库主备和动态切换 2.带权重的读写分离 3.单线程读重试 4.集中式数据源信息管理和动态变更 5.剥离的稳定jboss数据源 6.支持mysql和oracle数据库 7.基于jdbc规范,很容易扩展支持实现jdbc规范的数据源 8.无server,client-jar形式存在,应用直连数据库 9.读写次数,并发度流程控制,动态变更 10.可分析的日志打印,日志流控,动态变更 TDDL必须要依赖diamond配置中心(diamond是淘宝内部使用的一个管理持久配置的系统,目前淘宝内部绝大多数系统的配置,由diamond来进行统一管理,同时diamond也已开源)。 TDDL动态数据源使用示例说明:http://rdc.taobao.com/team/jm/archives/1645 diamond简介和快速使用:http://jm.taobao.org/tag/diamond%E4%B8%93%E9%A2%98/ TDDL源码:https://github.com/alibaba/tb_tddl TDDL复杂度相对较高。当前公布的文档较少,只开源动态数据源,分表分库部分还未开源,还需要依赖diamond,不推荐使用。
MySQL proxy
官网提供的,小巧精干型的,但是能力有限,对于大数据量的分库分表无能为力,适合中小型的互联网应用,基本上 mysql-proxy - master/slave就可以构成一个简单版的读写分离和负载均衡
小总结
分库分表演变过程
单库多表--->读写分离主从复制--->垂直分库,每个库又可以带着salve--->继续垂直分库,极端情况单库单表 --->分区(变相的水平拆分表,只不过是单库的)--->水平分表后再放入多个数据库里,进行分布式部署 单库多表 读写分离主从复制 垂直分库(每个库又可以带salve) 继续垂直分库,理论上可以到极端情况,单库单表 分区(partition是变相的水平拆分,只不过是单库内进行) 终于到水平分表,后续放入多个数据库里,进行分布式部署,终极method。 但是理论上OK,实际上中间的各种通信、调度、维护和编码要求,更加高
分库分表后的难题
分布式事务的问题,数据的完整性和一致性问题。 数据操作维度问题:用户、交易、订单各个不同的维度,用户查询维度、产品数据分析维度的不同对比分析角度。 跨库联合查询的问题,可能需要两次查询 跨节点的count、order by、group by以及聚合函数问题,可能需要分别在各个节点上得到结果后在应用程序端进行合并 额外的数据管理负担,如:访问数据表的导航定位 额外的数据运算压力,如:需要在多个节点执行,然后再合并计算 程序编码开发难度提升,没有太好的框架解决,更多依赖业务看如何分,如何合,是个难题。
不到最后一步,轻易不用进行水平分表
6. MySql锁机制
概述
定义
锁是计算机协调多个进程或线程并发访问某一资源的机制。 在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
生活购物
打个比方,我们到淘宝上买一件商品,商品只有一件库存,这个时候如果还有另一个人买, 那么如何解决是你买到还是另一个人买到的问题?  这里肯定要用到事物,我们先从库存表中取出物品数量,然后插入订单,付款后插入付款表信息, 然后更新商品数量。在这个过程中,使用锁可以对有限的资源进行保护,解决隔离和并发的矛盾。
锁的分类
从对数据操作的类型(读\写)分
读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
从对数据操作的粒度分
为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很耗资源的事情(涉及获取,检查,释放锁等动作),因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity)”的概念。 一种提高共享资源并发发性的方式是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式是,只对会修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
表锁
行锁
三锁
表锁(偏读)
特点
偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
案例分析
建表SQL
【表级锁分析--建表SQL】 create table mylock( id int not null primary key auto_increment, name varchar(20) )engine myisam; insert into mylock(name) values('a'); insert into mylock(name) values('b'); insert into mylock(name) values('c'); insert into mylock(name) values('d'); insert into mylock(name) values('e'); select * from mylock; 【手动增加表锁】 lock table tableName1 read(write),tableName2 read(write),其它; 【查看表上加过的锁】 show open tables;  【释放表锁】 unlock tables;
加读锁
我们为mylock表加read锁(读阻塞写例子) session_1 session_2 获得表mylock的READ锁定  连接终端 当前session可以查询该表记录  其他session也可以查询该表的记录  当前session不能查询其它没有锁定的表  其他session可以查询或者更新未锁定的表  当前session中插入或者更新锁定的表都会提示错误:锁定的表当前session中插入或者更新锁定的表都会提示错误:  其他session插入或者更新锁定表会一直等待获得锁:会一直等待其他session插入或者更新锁定表会一直等待获得锁:  释放锁  Session2获得锁,插入操作完成: 
加写锁
我们为mylock表加write锁(MyISAM存储引擎的写阻塞读例子) session_1 session_2 获得表mylock的WRITE锁定  连接终端 当前session对锁定表的查询+更新+插入操作都可以执行:  其他session对锁定表的查询被阻塞,需要等待锁被释放:  释放锁  Session2获得锁,查询返回: 
案例结论
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。 MySQL的表级锁有两种模式: 表共享读锁(Table Read Lock) 表独占写锁(Table Write Lock) 锁类型 可否兼容 读锁 写锁 读锁 是 是 否 写锁 是 否 否 结论: 结合上表,所以对MyISAM表进行操作,会有以下情况: 1、对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。 2、对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。 简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。
表锁分析
【看看哪些表被加锁了】 mysql>show open tables; 【如何分析表锁定】 可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定: SQL:show status like 'table%';  这里有两个状态变量记录MySQL内部表级锁定的情况,两个变量说明如下: Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1 ; Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况; 此外,Myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞
行锁(偏写)
特点
偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁
由于行锁支持事务,复习老知识
事务(Transaction)及其ACID属性
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。 l 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。 l 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。 l 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。 l 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
并发事务处理带来的问题
更新丢失(Lost Update)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新。 例如,两个程序员修改同一java文件。每程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前一个程序员所做的更改。 如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。
脏读(Dirty Reads)
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。 一句话:事务A读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取 的数据无效,不符合一致性要求。
不可重复读(Non-Repeatable Reads)
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。 一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性
幻读(Phantom Reads)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。 一句话:事务A读取到了事务B体提交的新增数据,不符合隔离性。 多说一句:幻读和脏读有点类似, 脏读是事务B里面修改了数据, 幻读是事务B里面新增了数据。
事务隔离级别
脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。  数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。
案例分析
建表SQL
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb; insert into test_innodb_lock values(1,'b2'); insert into test_innodb_lock values(3,'3'); insert into test_innodb_lock values(4,'4000'); insert into test_innodb_lock values(5,'5000'); insert into test_innodb_lock values(6,'6000'); insert into test_innodb_lock values(7,'7000'); insert into test_innodb_lock values(8,'8000'); insert into test_innodb_lock values(9,'9000'); insert into test_innodb_lock values(1,'b1'); create index test_innodb_a_ind on test_innodb_lock(a); create index test_innodb_lock_b_ind on test_innodb_lock(b); select * from test_innodb_lock;
行锁定基本演示
行锁定基本演示 Session_1 Session_2   更新但是不提交,没有手写commit;  Session_2被阻塞,只能等待  提交更新  解除阻塞,更新正常进行  commit命令执行 下面试试1号会话更新a =1 下面试试2号会话更新a =9
无索引行锁升级为表锁
Session_1 Session_2 正常情况,各自锁定各自的行,互相不影响,一个2000另一个3000   由于在column字段b上面建了索引,如果没有正常使用,会导致行锁变表锁 比如没加单引号导致索引失效,行锁变表锁  被阻塞,等待。只到Session_1提交后才阻塞解除,完成更新 
间隙锁危害
间隙锁带来的插入问题 Session_1 Session_2  阻塞产生,暂时不能插入  commit; 阻塞解除,完成插入  【什么是间隙锁】 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”, InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。 【危害】 因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。 间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害
案列结论
Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,Innodb的整体性能和MyISAM相比就会有比较明显的优势了。 但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
行锁分析
【如何分析行锁定】 通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况 mysql>show status like 'innodb_row_lock%';  对各个状态量的说明如下: Innodb_row_lock_current_waits:当前正在等待锁定的数量; Innodb_row_lock_time:从系统启动到现在锁定总时间长度; Innodb_row_lock_time_avg:每次等待所花平均时间; Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间; Innodb_row_lock_waits:系统启动后到现在总共等待的次数; 对于这5个状态变量,比较重要的主要是 Innodb_row_lock_time_avg(等待平均时长), Innodb_row_lock_waits(等待总次数) Innodb_row_lock_time(等待总时长)这三项。 尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。
优化建议
尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
合理设计索引,尽量缩小锁的范围
尽可能较少检索条件,避免间隙锁
尽量控制事务大小,减少锁定资源量和时间长度
尽可能低级别事务隔离
页锁
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
了解一下即可
开销、加锁速度、死锁、粒度、并发性能 只能就具体应用的特点来说哪种锁更合适
7. 主从复制
复制的基本原理
slave会从master读取binlog来进行数据同步
三步骤+原理图
 MySQL复制过程分成三步: 1 master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events; 2 slave将master的binary log events拷贝到它的中继日志(relay log); 3 slave重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的
master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events
slave将master的binary log events拷贝到它的中继日志(relay log)
slave重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的
复制的基本原则
每个slave只有一个master
每个slave只能有一个唯一的服务器ID
每个master可以有多个salve
复制的最大问题
延时
一主一从常见配置
mysql版本一致且后台以服务运行
主从都配置在[mysqld]结点下,都是小写
主机修改my.ini配置文件

1. [必须]主服务器唯一ID
server-id=1
2. [必须]启用二进制日志
log-bin=自己本地的路径/mysqlbin
log-bin=D:/devSoft/MySQLServer5.5/data/mysqlbin
3. [可选]启用错误日志
log-err=自己本地的路径/mysqlerr
log-err=D:/devSoft/MySQLServer5.5/data/mysqlerr
4. [可选]根目录
basedir="自己本地路径"
basedir="D:/devSoft/MySQLServer5.5/"
5. [可选]临时目录
tmpdir="自己本地路径"
tmpdir="D:/devSoft/MySQLServer5.5/"
6. [可选]数据目录
datadir="自己本地路径/Data/"
datadir="D:/devSoft/MySQLServer5.5/Data/"
7. read-only=0
主机,读写都可以
8. [可选]设置不要复制的数据库
binlog-ignore-db=mysql
9. [可选]设置需要复制的数据库
binlog-do-db=需要复制的主数据库名字
从机修改my.cnf配置文件

[必须]从服务器唯一ID
[可选]启用二进制日志
因修改过配置文件,请主机+从机都重启后台mysql服务
主机从机都关闭防火墙
windows手动关闭
关闭虚拟机linux防火墙 service iptables stop
在Windows主机上建立帐户并授权slave
GRANT REPLICATION SLAVE ON *.* TO 'zhangsan'@'从机器数据库IP' IDENTIFIED BY '123456';

flush privileges;
查询master的状态
show master status;

记录下File和Position的值
执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化
在Linux从机上配置需要复制的主机
CHANGE MASTER TO MASTER_HOST='主机IP',MASTER_USER='zhangsan',MASTER_PASSWORD='123456',MASTER_LOG_FILE='File名字',MASTER_LOG_POS=Position数字;
CHANGE MASTER TO MASTER_HOST='192.168.124.3', MASTER_USER='zhangsan', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysqlbin.具体数字',MASTER_LOG_POS=具体值; 
启动从服务器复制功能
start slave;
show slave status\G

下面两个参数都是Yes,则说明主从配置成功!
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
主机新建库、新建表、insert记录,从机复制
如何停止从服务复制功能
stop slave;