1 主从复制

01.主从复制的作用
    主数据库出现问题,可以切换到从数据库。
    可以进行数据库层面的读写分离。
    可以在从数据库上进行日常备份。

02.MySQL主从复制工作原理
    在主库上把数据更高记录到二进制日志
    从库将主库的日志复制到自己的中继日志
    从库读取中继日志的事件,将其重放到从库数据中

03.MySQL主从同步是如何实现的?
    复制(replication)是MySQL数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。
    总体来说,replication的工作原理分为以下3个步骤:
    主服务器(master)把数据更改记录到二进制日志(binlog)中。
    从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中。
    从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。

04.基本原理流程,3个线程以及之间的关联
    主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
    从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;
    从:sql执行线程——执行relay log中的语句;

05.MySQL主从复制解决的问题
    数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
    负载均衡:降低单个服务器的压力
    高可用和故障切换:帮助应用程序避免单点失败
    升级测试:可以用更高版本的MySQL作为从库
1.MySQL版本
	5.0 - 5.1: 早期产品的延续,升级维护
	5.4 - 5.x: MySQL整合了三方公司的新存储引擎 (推荐5.5)
	
2.rpm安装MySQL
	rpm -ivh mysql-community-server-5.7.20-1.el6.x86_64.rpm	--force --nodeps
	rpm -ivh mysql-community-client-5.7.20-1.el6.x86_64.rpm	--force --nodeps
	验证:mysqladmin --version

3.配置MySQL
	a.启动MySQL服务
		systemctl start mysqld.service
	b.查看MySQL状态
		systemctl status mysqld.service
	c.从日志中获取临时密码
		grep "password" /var/log/mysqld.log
	d.登录MySQL
		mysql -uroot -p
	e.修改密码
		mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'zY..0605...';
	d.统一字符编码为utf8	
		mysql> set character_set_database=utf8;
		mysql> set character_set_server=utf8;
	f.开启MySQL远程访问
		mysql> grant all privileges on *.* to 'root'@'%' identified by 'zY..0605...' with grant option;
		mysql> flush privileges; 		//刷新设置立即生效
		mysql> exit						//退出	
	d.重启
		reboot
		
4.firewalld添加开放端口
	a.mysql端口3306
		firewall-cmd --zone=public --add-port=3306/tcp --permanent
	b.Tomcat端口8080
		firewall-cmd --zone=public --add-port=8080/tcp --permanent
	c.重新载入
		firewall-cmd --reload

5.运程cmd登录MySQL
	mysql -h 192.168.2.128 -p 3306 -u root -p

6.如何确定MySQL服务已经启动?
	a.chkconfig命令
		chkconfig --list
	b.查询端口
		netstat -tulpn
	c.查询进程
		ps -ef | grep mysqld
		ps -ef | grep mysqld_safe

7.MySQL常见命令
							CentOS 6 							CentOS 7
	启动命令			service mysqld start		systemctl start mysqld.service
	关闭命令			service mysqld stop			systemctl stop mysqld.service
	重启命令			service mysqld restart		systemctl restart mysqld.service
	查看服务状态	       service mysqld status	   systemctl status mysqld.service
	查看MySQL系统配置    cat /etc/my.cnf						 cat /etc/my.cnf
	
8.MySQL服务:常见报错/var/lib/mysql/mysql.sock不存在
							CentOS 6 							CentOS 7
	手动启动服务		   /etc/init.d/mysql start			/etc/init.d/mysqld start
	开机自启			 chkconfig mysql on				systemctl enable mysqld.service 
	关闭开机自启		    chkconfig mysql off			   systemctl disable mysqld.service 
	检查开机自启				 ntsysv								 ntsysv	

9.彻底卸载MySQL
	a.查看是否有安装的MySQL:
		rpm -qa | grep -i mysql		//方式一
		yum list install mysql*		//方式二
	b.卸载MySQL安装包
		yum remove mysql mysql-server mysql-libs compat-mysql51
		yum remove mysql-community-release、
		rpm -e --nodeps mysql-community-libs-5.7.22-1.el7.x86_64
		rpm -e –nodeps mysql57-community-release-el7-11.noarch	
	c.删除残留的MySQL目录或文件
		i.残留的MySQL目录	
			whereis mysql
		ii.残留的MySQL文件
			find / -name mysql
		iii.删除
			rm -rf xxx
	d.删除MySQL配置文件
		rm –rf /usr/my.cnf
		rm -rf /root/.mysql_sercret
	e.删除MySQL开机自启服务
		chkconfig --list | grep -i mysql
		chkconfig --del mysqld 		//服务名,自己设置的名字
		
10.yum安装MySQL
	a.下载MySQL的yum源
		wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm 
	b.安装yum源
		rpm -ivh mysql57-community-release-el7-11.noarch.rpm 
	c.正式安装MySQL
		yum install mysql-community-server
		
11.免密码登录MySQL
	a.停止MySQL服务
		systemctl stop mysqld.service
	b.以不检查权限的方式启动mysqld,root身份运行
		mysqld --user=root --skip-grant-tables &
	c.免密码登录MySQL
		mysql -u root
	d.设置密码
		UPDATE mysql.user SET authentication_string=PASSWORD('密码') where USER='root';
		ALTER USER 'root'@'localhost' IDENTIFIED BY '密码';
		SET PASSWORD FOR root=PASSWORD('密码');
		
12.修改编码
	a.查看字符编码
		mysql> status
		mysql> show variables like '%char%' ;	
	b.修改方式一:
		mysql> set character_set_database=utf8;
		mysql> set character_set_server=utf8;
	c.修改方式二:vi /etc/my.cnf
    	[mysql]
        default-character-set=utf8

		[client]
		default-character-set=utf8

		[mysqld]
		character_set_server=utf8
		character_set_client=utf8
		collation_server=utf8_general_ci
		
13.数据库存放目录:#ps -ef|grep mysql
	a.数据库目录
		datadir=/var/lib/mysql 
	b.pid文件目录
		pid-file=/var/lib/mysql/bigdata01.pid
	c.MySQL核心目录
		/var/lib/mysql			安装目录
		/usr/share/mysql  		配置文件
		/usr/bin				命令目录(mysqladmin、mysqldump等)
		/etc/init.d/mysql		启停脚本
	d.MySQL配置文件
		my-huge.cnf				高端服务器  1-2G内存
		my-large.cnf   			中等规模
		my-medium.cnf  			一般
		my-small.cnf  			较小
		但是,以上配置文件mysql默认不能识别,默认只能识别 /etc/my.cnf
		若采用 my-huge.cnf,通过命令:#cp /usr/share/mysql/my-huge.cnf /etc/my.cnf
	e.注意
		MySQL5.5:默认配置文件/etc/my.cnf
		MySQL5.6:默认配置文件/etc/mysql-default.cnf
14.表名问题
	a.MySQL表名
		windows:不区分大小写
		linux:区分大小写
	b.Oracle表名
		windows/linux都不区分大小写

15.数据库引擎
	a.MySQL默认引擎InnoDB,
	b.建立数据表时,指定MyISAM引擎:create table xx(id int,name varchar) engine=MyISAM
	
16.数据库方言:各个数据库的底层执行细节、优化细节有所区别,因此指定方言可以让程序更加高效

2 存储过程与存储函数

01.概念
    存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。
    如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

02.优点
    存储过程是预编译过的,执行效率高。
    存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
    安全性高,执行存储过程需要有一定权限的用户。
    存储过程可以重复使用,减少数据库开发人员的工作量。
    ---------------------------------------------------------------------------------------------------------
    存储过程增强了SQL语言的功能和灵活性。存储过程可以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
    存储过程允许标准组件是编程。存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。
    存储过程能实现较快的执行速度。如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。
    
03.缺点
    调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。
    移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。
    重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
    如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。

04.存储过程、存储函数的区别
    函数(function)总是向调用者返回数据,并且一般只返回一个值;
    存储过程(procedure)不直接返回数据,但可以改变输出参数的值,这可以近似看作能返回值,且存储过程输出参数的值个数没有限制。

3 MySQL分层与存储引擎

01.默认引起
    MyISAM:性能优先(表锁)
    InnoDB(默认):事务优先(适合高并发操作、行锁)

02.InnoDB引擎的4大特性
    插入缓冲(insert buffer)
    二次写(double write)
    自适应哈希索引(ahi)
    预读(read ahead)

03.常用的存储引擎有以下
    Innodb引擎(默认):Innodb引擎提供了对数据库ACID事务的支持。还提供了行级锁和外键的约束。设计的目标就是处理大数据容量的数据库系统。
    MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
    MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

03.MyISAM索引与InnoDB索引的区别?
    InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
    InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
    MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
    InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

4 索引与B树

4.1 B树

01.B树和B+树的区别
    在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
    B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

02.使用B树的好处
    B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。
    这种特性使得B树在特定数据重复多次查询的场景中更加高效。

03.使用B+树的好处
    由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。
    B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,
    然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

4.2 索引概念

01.什么是索引?
    索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
    索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。
    索引的实现通常使用B树及其变种B+树。
    更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

02.索引有哪几种类型?
    主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
    唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
              可以通过 ALTER TABLE table_name ADD UNIQUE (column);                               创建唯一索引
              可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2);                      创建唯一组合索引
    普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。
              可以通过ALTER TABLE table_name ADD INDEX index_name (column);                      创建普通索引
              可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);    创建组合索引
    全文索引: 是目前搜索引擎使用的一种关键技术。
               可以通过ALTER TABLE table_name ADD FULLTEXT (column);                             创建全文索引
    组合索引

03.主键索引与唯一索引的区别
    主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
    主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。
    ---------------------------------------------------------------------------------------------------------
    唯一性索引列允许空值,而主键列不允许为空值。
    主键列在创建时,已经默认为空值 ++ 唯一索引了。
    ---------------------------------------------------------------------------------------------------------
    一个表最多只能创建一个主键,但可以创建多个唯一索引。
    主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
    主键可以被其他表引用为外键,而唯一索引不能。

04.索引的优点
    可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
    通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

05.索引的缺点
    时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,
    索引也要动态的维护,会降低增/改/删的执行效率;
    空间方面:索引需要占物理空间。

06.索引使用场景
    当数据多且字段值有相同的值得时候用普通索引。
    当字段多且字段值没有重复的时候用唯一索引。
    当有多个字段名都经常被查询的话用复合索引。
    普通索引不支持空值,唯一索引支持空值。
    但是,若是这张表增删改多而查询较少的话,就不要创建索引了,因为如果你给一列创建了索引,那么对该列进行增删改的时候,都会先访问这一列的索引,
    若是增,则在这一列的索引内以新填入的这个字段名的值为名创建索引的子集,
    若是改,则会把原来的删掉,再添入一个以这个字段名的新值为名创建索引的子集,
    若是删,则会把索引中以这个字段为名的索引的子集删掉。
    所以,会对增删改的执行减缓速度,
    所以,若是这张表增删改多而查询较少的话,就不要创建索引了。

4.3 索引手段

01.索引是越多越好吗?
    索引并非越多越好,一个表中如有大量的索引,不仅占用磁盘空间,还会影响INSERT、DELETE、UPDATE等语句的性能,
    因为在表中的数据更改时,索引也会进行调整和更新。

02.说出一些数据库优化方面的经验?
    有外键约束的话会影响增删改的性能,如果应用程序可以保证数据库的完整性那就去除外键
    Sql语句全部大写,特别是列名大写,因为数据库的机制是这样的,sql语句发送到数据库服务器,数据库首先就会把sql编译成大写在执行,如果一开始就编译成大写就不需要了把sql编译成大写这个步骤了
    如果应用程序可以保证数据库的完整性,可以不需要按照三大范式来设计数据库
    其实可以不必要创建很多索引,索引可以加快查询速度,但是索引会消耗磁盘空间
    如果是jdbc的话,使用PreparedStatement不使用Statement,来创建SQl,PreparedStatement的性能比Statement的速度要快,使用PreparedStatement对象SQL语句会预编译在此对象中,PreparedStatement对象可以多次高效的执行

03.怎么优化SQL查询语句
    对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
    用索引可以提高查询
    SELECT子句中避免使用*号,尽量全部大写SQL
    应尽量避免在 where 子句中对字段进行 is null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,使用 IS NOT NULL
    where 子句中使用 or 来连接条件,也会导致引擎放弃使用索引而进行全表扫描
    in 和 not in 也要慎用,否则会导致全表扫描

04.你怎么知道SQL语句性能是高还是低
    查看SQL的执行时间
    使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MYSQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

-------------------------------------------------------------------------------------------------------------

01.SQL的执行顺序
    FROM:将数据从硬盘加载到数据缓冲区,方便对接下来的数据进行操作。
    WHERE:从基表或视图中选择满足条件的元组。(不能使用聚合函数)
    JOIN(如right left 右连接-------从右边表中读取某个元组,并且找到该元组在左边表中对应的元组或元组集)
    ON:join on实现多表连接查询,推荐该种方式进行多表查询,不使用子查询。
    GROUP BY:分组,一般和聚合函数一起使用。
    HAVING:在元组的基础上进行筛选,选出符合条件的元组。(一般与GROUP BY进行连用)
    SELECT:查询到得所有元组需要罗列的哪些列。
    DISTINCT:去重的功能。
    UNION:将多个查询结果合并(默认去掉重复的记录)。
    ORDER BY:进行相应的排序。
    LIMIT 1:显示输出一条数据记录(元组)

02.数据库结构优化
    一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。
    需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。
    将字段很多的表分解成多个表
    对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。
    因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
    增加中间表
    对于需要经常联合查询的表,可以建立中间表以提高查询效率。
    通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。
    增加冗余字段
    设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。
    但是,合理的加入冗余字段可以提高查询速度。
    表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

-------------------------------------------------------------------------------------------------------------

01.如何优化查询过程中的数据访问
    访问数据太多导致查询性能下降
    确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
    确认MySQL服务器是否在分析大量不必要的数据行
    避免犯如下SQL语句错误
    避免查询不需要的数据。解决办法:使用limit解决
    多表关联返回全部列。解决办法:指定列名
    总是返回全部列。解决办法:避免使用SELECT *
    重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
    使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过如下技巧去优化:
    使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果。
    改变数据库和表的结构,修改数据表范式
    重写SQL语句,让优化器可以以更优的方式执行查询。

02.如何优化长难的查询语句
    分析是一个复杂查询还是多个简单查询速度快
    MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多
    使用尽可能小的查询是好的,但是有时将一个大的查询分解为多个小的查询是很有必要的。
    将一个大的查询分为多个小的相同的查询
    一次性删除1000万的数据要比一次删除1万,暂停一会的方案更加损耗服务器开销。
    分解关联查询,让缓存的效率更高。
    执行单个查询可以减少锁的竞争。
    在应用层做关联更容易对数据库进行拆分。
    查询效率会有大幅提升。
    较少冗余记录的查询。

03.优化特定类型的查询语句
    count(*)会忽略所有的列,直接统计所有列数,不要使用count(列名)
    MyISAM中,没有任何where条件的count(*)非常快。
    当有where条件时,MyISAM的count统计不一定比其它引擎快。
    可以使用explain查询近似值,用近似值替代count(*)
    增加汇总表
    使用缓存

04.优化关联查询
    确定ON或者USING子句中是否有索引。
    确保GROUP BY和ORDER BY只有一个表中的列,这样MySQL才有可能使用索引。

05.优化子查询
    用关联查询替代
    优化GROUP BY和DISTINCT
    这两种查询据可以使用索引来优化,是最有效的优化方法
    关联查询中,使用标识列分组的效率更高
    如果不需要ORDER BY,进行GROUP BY时加ORDER BY NULL,MySQL不会再进行文件排序。
    WITH ROLLUP超级聚合,可以挪到应用程序处理

06.优化LIMIT分页
    LIMIT偏移量大的时候,查询效率较低
    可以记录上次查询的最大ID,下次查询时直接根据该ID来查询

07.优化UNION查询
    UNION ALL的效率高于UNION

08.优化WHERE子句
    多数数据库都是从左往右的顺序处理条件的,把能够过滤更多数据的条件放到前面,把过滤少的条件放在后面

-------------------------------------------------------------------------------------------------------------

01.大表数据查询,怎么优化
    优化shema、sql语句+索引;
    第二加缓存,memcached, redis;
    主从复制,读写分离;
    垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;
    水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;

02.大表怎么优化?分库分表了是怎么做的?分表分库了有什么问题?有用到中间件么?他们的原理知道么?
    当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
    限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
    读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读;
    缓存: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;
    ---------------------------------------------------------------------------------------------------------
    还有就是通过分库分表的方式进行优化,主要有垂直分区、垂直分表和水平分区、水平分表
        垂直分区
            根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,
            可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
            简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。
        垂直分表
            把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中
            适用场景
            1、如果一个表中某些列常用,另外一些列不常用
            2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
        水平分区
            保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 
            水平拆分可以支撑非常大的数据量。
            水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。
            举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
        水平分表
            表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数

4.4 创建、删除、查询

文章参考:https://www.cnblogs.com/annsshadow/p/5037667.html
 
1.索引分类
	主键索引:不能重复		   id不能是null
	唯一索引:不能重复		   id可以是null
	单值索引:单列age		  	一个表可以多个单值索引
	复合索引:多个列构成的索引 	相当于二级目录(name,age)、(a,b,c,d,...,n)
 
2.创建索引
	方式一:create 索引类型 索引名 on 表(字段)
        a.主键
            create table tb(
            	id int(4) auto_increment,
            	name varchar(5),
            	dept varchar(5),
            	primary key(id)		
            )ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
		b.单值
			create index dept_index on tb(dept);
		c.唯一
			create unique index name_index on tb(name);
		d.复合索引
			create index dept_name_index on tb(dept,name);
			
	方式二:alter table 表名 索引类型 索引名(字段)
        a.主键
            alter table tb add primary key(id);
		b.单值
			alter table tb add index dept_index(dept);
		c.唯一
			alter table tb add unique index name_index(name);
		d.复合索引
			alter table tb add index dept_name_index(dept,name);
	
	注意:如果一个字段是primary key,则改字段默认就是“主键索引”	
	
3.删除索引	drop index 索引名 on 表名 ;
		   drop index name_index on tb ;
 
4.查询索引	show index from 表名;
		   show index from 表名\G

5 MySQL执行计划

1.创建表
create table course(
	cid int(3),
	cname varchar(20),
	tid int(3)
);
 
create table teacher(
	tid int(3),
	tname varchar(20),
	tcid int(3)
);
 
create table teacherCard(
	tcid int(3),
	tcdesc varchar(200)
);
 
2.准备数据
insert into course values(1,'java',1);
insert into course values(2,'html',1);
insert into course values(3,'sql',2);
insert into course values(4,'web',3);
 
insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);
 
insert into teacherCard values(1,'tzdesc');
insert into teacherCard values(2,'twdesc');
insert into teacherCard values(3,'tldesc');

5.1 id:编号

5.2 select_type:查询类型

5.3 type:索引类型

5.4 possible_keys / key / key_len

5.5 ref / rows / Extra

6 优化案例

6.1 单表优化

1.创建表
create table book(
	bid int(4) primary key,
	name varchar(20) not null,
	authorid int(4) not null,
	publicid int(4) not null,
	typeid int(4) not null 
);
 
2.准备数据
insert into book values(1,'tjava',1,1,2) ;
insert into book values(2,'tc',2,1,2) ;
insert into book values(3,'wx',3,2,1) ;
insert into book values(4,'math',4,2,3) ;	
 
3.需要优化的SQL:查询authorid=1且 typeid为2或3的 bid
explain select bid from book where typeid in(2,3) and authorid=1 order by typeid desc ;
 
优化一:按照上述的SQL中出现的字段,依次加上索引
alter table book add index idx_bta (bid,typeid,authorid);
 
drop index idx_bta on book; --索引一旦进行升级优化,需要将之前废弃的索引删掉,防止干扰。
 
优化二:根据SQL实际解析的顺序,调整索引的顺序:
alter table book add index idx_tab (typeid,authorid,bid); --虽然可以回表查询bid,但是将bid放到索引中 可以提升使用using index ;
 
drop index idx_tab on book; --索引一旦进行 升级优化,需要将之前废弃的索引删掉,防止干扰
 
优化三(之前是index级别),因为范围查询in有时会失效,因此交换索引的顺序,将typeid in(2,3)放到最后
alter table book add index idx_atb (authorid,typeid,bid);
 
 
4.小结:a.(using filesort) 最佳做前缀,保持索引的定义和使用的顺序一致性
       b.(index级别) 根据SQL实际解析的顺序,逐步优化索引
       c.(ref级别) 将含In的范围查询 放到 where条件的最后,防止失效
 
5.本例中同时出现了Using where(需要回原表); Using index(不需要回原表)?
	a.Using where(需要回原表):
		where authorid=1 and typeid in(2,3)中authorid在索引(authorid,typeid,bid)中,因此不需要回原表(直接在索引表中能查到)
	b.Using index(不需要回原表):
		而typeid虽然也在索引(authorid,typeid,bid)中,但是含in的范围查询已经使该typeid索引失效,因此相当于没有typeid这个索引,所以需要回原表(using where
 
6.验证本次SQL优化中,一半需要回原表,一半不需要回原表?
	证明一:
		explain select bid from book where  authorid=1 and  typeid =3 order by typeid desc ;---使用 typeid=3 代替 typeid in(2,3) ,没有了In,则不会出现using where
	证明二:
		通过key_len证明In可以使索引失效

6.2 两表优化

1.创建表
create table teacher2(
    tid int(4) primary key,
	cid int(4) not null
);
 
create table course2(
	cid int(4) ,
	cname varchar(20)
);
 
2.准备数据
insert into teacher2 values(1,2);
insert into teacher2 values(2,1);
insert into teacher2 values(3,3);
 
insert into course2 values(1,'java');
insert into course2 values(2,'python');
insert into course2 values(3,'kotlin');
 
3.需要优化的SQL
explain select *from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java
 
4.优化:加索引
alter table teacher2 add index index_teacher2_cid(cid) ; 
alter table course2 add index index_course2_cname(cname);

6.3 三表优化

1.创建表
create table test03(
	a1 int(4) not null,
	a2 int(4) not null,
  	a3 int(4) not null,
  	a4 int(4) not null
);
 
2.增加索引
alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
 
3.测试SQL
--推荐写法,因为索引的使用顺序(where后面的顺序)和复合索引的顺序一致
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a3=3 and a4 =4 ; 
 
--虽然编写的顺序 和索引顺序不一致,但是 sql在真正执行前经过了SQL优化器的调整,结果与上条SQL一致
explain select a1,a2,a3,a4 from test03 where a4=1 and a3=2 and a2=3 and a1 =4 ; 
 
--using index:用到了a1 a2两个索引,该两个字段不需要回表查询
--using where:因为a4跨列使用,造成了该索引失效,需要回表查询
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3; 
 
--using filesort(文件内排序,“多了一次额外的查找/排序”) :不要跨列使用( where和order by 拼起来,不要跨列使用)
explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a3; 
 
--无using filesort
explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a2 , a3;
 
4.总结
	a.如果复合索引 (a,b,c,d) 和 使用的顺序全部一致(且不跨列使用),则复合索引全部使用
 	  如果部分一致(且不跨列使用),则使用部分索引
	b.where和order by 拼起来,不要跨列使用 

6.4 避免索引失效的一些原则

1.复合索引
	a.复合索引,不要跨列或无序使用(最佳左前缀)
	b.复合索引,尽量使用全索引匹配
 
2.不要在索引上进行任何操作(计算、函数、类型转换),否则索引失效
explain select * from book where authorid = 1 and typeid = 2 ;--用到了a、t    2个索引
explain select * from book where authorid = 1 and typeid*2 = 2 ;--用到了a     1个索引
explain select * from book where authorid*2 = 1 and typeid*2 = 2 ;--用到了0个索引
explain select * from book where authorid*2 = 1 and typeid = 2 ;--用到了0个索引,原因:对于复合索引,如果左边失效,右侧全部失效。例如复合索引(a,b,c),如果 b失效,则b c同时失效。
 
-- 单值索引
drop index idx_atb on book ;  --丢弃复合索引
alter table book add index idx_authroid (authorid) ; --增加单值索引authorid
alter table book add index idx_typeid (typeid) ; --增加单值索引typeid
explain select * from book where authorid*2 = 1 and typeid = 2 ;
 
3.复合索引不能使用不等于(!=  <>)或is null (is not null),否则自身以及右侧所有全部失效。
-- SQL优化,是一种概率层面的优化。至于是否实际使用了我们的优化,需要通过explain进行推测。
explain select * from book where authorid = 1 and typeid =2 ;
explain select * from book where authorid != 1 and typeid =2 ;
explain select * from book where authorid != 1 and typeid !=2 ;、
 
--体验概率情况(< > =):原因是服务层中有SQL优化器,可能会影响我们的优化。
drop index idx_typeid on book;
drop index idx_authroid on book;
alter table book add index idx_book_at (authorid,typeid);
explain select * from book where authorid = 1 and typeid =2 ;--复合索引at全部使用
explain select * from book where authorid > 1 and typeid =2 ; --复合索引中如果有>,则自身和右侧索引全部失效。
explain select * from book where authorid = 1 and typeid >2 ;--复合索引at全部使用
 
----明显的概率问题---
explain select * from book where authorid < 1 and typeid =2 ;--复合索引at只用到了1个索引
explain select * from book where authorid < 4 and typeid =2 ;--复合索引全部失效
 
-- 我们学习索引优化 ,是一个大部分情况适用的结论,但由于SQL优化器等原因,该结论不是100%正确。
-- 一般而言, 范围查询(> <  in)之后的索引失效
 
4.补救措施。100%概率,尽量使用索引覆盖(using index
select a,b,c from xx..where a=  .. and b =.. ; --复合索引(a,b,c)
 
5.like尽量以“常量”开头,不要以'%'开头,否则索引失效
explain select * from teacher  where tname like '%x%'; --tname索引失效
explain select * from teacher  where tname like 'x%'   --tname索引成功
explain select tname from teacher  where tname like '%x%'; --如果必须使用like '%x%'进行模糊查询,可以使用索引覆盖挽救一部分。
 
6.尽量不要使用类型转换(显示、隐式),否则索引失效
explain select * from teacher where tname = 'abc' ;
explain select * from teacher where tname = 123 ; --程序底层将 123 -> '123',即进行了类型转换,因此索引失效
 
7.尽量不要使用or,否则索引失效
explain select * from teacher where tname ='' or tcid >1 ; --将or左侧的tname失效

6.5 一些其他的优化方法

1.exist和in
 	select ..from table where exist (子查询) ;		 --如果子查询的数据集大,则使用exist,效率高
	select ..from table where 字段 in  (子查询) ;	--如果主查询的数据集大,则使用in,效率高。
	a.exist语法
	将主查询的结果,放到子查需结果中进行条件校验(看子查询是否有数据,如果有数据,则校验成功),
	如果符合校验,则保留数据;
	select tname from teacher where exists (select * from teacher) ; 
	select tname from teacher where exists (select * from teacher where tid =9999) ;
	
	b.in语法
	select ..from table where tid in  (1,3,5) ;
 
2.order byusing filesort 有两种算法:双路排序、单路排序(根据IO的次数)
	a.MySQL4.1之前,默认使用双路排序:扫描2次磁盘(1:从磁盘读取排序字段 ,对排序字段进行排序(在buffer中进行的排序) 2:扫描其他字段 )
	
	b.MySQL4.1之后 默认使用单路排序:只读取一次(全部字段),在buffer中进行排序。但种单路排序 会有一定的隐患 (不一定真的是“单路|1次IO”,有可能多次IO)。原因:如果数据量特别大,则无法 将所有字段的数据 一次性读取完毕,因此 会进行“分片读取、多次读取”。
	
	c.注意:单路排序比双路排序会占用更多的buffer。单路排序在使用时,如果数据大,可以考虑调大buffer的容量大小:set max_length_for_sort_data = 1024  单位byte
	
	d.如果max_length_for_sort_data值太低,则mysql会自动从 单路->双路   (太低:需要排序的列的总大小超过了max_length_for_sort_data定义的字节数)
	
	e.提高order by查询的策略:
	①选择使用单路、双路 ;调整buffer的容量大小;
	②避免select * ...  
	③复合索引 不要跨列使用 ,避免using filesort
	④保证全部的排序字段 排序的一致性(都是升序 或 降序)	

6.6 附:常见SQL

01.达梦(DM)
    达梦数据库使用 EXPLAIN 和 EXPLAIN PLAN FOR 查看执行计划。以下是一个示例:
    -- 示例 SQL 查询
    EXPLAIN SELECT * FROM employees WHERE department_id = 10;
    输出执行计划将会显示查询计划步骤,包括表扫描方式(如全表扫描或索引扫描)、估算的返回行数等信息。

02.金仓(Kingbase)
    金仓数据库的执行计划命令类似 PostgreSQL。
    使用 EXPLAIN 和 EXPLAIN ANALYZE 查看执行计划,ANALYZE 会实际执行查询以提供更精确的时间。
    -- 示例 SQL 查询
    EXPLAIN SELECT * FROM employees WHERE department_id = 10;
    -- 使用 EXPLAIN ANALYZE 实际执行查询
    EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 10;
    执行计划会显示各个操作步骤的成本、行数和扫描方式(如顺序扫描、索引扫描等)。

03.PostgreSQL
    PostgreSQL 也使用 EXPLAIN 和 EXPLAIN ANALYZE 查看执行计划。
    -- 仅查看执行计划
    EXPLAIN SELECT * FROM employees WHERE department_id = 10;
    -- 实际执行查询并显示详细执行时间
    EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 10;
    输出会显示估算的成本、扫描方法、连接方式等。使用 ANALYZE 会提供实际执行的时间和行数。

04.Oracle
    在 Oracle 数据库中,EXPLAIN PLAN 可以生成查询的执行计划,并存储在 PLAN_TABLE 表中,方便分析。
    -- 将执行计划插入 PLAN_TABLE 表中
    EXPLAIN PLAN FOR SELECT * FROM employees WHERE department_id = 10;
    -- 查看 PLAN_TABLE 中的执行计划
    SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
    输出结果包含查询的步骤、操作类型(如全表扫描、索引扫描等)、估算成本和访问路径。

05.SQL Server
    SQL Server 使用 SET SHOWPLAN_ALL 或 SET SHOWPLAN_XML 查看执行计划,不会实际执行查询。以下是一个示例:
    -- 查看执行计划
    SET SHOWPLAN_ALL ON;
    GO
    SELECT * FROM employees WHERE department_id = 10;
    GO
    SET SHOWPLAN_ALL OFF;
    执行计划会显示操作步骤、行数估算和成本。可以使用 SHOWPLAN_XML 查看 XML 格式的计划。

06.MySQL
    MySQL 中使用 EXPLAIN 或 EXPLAIN ANALYZE 查看执行计划。以下是一个简单的示例:
    -- 仅查看执行计划
    EXPLAIN SELECT * FROM employees WHERE department_id = 10;
    -- 使用 ANALYZE 显示更详细的执行信息
    EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 10;
    输出结果包括表的访问类型(如全表扫描、索引扫描)、查询的各个步骤和对应的成本估算。

7 SQL排查-慢查询日志

1.SQL排查 - 慢查询日志:
	MySQL提供的一种日志记录,用于记录MySQL种响应时间超过阀值的SQL语句(long_query_time,默认10秒)
	默认关闭,建议:开发调优时打开,而最终部署时关闭
 
2.检查是否开启了慢查询日志
	show variables like '%slow_query_log%' ;
 
3.临时在内存中开启
	set global slow_query_log = 1 ;
 
4.永久开启:
	vi /etc/my.cnf 
	[mysqld]
	slow_query_log=1
	slow_query_log_file=/var/lib/mysql/localhost-slow.log
 
5.慢查询阀值:
	show variables like '%long_query_time%' ;
 
6.临时设置阀值:
	set global long_query_time = 5 ; --设置完毕后,重新登陆后起效 (不需要重启服务)
 
7.永久设置阀值:
	vi /etc/my.cnf 
	[mysqld]
	long_query_time=3
 
8.测试
	select sleep(4);
	select sleep(5);
	select sleep(3);
	select sleep(3);
	--查询超过阀值的SQL:show global status like '%slow_queries%' ;
	
	a.慢查询的sql被记录在了日志中,因此可以通过日志查看具体的慢SQL。
		cat /var/lib/mysql/localhost-slow.log
 
	b.通过mysqldumpslow工具查看慢SQL,可以通过一些过滤条件 快速查找出需要定位的慢SQL
       	mysqldumpslow --help
    
    c.语法:mysqldumpslow 各种参数 慢查询日志的文件
		s:排序方式
		r:逆序
		l:锁定时间
		g:正则匹配模式	
        
--获取返回记录最多的3个SQL
mysqldumpslow -s r -t 3  /var/lib/mysql/localhost-slow.log
 
--获取访问次数最多的3个SQL
mysqldumpslow -s c -t 3 /var/lib/mysql/localhost-slow.log
 
--按照时间排序,前10条包含left join查询语句的SQL
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/localhost-slow.log

8 海量数据

8.1 模拟海量数据

通过存储函数(无return)插入海量数据 / 通过存储过程(有return)插入海量数据
 
1.创建表
create database testdata;
use testdata;
create table dept(
	dno int(5) primary key default 0,
	dname varchar(20) not null default '',
	loc varchar(30) default ''
)engine=innodb default charset=utf8;
 
create table emp(
	eid int(5) primary key,
	ename varchar(20) not null default '',
	job varchar(20) not null default '',
	deptno int(5) not null default 0
)engine=innodb default charset=utf8;
 
2.randstring(6) -> 用于模拟员工名称:存储函数
delimiter $ 
create function randstring(n int)   returns varchar(255) 
begin
	declare all_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	declare return_str varchar(255) default '' ;
	declare i int default 0 ; 
	while i<n		 
	do									
		set return_str = concat(return_str, substring(all_str, FLOOR(1+rand()*52),1));
    	set i=i+1 ;
	end while ;
	return return_str ;
end $ 
 
3.产生随机整数:存储函数
create function ran_num() returns int(5)
begin
	declare i int default 0;
	set i =floor( rand()*100 ) ;
	return i ;
end $
 
4.插入海量数据到emp表:存储过程
create procedure insert_emp( in eid_start int(10),in data_times int(10))
begin 
	declare i int default 0;
	set autocommit = 0 ;
	repeat	
		insert into emp values(eid_start + i, randstring(5) ,'other' ,ran_num()) ;
		set i=i+1 ;
		until i=data_times
	end repeat ;
	commit ;
end $
 
5.插入海量数据到dept表:存储过程
create procedure insert_dept(in dno_start int(10) ,in data_times int(10))
begin
	declare i int default 0;
	set autocommit = 0 ;
	repeat	
		insert into dept values(dno_start+i ,randstring(6),randstring(8)) ;
		set i=i+1 ;
		until i=data_times
	end repeat ;
	commit ;
end$
 
6.插入数据
delimiter; 
call insert_emp(1000,800000) ;
call insert_dept(10,30) ;
 
-- 更改结束符,SQL默认以;结束    本次修改为$结束
delimiter; 
delimiter$
 
-- 修改为一次性提交
set autocommit = 0 ;
 
--报错This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
 
原因:存储过程/存储函数在创建时 与之前的开启慢查询日志发生冲突
解决冲突:
	1.临时解决( 开启log_bin_trust_function_creators )
		show variables like '%log_bin_trust_function_creators%';
		set global log_bin_trust_function_creators = 1;
	2.永久解决:
		/etc/my.cnf 
		[mysqld]
		log_bin_trust_function_creators = 1

8.2 分析海量数据

1.profiles
show variables like '%profiling%';
set profiling = on ; 
show profiles;--默认关闭,会记录所有profiling打开之后的全部SQL查询语句所花费的时间。缺点:不够精确,只能看到总共消费的时间,不能看到各个硬件消费的时间(cpu、io)
 
2.精确分析:sql诊断
show profile all for query 上一步查询的的Query_Id
show profile cpu,block io for query 上一步查询的的Query_Id
 
3.全局查询日志:记录开启之后的全部SQL语句
(这次全局的记录操作,仅仅在调优、开发过程中打开即可,在最终的部署实施时,一定会关闭)
--开启全局日志
show variables like '%general_log%';
set global general_log = 1 ;--开启全局日志
 
--方式一:设置将全部的SQL记录在mysql.general_log表
set global log_output='table';
select * from  mysql.general_log ;
 
--方式二:将执行的所有SQL记录在文件中
set global log_output='file';
set global general_log = on ;
set global general_log_file='/tmp/general.log';

9 锁机制

按数据操作的类型来分:
    读锁(共享锁)
    写锁(排他锁或互斥锁)

按数据操作的粒度来分:
    表锁
    页锁
    行锁

-------------------------------------------------------------------------------------------------------------

01.行级锁 
    行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。
    行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。
    特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

02.表级锁
    表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。
    最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
    特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

03.页级锁
    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。
    所以取了折衷的页级,一次锁定相邻的一组记录。
    特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

-------------------------------------------------------------------------------------------------------------

01.悲观锁
    假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
    在查询完数据的时候就把事务锁起来,直到提交事务。
    实现方式:使用数据库中的锁机制
    ---------------------------------------------------------------------------------------------------------
    悲观锁适用于写比较多的情况下(多读场景),如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry

02.乐观锁
    假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
    在修改数据的时候把事务锁起来,通过version的方式来进行锁定。
    实现方式:乐一般会使用版本号机制或CAS算法实现。
    ---------------------------------------------------------------------------------------------------------
    乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

9.1 表锁

锁机制:解决因资源共享 而造成的并发问题
 
1.示例:买最后一件衣服X
A:  	X	买:衣服X加锁  -> 试衣服、下单、付款、打包  -> X解锁
B:		X   买:发现衣服X已被加锁,等待X解锁, X已售空
 
2.操作类型:
	a.读锁(共享锁): 对同一个数据(衣服),多个读操作可以同时进行,互不干扰。
	b.写锁(互斥锁): 如果当前写操作没有完毕(买衣服的一系列操作),则无法进行其他的读操作、写操作
 
3.操作范围:
	a.表锁 :一次性对一张表整体加锁。如MyISAM存储引擎使用表锁,开销小、加锁快;无死锁;但锁的范围大,容易发生锁冲突、并发度低
	b.行锁 :一次性对一条数据加锁。如InnoDB存储引擎使用行锁,开销大,加锁慢;容易出现死锁;锁的范围较小,不易发生锁冲突,并发度高(很小概率 发生高并发问题:脏读、幻读、不可重复度、丢失更新等问题)
	c.页锁
	
4.表锁: --自增操作 MYSQL/SQLSERVER支持;oracle需要借助于序列来实现自增
create table tablelock(
id int primary key auto_increment , 
name varchar(20)
)engine myisam;
 
insert into tablelock(name) values('a1');
insert into tablelock(name) values('a2');
insert into tablelock(name) values('a3');
insert into tablelock(name) values('a4');
insert into tablelock(name) values('a5');
 
--增加锁
locak table 表1  read/write  ,表2  read/write   ,...
 
--查看加锁的表
show open tables ;
 
--释放锁
unlock tables ;
 
--会话:session,每一个访问数据的dos命令行、数据库客户端工具都是一个会话
 
------------------------------------------加读锁------------------------------------------
会话1:
lock table  tablelock read ;
select * from tablelock; --读(查)tablelock,可以
delete from tablelock where id =1 ; --写(增删改)tablelock,不可以
select * from emp ; --读其他表,不可以
delete from emp where eid = 1; --写其他表,不可以
 
--结论1:如果某一个会话对A表加了read锁,则该会话可以对A表进行读操作、不能进行写操作;且该会话不能对其他表进行读、写操作。即,如果给A表加了读锁,则当前会话只能对A表进行读操作。
 
会话2(其他会话):
select * from tablelock;   --读(查)tablelock表,可以
delete from tablelock where id =1 ; --写,会“等待”会话0将锁释放
select * from emp ;  --读(查)其他表,可以
delete from emp where eno = 1; --写,可以
 
--结论2:会话1给A表加了锁;其他会话的操作:a.可以对其他表(A表以外的表)进行读、写操作;b.对A表:读-可以;写-需要等待释放锁。
 
------------------------------------------加写锁------------------------------------------
--结论1:如果某一个会话对A表加了read锁,则该会话可以对加了写锁的表  进行任何操作(增删改查);但是不能 操作(增删改查)其他表
 
--结论2:会话1给A表加了锁;其他会话的操作:a.可以对其他表(A表以外的表)进行读、写操作;b.对A表:读写(增删改查)-需要等待释放锁
 
-------------------------------------MySQL表级锁的锁模式------------------------------------
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(DML)前,会自动给涉及的表加写锁,所以对MyISAM表进行操作,会有以下情况:
a、对MyISAM表的读操作(加读锁),不会阻塞其他进程(会话)对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
b、对MyISAM表的写操作(加写锁),会阻塞其他进程(会话)对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。
 
-----------------------------------------分析表锁定----------------------------------------
查看哪些表加了锁:show open tables ; --1代表被加了锁
分析表锁定的严重程度: show status like 'table%' ;
				   Table_locks_immediate:可能获取到的锁数
			       Table_locks_waited:需要等待的表锁数(如果该值越大,说明存在越大的锁竞争)
一般建议:Table_locks_immediate/Table_locks_waited > 5000, 建议采用InnoDB引擎,否则MyISAM引擎

9.2 行锁

1.行锁
create table linelock(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb ;
 
insert into linelock(name) values('1') ;
insert into linelock(name) values('2') ;
insert into linelock(name) values('3') ;
insert into linelock(name) values('4') ;
insert into linelock(name) values('5') ;
 
--mysql默认自动commit; 
--oracle默认不会自动commit;
--为了研究行锁,暂时将自动commit关闭; set autocommit=0; 
 
 
-----------------------------------行锁:操作同一行数据--------------------------------------
--会话0:写操作
insert into linelock values(6, 'a6') ;
--会话1:写操作,同样的数据
update linelock set name='ax' where id = 6; 
--结论:
1.如果会话x对某条数据a进行DML操作(研究时:关闭了自动commit的情况下),则其他会话必须等待会话x结束事务(commit/rollback)后  才能对数据a进行操作。
2.表锁是通过unlock tables,也可以通过事务解锁; 行锁是通过事务解锁。
	
	
-----------------------------------行锁:操作不同行数据--------------------------------------
--会话0:写操作
insert into linelock values(8,'a8') ;
--会话1:写操作,不同的数据
update linelock set name='ax' where id = 5;
--结论:
行锁,一次锁一行数据。因此如果操作的是不同数据,则不干扰。
 
 
-----------------------------------行锁的注意事项-------------------------------------------
1.如果没有索引,则行锁会转为表锁
show index from linelock ;
alter table linelock add index idx_linelock_name(name);
 
--会话1:写操作
update linelock set name = 'ai' where name = '3' ;	
--会话2:写操作,不同的数据
update linelock set name = 'aiX' where name = '4' ;
		
--会话1:写操作
update linelock set name = 'ai' where name = 3 ;		
--会话2:写操作,不同的数据
update linelock set name = 'aiX' where name = 4 ;
		
--可以发现,数据被阻塞了(加锁)
-- 原因:如果索引发生了类型转换,则索引失效。 因此此次操作,会从行锁转为表锁。
 
 
2.行锁的情况二:间隙锁(值在范围内,但却不存在)
--此时linelock表中 没有id=7的数据
--即在此where范围中,没有id=7的数据,则id=7的数据成为间隙。
update linelock set name ='x' where id >1 and id< ;
--间隙:Mysql会自动给间隙加锁->间隙锁。即本题会自动给id=7的数据加间隙锁(行锁)。
--行锁:如果有where,则实际加索的范围 就是where后面的范围(不是实际的值)
 
 
3.行锁的情况三:如何仅仅是查询数据,能否加锁? 可以,for update 
--关闭自动提交
set autocommit =0 ;
start transaction ;
begin ;
 
--通过for update对query语句进行加锁
select * from linelock where id =2 for update ;
 
----------------------------------------行锁分析-------------------------------------------
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:等待次数。从系统启到现在一共等待的次数
 
--InnoDB默认采用行锁;
缺点: 比表锁性能损耗大。
优点:并发能力强,效率高。
因此建议,高并发用InnoDB,否则用MyISAM。