01mysql

一. MySQL基础篇

(一) Linux(CentOS 7)安装MySQL 5.6

卸载

1 查看MySQL软件
rpm -qa|grep mysql 
yum repolist all | grep mysql 
2 卸载
yum remove -y mysql mysql-libs mysql-common #卸载mysql 
rm -rf /var/lib/mysql #删除mysql下的数据文件 
rm /etc/my.cnf #删除mysql配置文件 
yum remove -y mysql-community-release-el6-5.noarch #删除组件

安装

1 下载MySQL
#下载rpm文件
wget http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm
#执行rpm源文件
rpm -ivh mysql-community-release-el6-5.noarch.rpm
2 执行、启动MySQL
#执行安装文件
yum install mysql-community-server
#启动MySQL
systemctl start mysqld
3 设置密码(将 root 账号设置密码为 root )
#没有原密码
/usr/bin/mysqladmin -u root password 'root'
#有原来的密码,则加(一般)
/usr/bin/mysqladmin -u root -p '123' password 'root'
4 登录MySQL
mysql -uroot -proot
#-u:指定数据库用户名
#-p:指定数据库密码,记住-u和登录密码之间没有空格
5 修改配置
vim /etc/my.cnf

注:配置文件

[mysqld]
# MySQL设置大小写不敏感:默认:区分表名的大小写,不区分列名的大小写
# 0:大小写敏感 1:大小写不敏感
lower_case_table_names=1
# 默认字符集
character-set-server=utf8
6 MySQL远程连接授权(grant 权限 on 数据库对象 to 用户)
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; 
#赋予root用户对所有数据库对象的全部操作权限:
FLUSH PRIVILEGES;   
#刷新权限

命令说明:

	ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。
	*.* :表示所有库中的所有表
	'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。
	IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。
7 关闭linux的防火墙
systemctl stop firewalld          		(默认)
systemctl disable firewalld.service   	(设置开启不启动)
8 客户端远程访问
利用navicat可以远程访问MySQL

注:如果连接不上,可以按以下步骤排错
1、MySQL是否正常启动
[root@localhost ~]# ps -ef | grep mysql
root 1114 1 0 10:21 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe -
-datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pidfile=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
mysql 1698 1114 0 10:21 ? 00:00:03 /usr/sbin/mysqld

2、查看防火墙是否关闭
[root@localhost ~]# systemctl status firewalld
firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled)
Active: inactive (dead)

3、查看root权限为所有ip都可以访问
mysql> show grants for root;
+-------------------------------------------------------------------------------
-------------------------------------------------+
| Grants for root@%
| GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY PASSWORD
'*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B' WITH GRANT OPTION |
+-----------------------------------------------------------------

4、服务器与客户端是否可以ping通
ping 192.168.239.129
正在 Ping 192.168.239.129 具有 32 字节的数据:
来自 192.168.239.129 的回复: 字节=32 时间<1ms TTL=64

5、客户端是否可以telnet到服务器端
telnet 192.168.239.129 3306

6、Navicat是否正确安装

(二) SQL语言分类

- 数据定义语言:简称【DDL】(Data Definition Language),用来定义数据库对象:数据库,表,列 等。关键字:create,alter,drop等 

- 数据操作语言:简称【DML】(Data Manipulation Language),用来对数据库中表的记录进行更 新。关键字:insert,delete,update等 

- 数据控制语言:简称【DCL】(Data Control Language),用来定义数据库的访问权限和安全级别, 及创建用户;关键字:grant等 

- 数据查询语言:简称【DQL】(Data Query Language),用来查询数据库中表的记录。关键字: select,from,where等

DDL语句

1.数据库操作:database
#创建数据库
create database 数据库名; 
create database 数据库名 character set 字符集

#查看所有的数据库
show databases;
#查看指定的数据库
show create database 数据库名;

#删除数据库
drop database 数据库名称;

#选择(切换)要使用的数据库
use 数据库名;

#查看当前使用的数据库
select database();
2.表操作:table
#创建表
create table 表名( 
    字段名 类型(长度) 约束, 
    字段名 类型(长度) 约束 
);

#约束条件(主键约束 = 唯一约束 + 非空约束)
- 主键约束:primary key 
- 唯一约束:unique 
- 非空约束:not null

#查看所有的表
show tables; 
#查看表结构
desc 表名;

#删除表
drop table 表名;

#修改表
alter table 表名 add 列名 类型(长度) 约束; 				   --修改表添加列. 
alter table 表名 modify 列名 类型(长度) 约束; 			   --修改表修改列的类型长度及约束. 
alter table 表名 change 旧列名 新列名 类型(长度) 约束; 		 --修改表修改列名. 
alter table 表名 drop 列名; 								--修改表删除列. 
rename table 表名 to 新表名; 								--修改表名 
alter table 表名 character set 字符集; 						--修改表的字符集 

DML语句

1.插入记录:insert
insert into 表 (列名1,列名2,列名3..) values (值1,值2,值3..); 		-- 向表中插入某些列 
insert into 表 values (值1,值2,值3..); 							--向表中插入所有列 

insert into 表 (列名1,列名2,列名3..) values select (列名1,列名2,列名3..) from 表 
insert into 表 values select * from 表

#例子
	INSERT INTO sort(sid,sname) VALUES('s001', '电器'); 
	INSERT INTO sort VALUES('s004','书籍');

注:

1. 列名数与values后面的值的个数相等
2. 列的顺序与插入的值得顺序一致
3. 列名的类型与插入的值要一致. 
4. 插入值得时候不能超过最大长度. 
5. 值如果是字符串或者日期需要加引号’’ (一般是单引号)
2. 更新记录:update
update 表名 set 字段名=值,字段名=值; 
update 表名 set 字段名=值,字段名=值 where 条件;        			#常用

注:

1. 列名的类型与修改的值要一致. 
2. 修改值得时候不能超过最大长度. 
3. 值如果是字符串或者日期需要加’’.
3.删除记录:delete
delete from 表名 [where 条件];

注:

删除表中所有记录使用【delete from 表名】,还是用【truncate table 表名】? 

删除方式: - delete :一条一条删除,不清空auto_increment记录数。 
		- truncate :直接将表删除,重新建表,auto_increment将置为零,从新开始。

DQL语句

提前创建表

#商品表 
CREATE TABLE product ( 
	pid INT PRIMARY KEY AUTO_INCREMENT, # 自增加 AUTO_INCREMENT 
	pname VARCHAR(20),#商品名称 
	price DOUBLE, #商品价格 
	pdate DATE, # 日期 
	cid int #分类ID 
);

#目录表 
create table category( 
    id INT PRIMARY KEY , 
    cname varchar(100) 
);

INSERT INTO product VALUES(NULL,'泰国大榴莲', 98, NULL, 1); 
INSERT INTO product VALUES(NULL,'泰国大枣', 38, NULL, 1); 
INSERT INTO product VALUES(NULL,'新疆切糕', 68, NULL, 2); 
INSERT INTO product VALUES(NULL,'十三香', 10, NULL, 2); 
INSERT INTO product VALUES(NULL,'泰国大枣', 20, NULL, 2);
insert into product values(null,'泰国大枣',98,null,20); #没有对应 

insert into product values(null,'iPhone手机',800,null,30);#没有对应 
INSERT INTO category VALUES(1,'国外食品'); 
INSERT INTO category VALUES(2,'国内食品'); 
INSERT INTO category VALUES(3,'国内服装'); #没有对应
简单查询
select * from product;		--查询所有的商品.
select pname,price from product;  --查询商品名和商品价格.
select * from product as p;		--别名查询,使用的as关键字,as可以省略的.表别名:
select pname as pn from product; 	--列别名
select distinct price from product; 	--去重
select pname,price+10 from product;			--查询结果是表达式(运算查询):将所有商品的价格+10元进行显示.
条件查询(> ,<,=,>=,<=,<> like )
select * from product where pname = '十三香';	--查询商品名称为十三香的商品所有信息:
select * from product where price > 60;			--查询商品价格>60元的所有的商品信息: 
select * from product where pname like '%新%'; --使用占位符 _ 和 % _代表一个字符 %代表任意个字符.
select * from product where pid in (2,5,8);		--in在某个范围中获得值(exists). 
排序(asc-升序,desc-降序)
select * from product order by price;	--查询所有的商品,按价格进行排序
select * from product where pname like '%新%' order by price desc;
									--查询名称有新的商品的信息并且按价格降序排序.
聚合函数,分组函数

注:

sum():求某一列的和 
avg():求某一列的平均值 
max():求某一列的最大值 
min():求某一列的最小值 
count():求某一列的元素个数
select sum(price) from product; 	--获得所有商品的价格的总和:
select avg(price) from product; 	--获得所有商品的价格的平均值:
select count(*) from product;		--获得所有商品的个数:

select cid,count(*) from product group by cid; 	--根据cno字段分组,分组后统计商品的个数
select cid,avg(price) from product group by cid having avg(price)>60;
                       --根据cno分组,分组统计每组商品的平均价格,并且平均价格> 60;
#注意事项          
1. select语句中的列(非聚合函数列),必须出现在group by子句中 
2. group by子句中的列,不一定要出现在select语句中 
3. 聚合函数只能出现select语句中或者having语句中,一定不能出现在where语句中。					
分页查询
#格式
SELECT * FROM table LIMIT [offset,] rows
						--offset :偏移量	rows :每页多少行记录。
#注意
	逻辑分页:将数据库中的数据查询到内存之后再进行分页。 
	物理分页:通过LIMIT关键字,直接在数据库中进行分页,最终返回的数据,只是分页后的数据。(limit)
子查询
#定义
	子查询允许把一个查询嵌套在另一个查询当中。 
	子查询,又叫内部查询,相对于内部查询,包含内部查询的就称为外部查询。 
	子查询可以包含普通select可以包括的任何子句,比如:distinct、 group by、order by、limit、 join和union等; 
	但是对应的外部查询必须是以下语句之一:select、insert、update、delete。

#位置
	select中、from 后、where 中. 
	group by 和order by 中无实用意义。
其他查询语句(union)
union 集合的并集(不包含重复记录)
union all 集合的并集(包含重复记录)

(三) SQL解析顺序

1. FROM(将最近的两张表,进行笛卡尔积)---VT1 
2. ON(将VT1按照它的条件进行过滤)---VT2 
3. LEFT JOIN(保留左表的记录)---VT3 
4. WHERE(过滤VT3中的记录)--VT4…VTn 
5. GROUP BY(对VT4的记录进行分组)---VT5 
6. HAVING(对VT5中的记录进行过滤)---VT6 
7. SELECT(对VT6中的记录,选取指定的列)--VT7 
8. ORDER BY(对VT7的记录进行排序)--VT8 
9. LIMIT(对排序之后的值进行分页)--MySQL特有的语法

(四) 表与表之间的关系

  • 一对一关系

    常见实例:一夫一妻

  • 一对多关系

    常见实例:会员和订单

  • 多对多关系(需要中间表实现)

    常见实例:商品和订单

(五) 多表关联查询

JOIN 按照功能大致分为如下三类:

#CROSS JOIN (交叉连接)							
	**交叉连接也叫笛卡尔积连接。****笛卡尔积**是指在数学中,两个集合 X 和 Y 的笛卡尓积( Cartesian 
    product ),又称直积,表示为 X*Y ,第一个对象是 X 的成员而第二个对象是 Y 的所有可能有序对的其
    中一个成员。

#INNER JOIN (内连接或等值连接)。
	内连接也叫**等值连接**,内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。
	
#OUTER JOIN (外连接)  
	左外连接left join、右外连接right join、全外连接join

(补) MySQL补充知识点

基本数据类型

数值类型
类型大小范围(有符号)范围(无符号)用途
TINYINT1 Bytes(-128,127)(0,255)小整数值
SMALLINT2 Bytes(-32 768,32 767)(0,65 535)大整数值
MEDIUMINT3 Bytes(-8 388 608,8 388 607)(0,16 777 215)大整数值
INT或INTEGER4 Bytes(-2 147 483 648,2 147 483 647)(0,4 294 967 295)大整数值
BIGINT8 Bytes(-9,223,372,036,854,775,808,9 223 372 036 854 775 807)(0,18 446 744 073 709 551 615)极大整数值
FLOAT4 Bytes(-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38)0,(1.175 494 351 E-38,3.402 823 466 E+38)单精度 浮点数值
DOUBLE8 Bytes(-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)双精度 浮点数值
DECIMAL对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2依赖于M和D的值依赖于M和D的值小数值

日期和时间类型
类型大小 ( bytes)范围格式用途
DATE31000-01-01/9999-12-31YYYY-MM-DD日期值
TIME3‘-838:59:59’/‘838:59:59’HH:MM:SS时间值或持续时间
YEAR11901/2155YYYY年份值
DATETIME81000-01-01 00:00:00/9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS混合日期和时间值
TIMESTAMP41970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07YYYYMMDD HHMMSS混合日期和时间值,时间戳

字符串类型
类型大小用途
CHAR0-255 bytes定长字符串
VARCHAR0-65535 bytes变长字符串
TINYBLOB0-255 bytes不超过 255 个字符的二进制字符串
TINYTEXT0-255 bytes短文本字符串
BLOB0-65 535 bytes二进制形式的长文本数据
TEXT0-65 535 bytes长文本数据
MEDIUMBLOB0-16 777 215 bytes二进制形式的中等长度文本数据
MEDIUMTEXT0-16 777 215 bytes中等长度文本数据
LONGBLOB0-4 294 967 295 bytes二进制形式的极大文本数据
LONGTEXT0-4 294 967 295 bytes极大文本数据
数字型:int 
浮点型:double 
字符型:varchar(可变长字符串) 
日期类型:date(只有年月日,没有时分秒) 
		datetime(年月日,时分秒) 
		timestamp
boolean类型:不支持,一般使用tinyint替代(值为0和1)

比较运算符

符号描述备注
=等于
<>, !=不等于
>大于
<小于
<=小于等于
>=大于等于
BETWEEN在两值之间>=min&&<=max
NOT BETWEEN不在两值之间
IN在集合中
NOT IN不在集合中
<=>严格比较两个NULL值是否相等两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0
LIKE模糊匹配
REGEXP 或 RLIKE正则式匹配
IS NULL为空
IS NOT NULL不为空

常见函数

MySQL 字符串函数
函数描述实例
CHAR_LENGTH(s)返回字符串 s 的字符数返回字符串 RUNOOB 的字符数SELECT CHAR_LENGTH("RUNOOB") AS LengthOfString;
CONCAT(s1,s2…sn)字符串 s1,s2 等多个字符串合并为一个字符串合并多个字符串SELECT CONCAT("SQL ", "Runoob ", "Gooogle ", "Facebook") AS ConcatenatedString;
FORMAT(x,n)函数可以将数字 x 进行格式化 “#,###.##”, 将 x 保留到小数点后 n 位,最后一位四舍五入。格式化数字 “#,###.##” 形式:SELECT FORMAT(250500.5634, 2); -- 输出 250,500.56
LOWER(s)将字符串 s 的所有字母变成小写字母字符串 RUNOOB 转换为小写:SELECT LOWER('RUNOOB') -- runoob
REVERSE(s)将字符串s的顺序反过来将字符串 abc 的顺序反过来:SELECT REVERSE('abc') -- cba
STRCMP(s1,s2)比较字符串 s1 和 s2,如果 s1 与 s2 相等返回 0 ,如果 s1>s2 返回 1,如果 s1<s2 返回 -1比较字符串:SELECT STRCMP("runoob", "runoob"); -- 0
TRIM(s)去掉字符串 s 开始和结尾处的空格去掉字符串 RUNOOB 的首尾空格:SELECT TRIM(' RUNOOB ') AS TrimmedString;
UPPER(s)将字符串转换为大写将字符串 runoob 转换为大写:SELECT UPPER("runoob"); -- RUNOOB

MySQL 数字函数
函数名描述实例
ABS(x)返回 x 的绝对值返回 -1 的绝对值:SELECT ABS(-1) -- 返回1
FLOOR(x)返回小于或等于 x 的最大整数小于或等于 1.5 的整数:SELECT FLOOR(1.5) -- 返回1
GREATEST(expr1, expr2, expr3, …)返回列表中的最大值返回以下数字列表中的最大值:SELECT GREATEST(3, 12, 34, 8, 25); -- 34返回以下字符串列表中的最大值:SELECT GREATEST("Google", "Runoob", "Apple"); -- Runoob
LEAST(expr1, expr2, expr3, …)返回列表中的最小值返回以下数字列表中的最小值:SELECT LEAST(3, 12, 34, 8, 25); -- 3返回以下字符串列表中的最小值:SELECT LEAST("Google", "Runoob", "Apple"); -- Apple
MAX(expression)返回字段 expression 中的最大值返回数据表 Products 中字段 Price 的最大值:SELECT MAX(Price) AS LargestPrice FROM Products;
MIN(expression)返回字段 expression 中的最小值返回数据表 Products 中字段 Price 的最小值:SELECT MIN(Price) AS MinPrice FROM Products;
MOD(x,y)返回 x 除以 y 以后的余数5 除于 2 的余数:SELECT MOD(5,2) -- 1
POW(x,y)返回 x 的 y 次方2 的 3 次方:SELECT POW(2,3) -- 8
POWER(x,y)返回 x 的 y 次方2 的 3 次方:SELECT POWER(2,3) -- 8
RAND()返回 0 到 1 的随机数SELECT RAND() --0.93099315644334
ROUND(x)返回离 x 最近的整数SELECT ROUND(1.23456) --1
SIGN(x)返回 x 的符号,x 是负数、0、正数分别返回 -1、0 和 1SELECT SIGN(-10) -- (-1)
SIN(x)求正弦值(参数是弧度)SELECT SIN(RADIANS(30)) -- 0.5
SQRT(x)返回x的平方根25 的平方根:SELECT SQRT(25) -- 5
SUM(expression)返回指定字段的总和计算 OrderDetails 表中字段 Quantity 的总和:SELECT SUM(Quantity) AS TotalItemsOrdered FROM OrderDetails;
TRUNCATE(x,y)返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入)SELECT TRUNCATE(1.23456,3) -- 1.234

MySQL 日期函数
函数名实例描述
ADDDATE(d,n)SELECT ADDDATE("2017-06-15", INTERVAL 10 DAY); ->2017-06-25计算起始日期 d 加上 n 天的日期
ADDTIME(t,n)加 5 秒:SELECT ADDTIME('2011-11-11 11:11:11', 5); ->2011-11-11 11:11:16 (秒)添加 2 小时, 10 分钟, 5 秒:SELECT ADDTIME("2020-06-15 09:34:21", "2:10:5"); -> 2020-06-15 11:44:26n 是一个时间表达式,时间 t 加上时间表达式 n
CURRENT_DATE()SELECT CURRENT_DATE(); -> 2018-09-19返回当前日期
CURRENT_TIMESELECT CURRENT_TIME(); -> 19:59:02返回当前时间
CURRENT_TIMESTAMP()SELECT CURRENT_TIMESTAMP() -> 2018-09-19 20:57:43返回当前日期和时间
DATE()SELECT DATE("2017-06-15"); -> 2017-06-15从日期或日期时间表达式中提取日期值
DATEDIFF(d1,d2)SELECT DATEDIFF('2001-01-01','2001-02-02') -> -32计算日期 d1->d2 之间相隔的天数
DATE_FORMAT(d,f)SELECT DATE_FORMAT('2011-11-11 11:11:11','%Y-%m-%d %r') -> 2011-11-11 11:11:11 AM按表达式 f的要求显示日期 d
DATE_SUB(date,INTERVAL expr type)Orders 表中 OrderDate 字段减去 2 天:SELECT OrderId,DATE_SUB(OrderDate,INTERVAL 2 DAY) AS OrderPayDate FROM Orders函数从日期减去指定的时间间隔。
DAY(d)SELECT DAY("2017-06-15"); -> 15返回日期值 d 的日期部分
DAYNAME(d)SELECT DAYNAME('2011-11-11 11:11:11') ->Friday返回日期 d 是星期几,如 Monday,Tuesday
DAYOFMONTH(d)SELECT DAYOFMONTH('2011-11-11 11:11:11') ->11计算日期 d 是本月的第几天
DAYOFWEEK(d)SELECT DAYOFWEEK('2011-11-11 11:11:11') ->6日期 d 今天是星期几,1 星期日,2 星期一,以此类推
DAYOFYEAR(d)SELECT DAYOFYEAR('2011-11-11 11:11:11') ->315计算日期 d 是本年的第几天
EXTRACT(type FROM d)SELECT EXTRACT(MINUTE FROM '2011-11-11 11:11:11') -> 11从日期 d 中获取指定的值,type 指定返回的值。 type可取值为: MICROSECONDSECONDMINUTEHOURDAYWEEKMONTHQUARTERYEARSECOND_MICROSECONDMINUTE_MICROSECONDMINUTE_SECONDHOUR_MICROSECONDHOUR_SECONDHOUR_MINUTEDAY_MICROSECONDDAY_SECONDDAY_MINUTEDAY_HOURYEAR_MONTH
FROM_DAYS(n)SELECT FROM_DAYS(1111) -> 0003-01-16计算从 0000 年 1 月 1 日开始 n 天后的日期
HOUR(t)SELECT HOUR('1:2:3') -> 1返回 t 中的小时值
LAST_DAY(d)SELECT LAST_DAY("2017-06-20"); -> 2017-06-30返回给给定日期的那一月份的最后一天
MINUTE(t)SELECT MINUTE('1:2:3') -> 2返回 t 中的分钟值
MONTHNAME(d)SELECT MONTHNAME('2011-11-11 11:11:11') -> November返回日期当中的月份名称,如 November
MONTH(d)SELECT MONTH('2011-11-11 11:11:11') ->11返回日期d中的月份值,1 到 12
NOW()SELECT NOW() -> 2018-09-19 20:57:43返回当前日期和时间
PERIOD_ADD(period, number)SELECT PERIOD_ADD(201703, 5); -> 201708为 年-月 组合日期添加一个时段
PERIOD_DIFF(period1, period2)SELECT PERIOD_DIFF(201710, 201703); -> 7返回两个时段之间的月份差值
QUARTER(d)SELECT QUARTER('2011-11-11 11:11:11') -> 4返回日期d是第几季节,返回 1 到 4
SECOND(t)SELECT SECOND('1:2:3') -> 3返回 t 中的秒钟值
SEC_TO_TIME(s)SELECT SEC_TO_TIME(4320) -> 01:12:00将以秒为单位的时间 s 转换为时分秒的格式
STR_TO_DATE(string, format_mask)SELECT STR_TO_DATE("August 10 2017", "%M %d %Y"); -> 2017-08-10将字符串转变为日期

MySQL 高级函数
函数名描述实例
BIN(x)返回 x 的二进制编码15 的 2 进制编码:SELECT BIN(15); -- 1111
BINARY(s)将字符串 s 转换为二进制字符串SELECT BINARY "RUNOOB"; -> RUNOOB
CAST(x AS type)转换数据类型字符串日期转换为日期:SELECT CAST("2017-08-29" AS DATE); -> 2017-08-29
COALESCE(expr1, expr2, …, expr_n)返回参数中的第一个非空表达式(从左向右)SELECT COALESCE(NULL, NULL, NULL, 'runoob.com', NULL, 'google.com'); -> runoob.com
CONV(x,f1,f2)返回 f1 进制数变成 f2 进制数SELECT CONV(15, 10, 2); -> 1111
CONVERT(s USING cs)函数将字符串 s 的字符集变成 csSELECT CHARSET('ABC') ->utf-8 SELECT CHARSET(CONVERT('ABC' USING gbk)) ->gbk
IF(expr,v1,v2)如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。SELECT IF(1 > 0,'正确','错误') ->正确
IFNULL(v1,v2)如果 v1 的值不为 NULL,则返回 v1,否则返回 v2。SELECT IFNULL(null,'Hello Word') ->Hello Word
ISNULL(expression)判断表达式是否为 NULLSELECT ISNULL(NULL); ->1

二. MySQL架构篇

(一) MySQL文件结构

日志文件(顺序IO)

查看当前数据库中的日志使用信息:

mysql> show variables like 'log_%';
1)错误日志(errorlog)
2)二进制日志(bin log)
3)通用查询日志(general query log)
4)慢查询日志(slow query log)

数据文件(随机IO)

查看MySQL数据文件:

SHOW VARIABLES LIKE '%datadir%';
1)InnoDB数据文件
.frm文件:主要存放与表相关的数据信息,主要包括表结构的定义信息 (表结构)
.ibd:使用独享表空间存储表数据和索引信息,一张表对应一个ibd文件。 (表数据和索引)
.ibdata文件:使用共享表空间存储表数据和索引信息,所有表共同使用一个或者多个ibdata文件。(共享)
2)MyIsam数据文件
.frm文件:主要存放与表相关的数据信息,主要包括表结构的定义信息 (表结构)
.myd文件:主要用来存储表数据信息。 (表数据)
.myi文件:主要用来存储表数据文件中任何索引的数据树。(索引)

(二) 逻辑架构图

mysql server层InnoDB层
连接器 --> 分析器(查询缓存) --> 优化器 --> 执行器磁盘结构、内存结构、数据落盘

(三) MySqlServer 层对象

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能, 以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现, 比如存储过程、触发器、视图等。

执行流程结果详情
连接器连接管理、权限验证先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获 取权限、维持和管理连接。
分析器(查询缓存)词法分析、语法分析(命中查询、返回结果)(大多数情况下我会建议你不要使用查询缓存,因为查询缓存往往弊大于利)
优化器生成计划、选择索引1)当有多个索引可用的时候,决定使用哪个索引; 2)在一个语句有多表关联(join)的时候,决定各个表的连接顺序,以哪个表为基准表。
执行器调用接口、返回结果1)要先判断一下你对这个表customer有没有执行查询的权限; 2)执行器会根据表的引擎定义,去使用这 个引擎提供的查询接口,提取数据。

(四) InnoDB 存储引擎层

​ 存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等 多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

存储引擎说明
MyISAM高速引擎,拥有较高的插入,查询速度,但不支持事务
InnoDB5.5版本后MySQL的默认数据库,支持事务和行级锁定,比 MyISAM处理速度稍慢
ISAMMyISAM的前身,MySQL5.0以后不再默认安装
MRG_MyISAM(MERGE)将多个表联合成一个表使用,在超大规模数据存储时很有用
Memory内存存储引擎,拥有极高的插入,更新和查询效率。但是会占用和 数据量成正比的内存空间。只在内存上保存数据,意味着数据可能 会丢失
Falcon一种新的存储引擎,支持事物处理,传言可能是InnoDB的替代者
Archive数据压缩后进行存储,非常适合存储大量的独立的,作为历史记 录的数据,但是只能进行插入和查询操作
CSVCSV 存储引擎是基于 CSV 格式文件存储数据(应用于跨平台的数据交换)

InnoDB的磁盘结构

1)系统表空间
  • 系统表空间包含InnoDB数据字典(元数据以及相关对象)、double write buffer、change buffer、undo logs的存储区域
2)用户表空间
  • 用户表空间只存储该表的数据、索引信息,其余信息还是存放在默认的系统表空间中。
3)rodo log 文件组
  • 当InnoDB的数据存储文件发生错误时,重做日志文件就能派上用场。InnoDB存储引擎可以使用重 做日志文件将数据恢复为正确状态,以此来保证数据的正确性和完整性
  • 为了得到更高的可靠性,用户可以设置多个镜像日志组,将不同的文件组放在不同的磁盘上,以此 来提高重做日志的高可用性。
4)磁盘文件逻辑结构
  • 文件 -> 段 -> 区 -> 页 -> 行

​ InnoDB对数据的存取是以页为单位的,一个数据页默认是16k

InnoDB的内存结构

1)buffer pool
1.数据页
2.索引页

​ InnoDB存储引擎工作时,需要以Page页为最小单位去将磁盘中的数据加载到内存中,与数据库相关的所有内容都存储在Page结构里。
​ Page分为几种类型,数据页和索引页就是其中最为重要的两种类型。

3.ChangeBuffer修改缓冲区(InsertBuffer插入缓冲区)

​ 为了提高辅助索引(非聚集索引,除主键之外的索引)更新性能,暂时先把辅助索引的更新内容写到 ChangeBuffer中。后台有线程定时更新。

4.自适应hash索引

​ 为了提高查询性能,由InnoDB自己维护了一个hash索引。用户无法干预。
​ 参考:https://juejin.im/post/6847902217270198286

5.锁信息和数据字典

​ InnoDB有自己的表缓存,可以称为表定义缓存或者数据字典(Data Dictionary)。当InnoDB打开一张
表,就增加一个对应的对象到数据字典。
​ 数据字典是对数据库中的数据、库对象、表对象等的元信息的集合。在MySQL中,数据字典信息内容就
包括表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、存储过程、触发器等内
**容。**MySQL INFORMATION_SCHEMA库提供了对数据局元数据、统计信息、以及有关MySQL server的
访问信息(例如:数据库名或表名,字段的数据类型和访问权限等)。该库中保存的信息也可以称为
MySQL的数据字典。

2)redo log buffer重做日志缓冲区
1.redolog是一个顺序写的日志文件。wal(write ahead log)模式。顺序写比随机写效率要高。使用redo 		log暂存提交成功的数据。如果一旦系统崩溃,可以使用redo log恢复数据。
2.redo log buffer就是一个缓冲区,定时将缓冲区的数据保存到磁盘。
	默认1s保存一次。默认在执行commit操作之前刷新redo log buffer。
	参数:innodb_flush_log_at_trx_commit控制redo log的落盘时机

	当属性值为0时,事务提交时,不会对重做日志进行写入操作,而是等待主线程按时写入每秒写入一次;
	当属性值为1时,事务提交时,会将重做日志写入文件系统缓存,并且调用文件系统的fsync,将文件系统缓冲中的数据真正写入磁盘存储,确保不会出现数据丢失;(一般)
	当属性值为2时,事务提交时,也会将日志文件写入文件系统缓存,但是不会调用fsync,而是让文件系统自己去判断何时将缓存写入磁盘。
3)双写缓冲区
  • 数据落盘需要执行双写操作,需要使用到双写缓冲区。稍后详解。
4)数据落盘

  • 脏页
    当更新数据commit之后,数据不是马上更新到磁盘。把数据更新到缓冲池中对应的数据页和索引页中。此时就会造成内存中的数据页和索引页与磁盘上的数据页不一致,就形成了脏页。
  • CheckPoint 执行脏页落盘操作的。
    sharp checkpoint:在关闭数据库的时候,将buffer pool中的脏页全部刷新到磁盘中
    fuzzy checkpoint:模糊落盘
    1、Master Thread Checkpoint;
    会以每秒或者每10秒一次的频率,将部分脏页从内存中刷新到磁盘,参数可调。
    2、FLUSH_LRU_LIST Checkpoint;
    使用LRU算法需要把一些数据页移除,但是数据页是脏页,需要执行checkpoint。
    3、Async/Sync Flush Checkpoint;
    redo log文件快写满时。
    Async:当需要落盘的日志超过日志文件的75%并且小于90%时。
    sync:当需要落盘的日志超过日志文件超过90%时。
    4、Dirty Page too much Checkpoint
    bufferPool缓冲池脏页太多
    Dirty Page 由[innodb_max_dirty_pages_pct]配置,innodb_max_dirty_pages_pct的默认值在innodb 1.0之前是90%,之后是75%
  • 双写落盘double write
    ​ 当脏页落盘过程中,是使用双写操作执行落盘的。
    ​ 1.把要落盘的数据写先写入双写缓冲区2m大小的双写缓冲区。
    ​ 2.把双写缓冲区的数据写入系统表空间中的双写区。
    ​ 3.把双写缓冲区的数据写入用户表空间中。

​ redolog中记录的是数据页中修改了哪些内容,并不是完整的数据页。

场景1:系统表空间中的数据页写坏了,可以通过用户表空间的数据页+redolog恢复。
场景2:系统表空间中的数据页写完了,用户表空间写坏了。可以通过系统表空间的数据页恢复。

​ 通过双写机制保证数据落盘过程万无一失。

三. MySQL事务篇

(一) 事务介绍

​ MySQL 是一个服务器/客户端架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每 个客户端与服务器连接上之后,就可以称之为一个会话( Session )。我们可以同时在不同的会话里输 入各种语句,这些语句可以作为事务的一部分进行处理。不同的会话可以同时发送请求,也就是说服务 器可能同时在处理多个事务,这样子就会导致不同的事务可能同时访问到相同的记录。我们前边说过事 务有一个特性称之为 隔离性 ,理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该 事务提交之后,其他事务才可以继续访问这个数据。但是这样子的话对性能影响太大,所以设计数据库 的大叔提出了各种 隔离级别 ,来最大限度的提升系统并发处理事务的能力,但是这也是以牺牲一定的 隔 离性 来达到的。

事务特征(ACID)

  • 原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败 。
  • 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏 。
  • 隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提 交)、RR(可重复读)、SERIALIZABLE (串行化)。
  • 持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失 。

事务的隔离级别

隔离级别标识影响详情
未提交读RU(READ UNCOMMITTED)脏读一个事务读取到另一个事务未提交的数据
已提交读RC(READ COMMITTED)不可重复读一个事务因读取到另一个事务已提交的update
可重复读RR(REPEATABLE READ)幻读一个事务因读取到另一个事务已提交的insert数据或者delete数据
串行化SERIALIZABLE不允许 读-写写-读 的并发操作

(二) 事务和MVCC底层原理详解

1、解决数据更新丢失的问题

1)LBCC
基于锁的并发控制。让操作串行化执行。效率低。
2)MVCC
基于版本的并发控制。使用快照形式。效率高。读写不冲突。主流数据库都是使用的MVCC。

2、InnoDB中MVCC的实现

特点:读不加锁,读写不冲突
实现方案:基于undolog+readview实现的。

1)undolog
	回滚日志。需要在undolog中记录未提交操作的原始状态。
	在undolog中会记录版本信息。在每一行记录上都有2-3个隐藏列。如果表没有主键时就是3个,如果有主键就是两个。

隐藏列:

  • rowid:如果没有主键,会自动生成一个隐藏列。(非必须)

  • 回滚指针:指向记录的上一个版本。

  • 事务id:记录了操作这条记录的事务id。

    事务id在mysql中每个事务都有一个唯一的id,并且是自增的。

​ 每次更新时,都是生成一个新的版本,并且由回滚指针指向旧版本。就会形成一个版本链。后台有个purge线程执行清理操作。删除记录时,是在记录上打上删除标记,并不直接删除。

2)readview

​ 生成一个readview相当于生成了一个快照。只要是ReadView不发生变化读到的结果就是相同的
​ readview是一个数组,生成的时机是执行select操作时生成。数组m_ids,其中记录的当前时刻,数据库中活跃的事务id列表。
​ 例如:
​ m_ids:[105,110,111,120]

​ 可见性判断条件:

支持两种事务隔离级别:RC、RR。
读未提交和串行化和MVCC没有关系。

一个select对应一个ReadView(m_ids),select语句执行完毕后ReadView就失效。
如果当前数据库中没有活跃事务,那么ReadView(m_ids)中就包含将要生成的事务id。

3)RC事务隔离级别的实现

读已提交,当前事务中可以读取到其他事务提交的结果。
实现方案:在当前事务中执行select查询,就会生成一个ReadView。如果同一个select再次执行,会再次生成ReadView

4)RR事务隔离级别

可重复读,当前事务中select语句多次执行得到的结果是相同的,无论其他事务是否已经提交。
实现方案:在当前事务中执行select查询,就会生成一个ReadView,之后同一个select使用同一个ReadView

5)小结:RC和RR的区别
	`READ COMMITTD`、`REPEATABLE READ`这两个隔离级别的一个很大不同就是生成`ReadView`的时机不同,`READ COMMITTD`在每一次进行普通`SELECT`操作前都会生成一个`ReadView`,而`REPEATABLE READ`只在第一次进行普通`SELECT`操作前生成一个`ReadView`,之后的查询操作都重复这个`ReadView`就好了。
6)redo log 与 undo log
	redo log顾名思义,就是重做日志,每次数据库的SQL操作导致的数据变化它都会记录一下,具体来说,redo log是物理日志,记录的是数据库页的物理修改操作。如果数据发生了丢失,数据库可以根据redo log进行数据恢复。
	undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。

四. MySQL索引篇

(一) 索引介绍

1、索引是什么

  • 官方介绍索引是帮助MySQL高效获取数据数据结构。更通俗的说,数据库索引好比是一本书前

面的目录,能加快数据库的查询速度

  • 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的

(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。

  • 我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说

明,默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引。

2、索引的优势和劣势

优势:

1.可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。

2.通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。

被索引的列会自动进行排序,包括【单列索引】和【组合索引】,只是组合索引的排序要复

杂一些。

如果按照索引列的顺序进行排序,对应order by语句来说,效率就会提高很多。

劣势:

1.索引会占据磁盘空间

2.索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不

仅要保存数据,还有保存或者更新对应的索引文件。

(二) 索引的使用

1、索引的类型

  • 主键索引:索引列中的值必须是唯一的,不允许有空值。
ALTER TABLE table_name ADD PRIMARY KEY (column_name);
  • 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。
ALTER TABLE table_name ADD INDEX index_name (column_name) ;
CREATE INDEX index_name ON table_name(column_name) ;
  • 唯一索引:索引列中的值必须是唯一的,但是允许为空值。
ALTER TABLE table_name ADD UNIQUE INDEX index_name (column_name) ;
CREATE UNIQUE INDEX index_name ON table_name(column_name) ;
  • 全文索引:只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,

    如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。

#创建表时,创建全文索引 
CREATE TABLE `t_fulltext` ( 
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `content` varchar(100) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY `idx_content` (`content`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

#创建全文索引 
ALTER TABLE `t_fulltext` ADD FULLTEXT INDEX `idx_content`(`content`);
CREATE FULLTEXT INDEX index_name ON table_name(column_name) ;

可以使用MATCH() … AGAINST语法执行全文搜索。

SELECT * FROM t_fulltext WHERE MATCH(content) AGAINST('开课吧');
  • 空间索引:MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。(少用

  • 前缀索引少用

    在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不

    能指定。

    ALTER TABLE table_name ADD INDEX index_name (column1(length));
    
  • 按照索引列的数量

    单列索引:索引中只有一个列。

    组合索引:使用2个以上的字段创建的索引。

    1.组合索引的使用,需要遵循最左前缀原则
    2.一般情况下,建议使用组合索引代替单列索引(主键索引除外)。
    
    #添加方式
    ALTER TABLE table_name ADD INDEX index_name (column1,column2);
    

2、删除索引

ALTER TABLE table_name DROP INDEX index_name;
DROP INDEX index_name ON table_name

3、查看索引

SHOW INDEX FROM table_name \G

(三) 索引的数据结构

1、索引分类
1)聚簇索引

基于主键创建的索引。或者叫做聚集索引,因为索引和数据是保存在一起的。

2)非聚簇索引

除了主键索引之外创建的索引都是非聚簇索引。辅助索引

2、索引的数据结构
1)hash

key-value形式的数据结构。
查询速度非常快。直接通过可以找到value。
优点:速度快。时间复杂度O(1)
缺点:不适合范围查询。不适合做索引。

2)二叉查找树

特点:左子树一定是小于根节点,右子树一定是大于根节点。时间复杂度O(log2n)
问题:根节点的选取最好是所有数据中中间数值。否则左子树和右子树高度可能不一致。最差情况下退化成链表。

3)二叉平衡树查找树

特点:向树中增加节点,动态调整树的平衡状态,要求左右子树的高度相差最大不能超过1.
问题:不断的添加数据,二叉树会不断的调整状态。调整的过程也是非常耗时的。数据量大时,树的高度也会非常高,每查找一个节点就会产生一个磁盘的IO。总体上性能也不太好。
要求树高度要低,推荐使用多叉树。

4)B树

每个节点中可以有多个分支,分支的数量叫做度。分支越多效果越好。每个数据的左右两个分支,左子树小于当前数值,右子树大于当前数值。B树是一种多叉平衡查找树
前提:
每个节点中保存的数据 8byte+4byte+4byte,每个数据节点是16k时,b树的度应该是1000

高度为2的b树可以存储数据量:
1000*1000=10W

特点:
1、每个节点中都保存数据
2、数据出现在中间节点中,就不会出现在叶子节点中。

问题:
选择索引数据结构时,需要考虑两点,一个就是等值查询,一个是范围查询。
如果使用b树做范围查询效率不高

5)B+树(常用)

对b树的改进:
1、b+树中除叶子节点外,中间节点不保存数据,中间节点保存的是主键
2、b+树中所有的数据都存放在叶子节点中
3、b+树中叶子节点之间有双向指针,形成一个双向链表

b+树适合于等值查询也适合于范围查询。在mysql中所有的索引都是b+tree结构。

3、MyIsam引擎的索引结构
1)主键索引
 b+tree结构。在MyIsam引擎中索引和数据是分在不同的文件中存储的。
 中间节点保存都是主键的值,叶子节点中保存的是对应数据行的地址。
2)非主键索引,辅助索引
在普通字段上创建的索引。
对应MyIsam引擎来说**辅助索引和主键索引是相同的**。
4、InnoDB引擎的索引结构
1)主键索引
  聚集索引或者聚簇索引。如果使用的是InnoDB引擎,没有设置主键系统会自动创建一个主键,rowid。
  InnoDB中主键索引和数据是存储在一起的,所以叫做聚簇索引,聚集索引。
  通过主键查询数据效率是最高的,找到叶子节点就找到数据行。

InnoDB引擎必有主键

1. 在表上定义主键PRIMARY KEY,InnoDB将主键索引用作聚簇索引。
2. 如果表没有定义主键,InnoDB会选择第一个不为NULL的唯一索引列用作聚簇索引。
3. 如果以上两个都没有,InnoDB 会使用一个6 字节长整型的隐式字段 ROWID字段构建聚簇索
引。该ROWID字段会在插入新行时自动递增。 
2)非主键索引,辅助索引
非主键索引也是b+tree结构,叶子节点中保存的就是主键的值。 如果根据辅助索引查询,只能得到主键的值,然后根据主键查询聚集索引找到对应的数据,这个过程就叫做回表。
如果使用辅助索引,效率是不如MyIsam引擎的辅助索引效率高。 
3)组合索引(重点)

1、什么是组合索引

组合索引就是辅助索引的一种形式。基于多个字段创建的一个辅助索引。
 创建辅助索引的两种形式:
  1、在每个字段上分别创建一个索引。
  2、创建一个辅助索引,里面包含三个字段。(推荐使用形式,组合索引)
 SQL语句中如果使用到一个表的索引,那么只能使用一个。
#示例
在组合索引中,如果有三个字段a、b、c
  先按照a排序,
  如果a相同,按照b排序
  如果b相同,按照c排序

2、组合索引的使用方法

1)要求查询条件中必须包含组合索引中的第一个字段。
2)可以满足根据a、ab、abc查询都可以。
3)如果条件中只有b、c是否能使用索引?否。
4)如果使用组合索引时,必须有索引中第一个字段,而且后面的条件必须按照创建索引的顺序出现。
  如果条件中a、c,那么只能a条件使用索引,c无法使用索引。

索引使用口诀

 全值匹配我最爱,最左前缀要遵守;
 带头大哥不能死,中间兄弟不能断;
 索引列上不计算,范围之后全失效;
 Like百分写最右,覆盖索引不写星;
 不等空值还有OR,索引失效要少用。
in是可以使用到索引的。如果表中数据量小并且,in集合中数据较多的情况下可能不使用索引。如果数据量大in一定会使用索引。

3、创建组合索引的原则

 1)经常出现在where条件中字段
 2)经常出现order by、group by字段中的
 3)经常出现在select字段列表中的字段
 select id,a,b,c from user wehre xxxx

组合索引的字段顺序如何设计呢?
出现在where条件中频率最高的字段应该排第一位,按照使用频率进行排序

order by使用索引情况的分析:
https://juejin.im/post/6854573217978253320

4)索引覆盖
 查询的字段内容可以直接从索引中获得,无需再回表,这样可以提高查询效率。
 在select语句中不要使用“*”,应该使用字段列表。
5)索引条件下推ICP
索引条件下推在mysql5.7以后才有的一个优化手段。默认开启的。
 作用就是,即便是组合索引条件中断或者范围查询,后面的条件依然可以使用索引。
 #optimizer_switch优化相关参数开关
 mysql> show VARIABLES like 'optimizer_switch'\G;
 #关闭ICP
 SET optimizer_switch = 'index_condition_pushdown=off';
 #开启ICP
 SET optimizer_switch = 'index_condition_pushdown=on';

(四) explain是否使用索引

1、Using index(覆盖索引)

#Extra中为Using index
1 查询的列被索引覆盖
2 并且where筛选条件是索引的是前导列

2、Using where ;Using index

#Extra中为Using where; Using index,
1 查询的列被索引覆盖
2 where筛选条件是索引列之一,但是不是索引的不是前导列,
  或者where筛选条件是索引列前导列的一个范围

3、NULL

#Extra中为NULL(没有信息)
1 查询的列未被索引覆盖,并且where筛选条件是索引的前导列,

意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引,

4、Using where

#Extra中为Using where
1 查询的列未被索引覆盖
2 where筛选条件非索引的前导列或者非索引列

5、Using index condition

1 查询的列不全在索引中,where条件中是一个前导列的范围
2 查询列不完全被索引覆盖,查询条件完全可以使用到索引(进行索引查找)

五.MySQL锁篇

(一) 锁介绍

1. 按照锁的粒度来说

全局锁:锁的是整个database。由MySQL的SQL layer层实现的
表级锁:锁的是某个table。由MySQL的SQL layer层实现的
行级锁:锁的是某行数据,也可能锁定行之间的间隙。由某些存储引擎实现,比如InnoDB。

2. 按照锁的功能来说

共享锁Shared Locks(S锁): 读锁

1、兼容性:加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁 
2、加锁方式:select…lock in share mode 

排他锁Exclusive Locks(X锁): 写锁

1、兼容性:加了X锁的记录,不允许其他事务再加S锁或者X锁 
2、加锁方式:select…for update

(二) 全局锁

​ 全局锁就对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的MDL的写语句,DDL语句,

已经更新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有的表进行锁

定,从而获取一致性视图,保证数据的完整性。

1. 对于innodb这种支持事务的引擎,使用mysqldump备份时可以使用--single-transaction参数,利用
mvcc提供一致性视图,而不使用全局锁,不会影响业务的正常运行。
2.而对于有MyISAM这种不支持事务的表,就只能通过全局锁获得一致性视图,对应的mysqldump参数为--lock-all-tables。

加锁

	mysql> flush tables with read lock;

解锁

	mysql> unlock tables;

​ 或者会话关闭,锁自动解除。
注意:全局锁一般很少使用,会影响业务处理。

(三) 表级锁

1、表级锁介绍

1、表读、写锁。 
2、元数据锁(meta data lock,MDL)。 
3、意向锁 Intention Locks(InnoDB) 
4、自增锁(AUTO-INC Locks)

2、读锁S、写锁X

1) 表锁相关命令
  • MySQL 实现的表级锁定的争用状态变量

    mysql> show status like 'table%';
    
    - table_locks_immediate:产生表级锁定的次数; 
    - table_locks_waited:出现表级锁定争用而发生等待的次数;
    
  • 表锁有两种表现形式

    表共享读锁(Table Read Lock) 
    表独占写锁(Table Write Lock)
    
  • 手动增加表锁:

    lock table 表名称1 read(write),表名称2 read(write),其他;
    
  • 查看表锁情况:

    show open tables;
    
  • 删除表锁

    unlock tables;
    
2) 表锁说明
  • 读锁(当前session加读锁)

    #当前session
    1. 当前session可以查询该表记录,但是不能查询其他没有加锁的表
    2. 当前session进行插入,更新,删除表会报错
    
    #其他session
    1. 其他session可以查询该表记录,也可以查询其他未加锁的表
    2. 其他session对该表进行插入,更新,删除会一直等待获得锁
    
  • 写锁(当前session加写锁)

    #当前session
    1. 当前session可以进行增,删,改,查操作
    
    #其他session
    1. 其他session进行增,删,改,查操作被阻塞,要等待锁被释放
    
  • 注意

    1.S锁的作用,在读取过程中防止其他会话对表进行修改操作,如果一个表上有任何锁,包括S、X锁,都无法再加X锁。
    	一个表上可以加多个S锁,数据可以共享读。
    2.X锁,排它锁,在修改过程中,对表处于一个独享状态。如果一个表上有X锁,那么此时表上不允许加任何锁。
    
    3.锁的兼容性:
    	S、S可以
    	S、X不可以
    	X、X不可以
    表锁一般也不经常使用,粒度比较粗。
    

3、元数据锁

1)元数据锁介绍

当事务未结束时,在事务中执行了select操作,不允许对表结构进行修改的。

MDL不需要显式使用,在访问一个表的时候会被自动加上。在 MySQL 5.5 版本中引入了 MDL

1.当对一个表做增删改查操作的时候,加 MDL 读锁;
2.当要对表做结构变更操作的时候,加 MDL 写锁。
  • 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。

  • 读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同

    时给一个表加字段,其中一个要等另一个执行完才能开始执行。

2)元数据锁演示
1、session1: begin;--开启事务 
   select * from mylock;--加MDL读锁 
2、session2: alter table mylock add f int; -- 修改阻塞 
3、session1:commit; --提交事务 或者 rollback 释放读锁 
4、session2:Query OK, 0 rows affected (38.67 sec) --修改完成 
Records: 0 Duplicates: 0 Warnings: 0

4、自增锁(AUTO-INC Locks)

​ AUTO-INC锁是一种特殊的表级锁,发生涉及AUTO_INCREMENT列的事务性插入操作时产生。

(四) 行级锁(InnoDB独有)

  • MySQL的行级锁,是由存储引擎来实现的,这里我们主要讲解InnoDB的行级锁

  • InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过 索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

    1 记录锁(Record Locks):锁定索引中一条记录。 
    2 间隙锁(Gap Locks):要么锁住索引记录中间的值,要么锁住第一个索引记录前面的值或者最后一个索引记录后面的值。(范围查询,解决MVCC的幻读问题)
    3 临键锁(Next-Key Locks):是索引记录上的记录锁和在索引记录之前的间隙锁的组合(间隙锁+记录锁)。
    4 插入意向锁(Insert Intention Locks):做insert操作时添加的对记录id的锁。
    

MySQL的行级锁,是由存储引擎来实现的,这里我们主要讲解InnoDB的行级锁。 InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过 索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

1)记录锁

​ 只有InnoDB引擎支持行锁。也就是说行锁是在InnoDB中实现。
​ 行锁也有共享锁和排他锁。
加共享锁:

select * from t where id=1 lock in share mode;

加排它锁:

select * from t where id=1 for update;

2)意向锁

  • ​ 意向锁共享锁IS:表中某个记录上有共享锁是添加。
  • ​ 意向排他锁IX:表中某个记录有排他锁是添加
    就是一个标志位,为了快速判断表上是否有行锁

​ 意向锁是表级锁。是InnoDB实现的,是行锁的一个副产品。
​ 不需要人为干预,InnoDB自动处理。在对记录加行锁时由InnoDB自动加意向锁。

是否兼容ISIXSX
IS
IX
S
X

3)间隙锁

  • ​ 区间锁, 仅仅锁住一个索引区间(开区间,不包括双端端点)
  • ​ 间隙锁可用于防止幻读,保证索引间的不会被插入数据
    ​ 间隙锁就是MVCC防止幻读的解决方案。
    ​ MVCC实现的是快照读,加锁不影响读操作。

    ​ 只有RR事务隔离级别才用到间隙锁,RC是不需要的。

4)临键锁

​ 记录锁+间隙锁
​ 默认情况下,innodb使用next-key locks来锁定记录。

  • ​ 记录锁:条件中根据主键等值查询,并且值存在时,只有记录锁。
  • ​ 间隙锁:条件中根据主键等值查询,并且值不存在时,只有间隙锁,锁等值所在的那个区间。
  • ​ 临键锁:条件中根据索引做范围查询,使用临键锁。

如果根据辅助索引更新:

	1)因为辅助索引不唯一,所以更新时,在辅助索引上加临键锁,
	2)并且在主索引上增加记录锁。

5)插入意向锁(Insert Intention Locks)

​ 做insert操作时,会锁定未提交记录的id的值。

6)行锁加锁规则

  • 主键索引
  1. 等值查询

    1)命中记录,加记录锁。
    2)未命中记录,加间隙锁。
    
  2. 范围查询

1)没有命中任何一条记录时,加间隙锁。
2)命中1条或者多条,包含where条件的临键区间,加临键锁
  • 辅助索引
  1. 等值查询

    1)命中记录,命中记录的辅助索引项+主键索引项加记录锁,辅助索引项两侧加间隙锁。
    2)未命中记录,加间隙锁
    
  2. 范围查询

    1)没有命中任何一条记录时,加间隙锁。
    2)命中1条或者多条,包含where条件的临键区间加临键锁。命中记录的id索引项加记录锁。
    

(五) 死锁

有两个或两个以上的资源的情况下,有两个事物互相持有了对方期待的资源,导致互相等待对方释放资源时,产生死锁。

  • 如何避免死锁呢?

MySQL默认会主动探知死锁,并回滚某一个影响最小的事务。等另一事务执行完成之后,再重新执行该事务。

如何避免死锁

1、注意程序的逻辑

根本的原因是程序逻辑的顺序,最常见的是交差更新
Transaction 1: 更新表A -> 更新表B
Transaction 2: 更新表B -> 更新表A
Transaction获得两个资源

2、保持事务的轻量

越是轻量的事务,占有越少的锁资源,这样发生死锁的几率就越小

3、提高运行的速度

避免使用子查询,尽量使用主键等等

4、尽量快提交事务,减少持有锁的时间

越早提交事务,锁就越早释放

六.MySQL性能优化篇

(一)性能优化的思路

1. 首先需要使用【慢查询日志】功能,去获取所有查询时间比较长的SQL语句
2. 查看执行计划,查看有问题的SQL的执行计划
3. 针对查询慢的SQL语句进行优化
4. 使用【show profile[s]】 查看有问题的SQL的性能使用情况
5. 调整操作系统参数优化
6. 升级服务器硬件

(二)慢查询日志

1、慢查询日志介绍

​ 数据库查询快慢是影响项目性能的一大因素,对于数据库,我们除了要优化 SQL,更重要的是得先找到需要优化的SQL。

MySQL数据库有一个“慢查询日志”功能,用来记录查询时间超过某个设定值的SQL语句,这将极大程度帮助我们快速定位到症结所在,以便对症下药。

​ 至于查询时间的多少才算慢,每个项目、业务都有不同的要求。

  • MySQL的慢查询日志功能默认是关闭的,需要手动开启

2、开启慢查询功能

  • 查看是否开启慢查询功能

    mysql> show variables like '%slow_query%';				#查看是否开启慢查询
    mysql> show variables like '%long_query_time%';			#慢查询阈值,当查询时间多于设定的阈值时,记录日志,
    
  • 临时开启慢查询功能

    #在 MySQL 执行 SQL 语句设置,但是如果重启 MySQL 的话将失效
    set global slow_query_log = ON; 
    set global long_query_time = 1;
    
  • 永久开启慢查询功能

    #修改/etc/my.cnf配置文件,重启 MySQL, 这种永久生效.
    [mysqld] 
    slow_query_log=ON 
    long_query_time=1
    

3、慢查询日志格式

mysql> select sleep(3);

查看日志信息:

tail -100f xxx.log;

格式说明:

  • 第一行,SQL查询执行的具体时间

  • 第二行,执行SQL查询的连接信息,用户和连接IP

  • 第三行,记录了一些我们比较有用的信息,如下解析

    Query_time,这条SQL执行的时间,越长则越慢 
    Lock_time,在MySQL服务器阶段(不是在存储引擎阶段)等待表锁时间 
    Rows_sent,查询返回的行数 
    Rows_examined,查询检查的行数,越长就当然越费时间
    
  • 第四行,设置时间戳,没有实际意义,只是和第一行对应执行时间。

  • 第五行及后面所有行(第二个# Time:之前),执行的sql语句记录信息,因为sql可能会很长。

4、分析慢查询日志的工具

使用mysqldumpslow工具,mysqldumpslow是MySQL自带的慢查询日志工具。可以使用mysqldumpslow工具搜索慢查询日志中的SQL语句。

[root@localhost mysql]# mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/slow.log

说明:

  • -s:是表示按照何种方式排序

    al 平均锁定时间
    ar 平均返回记录时间
    at 平均查询时间(默认)
    c 计数
    l 锁定时间
    r 返回记录
    t 查询时间
    
  • -t:是top n的意思,即为返回前面多少条的数据

  • -g:后边可以写一个正则匹配模式,大小写不敏感的

[root@mysql132 mysql]# mysqldumpslow -s t /var/lib/mysql/mysql132-slow.log 

Reading mysql slow query log from /var/lib/mysql/mysql132-slow.log 

Count: 1 Time=143.16s (143s) Lock=0.00s (0s) Rows=27907961.0 (27907961), 
root[root]@localhost select * from t_slow a left join t_slow b on a.name=b.name
    
Count: 5 Time=5.80s (28s) Lock=0.00s (0s) Rows=0.0 (0), 
root[root]@localhost insert into t_slow(name,address) select name,address from t_slow 

Count: 1 Time=3.01s (3s) Lock=0.00s (0s) Rows=1.0 (1), root[root]@localhost select sleep(N)

(三)查看执行计划explain

1.参数说明

mysql> explain select * from tuser where id = 2 \G 
*************************** 1. row ***************************
	id: 1 
	select_type: SIMPLE 
	table: tuser 
	partitions: NULL 
	type: const 
	possible_keys: PRIMARY 
	key: PRIMARY 
	key_len: 4 
	ref: const 
	rows: 1 
	filtered: 100.00 
	Extra: NULL 
1 row in set, 1 warning (0.01 sec)

说明:

  • id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.

    id相同:执行顺序由上到下			#多表同级查询
    id不同:如果是子查询,id号会自增,id越大,优先级越高。		#多表存在子查询
    
  • select_type: SELECT 查询的类型.

    1.simple
    	表示不需要union操作或者不包含子查询的简单select查询。有连接查询时,外层的查询为simple。
    2.primary
    	一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary。
    3.union
    	union连接的两个select查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是union
    4.dependent union
    	与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响
    5.union result
    	包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null
    6.subquery
    	除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquery
    7.dependent subquery
    	与dependent union类似,表示这个subquery的查询要受到外部表查询的影响
    8.derived
    	from字句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select
    
    
    #注意;
    ·DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中
    ·UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    ·UNION RESULT 从UNION表获取结果的SELECT
    
  • table: 查询的是哪个表

    1 如果查询使用了别名,那么这里显示的是别名
    2 如果不涉及对数据表的操作,那么这显示为null
    3 如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。
    4 如果是尖括号括起来的<union M,N>,与类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。
    
  • partitions: 匹配的分区

    #使用的哪些分区(对于非分区表值为null)。
    什么是分区表?
    	mysql内部实现的表的水平拆分,所有数据还在一个表中,但物理存储根据一定的规则放在不同的文件中。这个是mysql支持的功能,业务代码无需改动。
    技术现状:
    	业内进行一些技术交流的时候也更多的是自己分库分表,而不是使用分区表。
    	1)分区表,分区键设计不太灵活,如果不走分区键,很容易出现全表锁
    	2)一旦数据量并发量上来,如果在分区表实施关联,就是一个灾难
    	3)自己分库分表,自己掌控业务场景与访问模式,可控。分区表,研发写了一个sql,都不确定mysql是怎么玩的,不太可控
    	4)运维的坑
    
  • type: join 类型

    #显示的是单位查询的连接类型或者理解为访问类型,访问性能依次从好到差:
    system 
    const 
    eq_ref 
    ref 
    fulltext 
    ref_or_null 
    unique_subquery 
    index_subquery 
    range 
    index_merge 
    index ALL
    
    #注意
    - 除了all之外,其他的type都可以使用到索引 
    - 除了index_merge之外,其他的type只可以用到一个索引 
    - 最少要使用到range级别
    
  • possible_keys: 此次查询中可能选用的索引

    此次查询中可能选用的索引,一个或多个
    
  • key: 此次查询中确切使用到的索引.

    查询真正使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。
    
  • key_len:

    key_len越小 索引效果越好
    
  • ref: 哪个字段或常数与 key 一起被使用

    1 如果是使用的常数等值查询,这里会显示const
    2 如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段
    3 如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
    
  • rows: 显示此查询一共扫描了多少行. 这个是一个估计值.

    这里是执行计划中估算的扫描行数,不是精确值(InnoDB不是精确的值,MyISAM是精确的值,主要原
    因是InnoDB里面使用了MVCC并发机制)
    
  • filtered: 表示此查询条件所过滤的数据的百分比

    filtered列指示将由mysql server层需要对存储引擎层返回的记录进行筛选的估计百分比,也就是说存储引擎层返回的结果中包含有效记录数的百分比。最大值为100,这意味着没有对行进行筛选。值从100减小表示过滤量增加。rows显示检查的估计行数,rows×filtered显示将与下表联接的行数。例如,如果rows为1000,filtered为50.00(50%),则要与下表联接的行数为1000×50%=500。
    
  • extra: 额外的信息

(四)SQL语句优化(开发人员)

1、索引优化

  • 为搜索字段(where中的条件)、排序字段、select查询列创建合适的索引,不过要考虑数据的

    业务场景:查询多还是增删多?

  • 尽量建立组合索引并注意组合索引的创建顺序,按照顺序组织查询条件、尽量将筛选粒度大的查询条件放到最左边。

  • 尽量使用覆盖索引,SELECT语句中尽量不要使用*。

  • order by、group by语句要尽量使用到索引

  • 索引长度尽量短,短索引可以节省索引空间,使查找的速度得到提升,同时内存中也可以装载更多的索引键值。太长的列,可以选择建立前缀索引

  • 索引更新不能频繁,更新非常频繁的数据不适宜建索引,因为维护索引的成本。

  • order by的索引生效,order by排序应该遵循最佳左前缀查询,如果是使用多个索引字段进行排序,那么排序的规则必须相同(同是升序或者降序),否则索引同样会失效。

2、LIMIT优化

  • 如果预计SELECT语句的查询结果是一条,最好使用 LIMIT 1,可以停止全表扫描。

  • 处理分页会使用到 LIMIT ,当翻页到非常靠后的页面的时候,偏移量会非常大,这时LIMIT的效率会非常差。 LIMIT OFFSET , SIZE;

    LIMIT的优化问题,其实是 OFFSET 的问题,它会导致MySql扫描大量不需要的行然后再抛弃掉。

    解决方案:单表分页时,使用自增主键排序之后,先使用where条件 id > offset值,limit后面只写rows

3、其他查询优化

  • 小表驱动大表,建议使用left join时,以小表关联大表,因为使用join的话,第一张表是必须全扫描的,以少关联多就可以减少这个扫描次数。

  • 避免全表扫描,mysql在使用不等于(!=或者<>)的时候无法使用索引导致全表扫描。在查询的时候,如果对索引使用不等于的操作将会导致索引失效,进行全表扫描

  • 避免mysql放弃索引查询,如果mysql估计使用全表扫描要比使用索引快,则不使用索引。(最典型的场景就是数据量少的时候)

  • 尽量不使用count(*)、尽量使用count(主键)

  • JOIN两张表的关联字段最好都建立索引,而且最好字段类型是一样的。

  • WHERE条件中尽量不要使用not in语句(建议使用not exists)

  • 合理利用慢查询日志、explain执行计划查询、show profile查看SQL执行时的资源使用情况

(五)profile分析语句

1、介绍

Query Profiler是MySQL自带的一种query诊断分析工具**,通过它可以分析出一条SQL语句的**硬件性能

瓶颈在什么地方。通常我们是使用的explain,以及slow query log都无法做到精确分析

工具只有在MySQL 5.0.37以及以上版本中才有实现。

**默认的情况下,**MYSQL的该功能没有打开,需要自己手动启动。

2、开启Profile功能

  • Profile 功能由MySQL会话变量 : profiling控制,默认是OFF关闭状态。

  • 查看是否开启了Profile功能:

    select @@profiling; 
    -- 或者 
    show variables like '%profil%';
    
  • 开启profile功能

    set profiling=1; --1是开启、0是关闭
    

3、语句使用

  • show profileshow profiles 语句可以展示当前会话(退出session后,profiling重置为0) 中执行语句的资源使用情况.
  • show profiles :以列表形式显示最近发送到服务器上执行的语句的资源使用情况.显示的记录数由变量:profiling_history_size 控制,默认15条
  • show profile: 展示最近一条语句执行的详细资源占用信息,默认显示 Status和Duration两列
  • show profile 还可根据 show profiles 列表中的 Query_ID ,选择显示某条记录的性能分析信息

七.MySQL集群篇

(一)集群搭建之主从复制

(二)集群搭建之读写分离

八.MySQL分库分表篇

九.补充篇

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值