括号里的结果集小的时候用in,反之用exists。
in返回的是数据,而exists返回true或false。
CREATE TABLE `tblA`(
# 只有age和birth字段
#`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;
这里因为age为范围查找,所以复合索引到不了birth,如果age等于固定值查找,birth就可以用到复合索引。
这里排序的字段没按照顺序,所以排序的两个字段都用不到这个复合索引。
倒序排序使得用不上前一个字段的排序,从而进行文件内排序。(都是正序或者都是倒序的即可使用)
MySQL41之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和order by列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。
从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行了两次扫描,众所周知,I/O是很耗时的,以在mysq4.1之后,出现了第二种改进的算法,就是单路排序。
从磁盘读取査询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间因为它把每一行都保存在内存中了。
查看:SHOW VARIABLES LIKE '%slow_query_log%';
(默认关闭)
开启:SET GLOBAL slow_query_log=1;
(重启mysql就会失效)
慢于指定时间的sql就会被记录到慢查询日志中,阈值是参数long_query_time,默认为超过10秒算慢查询。
查看慢查询日志的阈值:SHOW VARIABLES LIKE 'long_query_time%';
(默认10秒)
设置阈值时间:SET GLOBAL long_query_time=3;
为什么设置后还是显示10秒?
其实已经生效了,只是还没显示出来,加上GLOBAL
关键字再去查询即可。
查看:SHOW GLOBAL VARIABLES LIKE 'long_query_time%';
记录慢SQL并后续分析
查看当前系统中有慢查询记录数:SHOW GLOBAL STATUS LIKE '%Slow_queries%';
查看mysqldumpslow的帮助信息:mysqldumpslow --help
工作常用参考
mysqldumpslow -s r -t 10 /var/lib/mysql/54e3aba6888e-slow.log
往表里插入1000万条数据
建表
CREATE TABLE `dept`(
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`deptno` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
`dname` VARCHAR(20) NOT NULL DEFAULT "",
`loc` VARCHAR(13) NOT NULL DEFAULT ""
)ENGINE=INNODB DEFAULT CHARSET=GBK;
CREATE TABLE emp(
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`empno` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
`ename` VARCHAR(20) NOT NULL DEFAULT "",
`job` VARCHAR(9) NOT NULL DEFAULT "",
`mgr` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
`hiredate` DATE NOT NULL,
`sal` DECIMAL(7,2) NOT NULL,
`comm` DECIMAL(7,2) NOT NULL,
`deptno` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0
)ENGINE=INNODB DEFAULT CHARSET=GBK;
设置参数log_bin_trust_function_creators
查看:SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
修改参数:SET GLOBAL log_bin_trust_function_creators=1;
创建函数,保证每条数据都不同
# DELIMITER:修改sql语句的结束符号,这里把 ; 改为 $$
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_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(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END $$
# DELIMITER ; #把结束符号修改回 ;
DELIMITER ;
# 删除语句
#drop function rand_string;
DELIMITER $$
CREATE FUNCTION rand_num() RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(100+RAND()*10);
RETURN i;
END $$
DELIMITER ;
# 删除语句
drop function rand_num;
创建存储过程
DELIMITER $$
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #关闭自动提交,提高性能
REPEAT #重复插入
SET i = i + 1;
INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)VALUES
((START+i),rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
COMMIT; #提交
END $$
DELIMITER ;
# 删除语句
drop procedure insert_emp;
DELIMITER $$
CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO dept(deptno,dname,loc)VALUES((START+i),rand_string(10),rand_string(8));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$
DELIMITER ;
# 删除语句
drop procedure insert_dept;
调用存储过程
运行SQL
select * from emp group by id%10 limit 150000;
select * from emp group by id%20 order by 5;
诊断SQL,show profile cpu,block io for query 【使用show profiles查出的Query_Id】;
这里显示的和下图老师显示的不同,并没有出现Copying to tmp table
,而是executing
。
可用参数:
上面的命令用到了CPU和BLOCK IO两个参数。
全局查询日志:开启后保存所有输入SQL语句,保存到mysql库里的general_log表中(安装mysql就有的)。
set global general_log=1;
set global log_output='TABLE';
select * from mysql.general_log;
只能在测试环境下使用,永远不要在生产环境开启这个功能(全局查询日志),因为开启后会把所有输入的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`;
# 查看表上加过的锁
show open tables;
# 手动增加表锁
lock table 表名 read(write), 表名2 read(write), 其它;
# 释放表锁
unlock tables;
加读锁
读锁可以共享读操作,即当前用户和其他用户都可以进行读操作,但是当前用户不能修改当前表,并且此时不能读其他表,而其他用户对当前表进行写操作时会进入阻塞状态,等待加了读锁的用户释放锁后才能进行写操作。
窗口1给mylock表加了表锁,可读,但不可写,并且不可读其他表。
窗口2可以读加了表锁的mylock表,并且可以读其他表,但是想写mylock表时,会进入阻塞状态,等待窗口1释放锁后进行写操作。
窗口1解锁:unlock tables;
窗口1解锁后,窗口2写操作成功。
加写锁
写锁为独享锁,加了写锁的表只能由加了锁的用户进行读写,但此用户不能对其他表进行操作,其他用户对其进行读写操作时,都会进入阻塞状态。
窗口1解锁后,窗口2完成读操作。
个人的记忆小技巧:原子一致,才能隔离持久。(对应ACID的顺序)
更新丢失
事务A和事务B同时进行修改,读取的数据都一样,事务A先一步完成修改提交后,事务B的提交的数据覆盖事务A已经修改了的数据,造成事务A更新的丢失。
脏读
事务A修改了数据但还未提交时,事务B读取到事务A修改了但未提交的数据,并进行了数据库上的操作,此时事务A出现问题,进行了回滚,造成事务B脏读。
不可重复读
事务A读取了某数据后,事务B进行了修改或者删除,之后事务A再次读取此数据时,发现此数据发生了改变或已被删除,此为不可重复读。
幻读
事务A按照某查询条件读取数据后,事务B插入了满足此查询条件的数据,之后事务A再次按照此查询条件读取数据,发现多了或少了某些数据,此为幻读。
查看隔离级别:
mysql5:show variables like 'tx_isolation';
mysql8:select @@transaction_isolation;
默认为可重复读(rr)。
建表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;
行锁定基本演示
关闭窗口1和窗口2的自动提交:set autocommit=0;
当关闭自动提交后,窗口1修改了数据,但未提交,此时窗口2也对此数据进行修改,就会进入阻塞状态,等待窗口1提交后,再对数据进行修改,如果窗口2等待的时间超过阈值,就会报错error。
当窗口1提交后,窗口2完成对数据的修改,但由于都关闭了自动提交,此时窗口2去查看数据已经修改为4003,而窗口1去查看,由于窗口2还未提交,所以看到的还是4002。(这是由于隔离级别为可重复读的缘故)
因为两个窗口都关闭了自动提交,所以当一个提交后,另一个也需要提交才能看到修改后的数据。
由于两个窗口修改的不是同一行,所以并不会被另一方影响到。
无索引行锁升级为表锁
由于字段b为varchar类型的,而窗口1查询时没加引号导致索引失效,从而导致行锁变成表锁,此时,其他用户对此表的其他行进行修改也被迫进入阻塞状态,直到窗口1提交后,其他用户才能修改。
由于操作的表中字段a没有2,而窗口1操作的范围包括到2,而此时,窗口2插入a为2的数据,窗口1还修改后还未提交,会造成窗口2进入阻塞状态,等待窗口1提交后才可插入数据,这是因为窗口1操作的范围内,会对每一行加上行锁,不管有没有数据。
虽然窗口2插入的数据在窗口1修改的数据范围里,但是并没有被修改。
begin;
# 为a=8行上锁
select * from test_innodb_lock where a=8 for update;
commit;
update test_innodb_lock set b='xxxx' where a=8;
为某行上锁:select * from 【表名】 where 【字段】=【xx】 for update;
如何锁定一行:在查询要上锁的数据的sql语句后加上for update
即可。
show status like 'innodb_row_lock%';
slave会从master读取 binlog来进行数据同步。
之前docker上两台mysql8配置的主从复制:docker mysql8.0.18 一主一从 实现 主从复制
分库分表中间件Sharding-Sphere学习笔记:Sharding-Sphere 学习笔记
Sharding-Sphere也是看尚硅谷的视频学的,感觉还不错。
CentOS7 关闭和开启防火墙
学习视频(p46-p63):https://www.bilibili.com/video/BV1KW411u7vy?p=46