赞
踩
-- 1、关系运算符:> < >= <= = <> (也可以用!=,是一种宽容处理,不建议)
select count(last_name),last_name,first_name FROM employees WHERE salary<>20000 -- 聚集函数只能显示一行
-- 2、按逻辑表达式筛选:and or
-- 3、范围表达式:between and,not between and
-- 4、确定集合:in,not in
-- 5、字符匹配:like,not like
-- 6、空值:is null ,is not null
select last_name,first_name,manager_id FROM employees limit 0,3
select last_name,first_name FROM employees WHERE commission_pct is NULL
-- 1、in
不相关子查询:子查询不依赖父级参数
select * from student where sdep in(select * from sdept)
-- 2、单一关系子查询,即子查询只会返回一条记录
相关子查询:子查询依赖父级参数
- select sno,sname,grade
- from sc x
- where grade >= (
- select AVG(grade) from sc y
- where x.sno = y.sno
- )
-- 3、(any、all)多关系子查询,即子查询会返回多条记录
相关的关系函数格式:>any、>all、<any、<all、>=any、>=all、<=any、<=all、=any、=all、<>any、<>all
- select sname,sage
- from student
- where sdept<any(
- select sage
- from student
- where sdept = 'cs'
- )
-- 4、exists子查询:存在返回true,反之false,比如select *,只要有一条记录就返回true
- select sname
- from student
- where exists(
- select *
- from sc
- where sno = student.sno and cno = '1'
- )
-- 1、union:并集
- select * from student where sdept = 'cs'
- union
- select * from student where sage <= 19
-- 2、intersect:交集
- select * from student where sdept = 'cs'
- intersect
- select * from student where sage <= 19
-- 3、except:差集,如下获取院系为cs的19岁以上学生
- select * from student where sdept = 'cs'
- except
- select * from student where sage <= 19
- select sno,cno
- from sc,(select sno,avg(grade) from sc group by sno)
- as avg_sc(avg_sno,avg_grade) //临时表
- where sc.sno = avg_sc.avg_sno and sc.grade >= avg_sc.avg_grade
分析工具认为派生表的table=temporary & type=all,因此要尽量避免派生表和其它表的关联查询;
关联查询可以直接多join查询,这样不会衍生派生表,格式如下:
select t_id,t1_id,t2_id from table1 INNER JOIN table2 on table1.f1 = table2.f1 INNER JOIN table3 on table1.f1 = table3.f1
- -- 排序查询,另外讲下查询顺序,先from表,然后where条件查询,接着select,最后order by
- select last_name,first_name,salary FROM employees WHERE commission_pct is NULL ORDER BY salary,first_name DESC
- select last_name,first_name,salary FROM employees WHERE commission_pct is NULL ORDER BY first_name DESC,salary
-- 常见函数:字符函数、数学函数、日期函数、流程控制函数、自定义函数
-- 1、字符函数
-- 拼接:CONCAT(str1,str2,...)
select CONCAT(first_name,last_name,IFNULL(commission_pct,"null")) FROM employees
-- 获取字节长度:LENGTH(str)
select LENGTH(first_name) FROM employees
-- 获取字符长度:CHAR_LENGTH(str)
select CHAR_LENGTH(first_name) FROM employees
-- 截取子串:SUBSTR(str FROM pos FOR len)
SELECT SUBSTR('张三丰爱上了郭襄',1,3)
-- 替换数据:INSERT
select INSERT('mysql',1,2,'MY')
-- 获取字符第一次出现的索引:INSTR(str,substr)
select INSTR('中华人民共和国和中华民国的那些事儿','中华')
-- 去除前后空格:TRIM([remstr FROM] str)
select TRIM(' 虚 竹 ')
-- 去除前后指定的字符:TRIM([remstr FROM] str)
select TRIM('xy' FROM 'xyxxx虚xx竹xxxxxy')
-- 去掉左边/右边指定字符
select LTRIM(' 虚 竹 ')
select RTRIM(' 虚 竹 ')
-- 返回字符串str最左边/右边的len个字符:LEFT(str,len)/RIGHT(str,len)
select LEFT('str',2)
select RIGHT('str',2)
-- LPAD/RPAD 左填充/右填充,结果都一样只是方向不同
select LPAD('木婉清',3, 'a')
select RPAD('木婉清',10,'a')
-- UPPER/LOWER 大小写
select UPPER('abcd')
-- 案例:查询员工表的姓名,要求格式:姓首字符大写,其它字符小写,名所有字符大写,且姓和名之间用_分割,最后起别名为OUTPUT
select UPPER(SUBSTR(first_name,1,1)),first_name FROM employees
select LOWER(SUBSTR(first_name,2)),first_name FROM employees
select UPPER(last_name) FROM employees
select concat(UPPER(SUBSTR(first_name,1,1)),LOWER(SUBSTR(first_name,2)),'_',UPPER(last_name)) OUTPUT FROM employees
-- STRCMP(expr1,expr2) 比较
select STRCMP('A','a')
select STRCMP('B','a')
-- 2、数学函数
-- 绝对函数:ABS(X)
select abs(-2.6)
-- 向上取整:CEIL(X)
select CEIL(1.09)
-- 四舍五入
select ROUND(1.87123)
-- 四舍五入小数点后保留三位
select ROUND(1.87123,3)
-- 截断:truncate
select TRUNCATE(1.87,1)
select TRUNCATE(1.87,0)
-- 取余数:MOD(N,M)
select MOD(-10,3)
select 3/10
-- 3、日期函数
select NOW()
select CURDATE()
select CURTIME()
select DATEDIFF('2019-7-13','1998-7-16')
select DATE_FORMAT('2019-7-13 18:20:20','%Y年%m月%d日 %H小时%i分钟%s秒')
select STR_TO_DATE('3/15 1998','%m/%d %Y')
select * from employees WHERE hiredate < STR_TO_DATE('3/15 1998','%m/%d %Y')
-- 4、流程控制
SELECT IF ( 100 > 9,
'好',
'坏'
)
SELECT
department_id,
salary,
CASE
department_id
WHEN 30 THEN
1
WHEN 50 THEN
2 ELSE 3
END result
FROM
employees
-- 聚合/统计函数
COUNT(DISTINCT expr,[expr...])
MAX(expr)
MIN(expr)
AVG([DISTINCT] expr)
SUM(expr)
-- 补充:一般查询count直接用*,意思是只要查询行有字段不为null就算上,相对快速
COUNT(*)
-- 分组
select count(*),salary from employees
-- 分组+HAVING:having是对统计函数作条件过滤
select COUNT(*),AVG(salary) avg from employees GROUP BY salary HAVING avg>4000
1.笛卡尔连接:
select * from tableA,tableB ...
select * from tableA inner join tableB ...(不加on条件也是笛卡尔积的一种写法)
例如下面两条sql,功能完全一致
select * from table1 inner join table1_copy1 ORDER BY t_id limit 50;
select * from table1,table1_copy1 ORDER BY table1.t_id limit 50;
2.内连接(其实所有连接操作底层都是笛卡尔的特定格式)
2.1.内连接:SELECT * FROM student INNER JOIN score ON student.studentno=score.studentno
2.2.等值连接:基于笛卡尔积,添加等值条件:SELECT * FROM student, score WHERE student.studentno=score.studentno 或者 SELECT * FROM student inner join score WHERE student.studentno=score.studentno
2.3.不等值连接:同等值连接,只是运算符包括>、>=、<=、<、!>、!<和<>。
2.4.自然连接:有去重功能的内连接,比如两张表的关联字段同名同类型(tableA.f1 & tableB.f1),联表查询结果一般会显示两列该字段,而自然连接可以去重保证只显示一列即可:SELECT * FROM tableA NATURAL JOIN tableB ON tableA.f1=tableB.f1 (结果只会展示一个f1字段)
3.外连接
3.1.左外连接:select * from student LEFT JOIN score ON student.studentno=score.studentno
3.2.右外连接:select * from student RIGHT JOIN score ON student.studentno=score.studentno
3.3.全连接:select * from student FULL JOIN score ON student.studentno=score.studentno
Join介绍(MySQL不提供full join)
Join是SQL语句中非常重要的一个运算操作,常见的Join操作如下:
mysql 官方只提供了内连接,左外连接,右外连接三种方式。通过一定的方法也可以实现其它的连接。
数据准备
a.创建两张表
- create table `person` (
- `id` int(11),
- `name` varchar(255),
- `city_id` int(11)
- ) ;
-
- create table `city` (
- `city_id` int(11) ,
- `city_name` varchar(255)
- ) ;
插入数据
- # 向person表中插入数据
- insert into person values (1, 'name1', 1);
- insert into person values (2, 'name2', 2);
- insert into person values (3, 'name3', 3);
- insert into person values (4, 'name4', 5);
-
- #向city表中插入数据
- insert into city values (1, 'city1');
- insert into city values (2, 'city2');
- insert into city values (3, 'city3');
- insert into city values (4, 'city4');
1、左外连接
左外连接返回左表的所有行,如果右表中没有匹配行,则返回NULL。
select * from city left join person on city.city_id = person.city_id;
运行结果:
- +---------+-----------+------+-------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+-------+---------+
- | 1 | city1 | 1 | name1 | 1 |
- | 2 | city2 | 2 | name2 | 2 |
- | 3 | city3 | 3 | name3 | 3 |
- | 4 | city4 | NULL | NULL | NULL |
- +---------+-----------+------+-------+---------+
2、内连接
内连接返回的是两张表共有的数据。
select * from person inner join city on person.city_id = city.city_id
运行结果:
- +------+-------+---------+---------+-----------+
- | id | name | city_id | city_id | city_name |
- +------+-------+---------+---------+-----------+
- | 1 | name1 | 1 | 1 | city1 |
- | 2 | name2 | 2 | 2 | city2 |
- | 3 | name3 | 3 | 3 | city3 |
- +------+-------+---------+---------+-----------+
3、右外连接
右外连接返回右表的所有行,如果左表中没有匹配行,则返回NULL。
select * from city right join person on person.city_id = city.city_id;
运行结果:
- +---------+-----------+------+-------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+-------+---------+
- | 1 | city1 | 1 | name1 | 1 |
- | 2 | city2 | 2 | name2 | 2 |
- | 3 | city3 | 3 | name3 | 3 |
- | NULL | NULL | 4 | name4 | 5 |
- +---------+-----------+------+-------+---------+
4、左连接
左连接是得到A表中去除B表内容的剩下的部分,也就是A表独有的一部分。可以看做是在左外连接的结果中将双方共有的部分去掉得到的。
select * from city left join person on city.city_id = person.city_id where person.city_id is null;
运行结果:
- +---------+-----------+------+------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+------+---------+
- | 4 | city4 | NULL | NULL | NULL |
- +---------+-----------+------+------+---------+
5、右连接
右连接是得到B表中去除A表内容的剩下的部分,也就是B表独有的一部分。可以看做是在右外连接的结果中将双方共有的部分去掉得到的。
select * from city right join person on city.city_id = person.city_id where city.city_id is null;
运行结果:
- +---------+-----------+------+-------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+-------+---------+
- | NULL | NULL | 4 | name4 | 5 |
- +---------+-----------+------+-------+---------+
6、全连接
全连接返回AB两表全部的数据,mysql没有提供full join关键字,不过可以使用使用union来实现,全连接等于左外连接与右外连接的并集。
- select * from city left join person on person.city_id = city.city_id
- union
- select * from city right join person on person.city_id = city.city_id;
运行结果:
- +---------+-----------+------+-------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+-------+---------+
- | 1 | city1 | 1 | name1 | 1 |
- | 2 | city2 | 2 | name2 | 2 |
- | 3 | city3 | 3 | name3 | 3 |
- | 4 | city4 | NULL | NULL | NULL |
- | NULL | NULL | 4 | name4 | 5 |
- +---------+-----------+------+-------+---------+
7、差集
差集就是两张表都没有同时出现的数据集,其实也就是左连接与右连接的并集。
- select * from city left join person on city.city_id = person.city_id where person.city_id is null
- union
- select * from city right join person on city.city_id = person.city_id where city.city_id is null;
运行结果:
- +---------+-----------+------+-------+---------+
- | city_id | city_name | id | name | city_id |
- +---------+-----------+------+-------+---------+
- | 4 | city4 | NULL | NULL | NULL |
- | NULL | NULL | 4 | name4 | 5 |
————————————————
例题
1、简单的两表查询
use myemployees
select last_name,department_name FROM employees e,departments d
2、添加筛选条件
查询部门编号>100的部门名和所在的城市名
查询有奖金的员工名、部门名
查询城市名中第二个字符为o的部门名和城市名
3、添加分组+筛选
查询每个城市的部门个数
查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
查询部门中员工个数>10的部门名
4、添加分组+筛选+排序
查询部门中员工个数>10的部门名,并按照部门名降序
查询每个工种的员工个数和工种名,并按照个数降序
5、三表连接
案例查询员工名、城市名
例如
----------where and:临时表生成后才条件查询----------
select * from table1 left join table2 on table1.f1=table2.f1 where table1.f1 <> 1 and table2.f2 <> 2
----------and and:临时表生成时就条件查询----------
select * from table1 left join table2 on table1.f1=table2.f1 and table1.f1 <> 1 and table2.f2 <> 2
----------and where:临时表生成时就条件查询 + 临时表生成时就条件查询----------
select * from table1 left join table2 on table1.f1=table2.f1 and table1.f1 <> 1 where table2.f2 <> 2
区别
整型
tinyint smallint int bigint
浮点型
double(m,n) float decimal m表示总长,n表示小数点后保留位数
字符型
char varchar text
二进制,用来存储图形数据
blob
日期型
date 格式:yyyy-MM-dd
time 格式:hh:mm:ss
timestamp 格式:yyyyMMdd hhmmss
datetime 格式:yyyy-MM-dd hh:mm:ss
not null
default
primary key
FOREIGN KEY
unique
check
例如:create table sc(
sno char(9) [not null,unique,PRIMARY KEY],
cno char(4),
grade SMALLINT,
[PRIMARY key(sno,cno),]
[FOREIGN KEY(sno) REFERENCES student(sno),]
[check(grade<>0 and grade>60)]
)
1、DDL (database definition language)
创表
CREATE TABLE IF NOT EXISTS xxx;
删表
drop table if exists xxx [RESTRICT/CASCADE];
修改:ALTER TABLE 语句用于在已有的表中添加、修改或删除列,。
1. ALTER TABLE table_name ADD column_name datatype
2. ALTER TABLE table_name DROP COLUMN column_name
3. ALTER TABLE table_name ALTER COLUMN column_name datatype
4. ALTER TABLE table_name RENAME COLUMN column_name to newname
5. ALTER TABLE table_name RENAME to newname
复制表
复制结构+数据
create table xxx select * from yyy
create table xxx select * from yyy.column
复制结构
create table xxx like yyy
2、DML(database manipulate language)
delete from tablename
update tablename set colume = newvalue where ...
insert into tablename values(...)
3、DQL
select * from tablename [lock in share mode|for update]
4、DCL
rollback、commit、grant...
innodb默认事务自动开启
1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
3. InnoDB支持主键
4. InnoDB是聚簇索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。而辅助(非聚簇)索引需要两次查询,先查询到主键,然后再通过主键查询到数据。
5. 5.6版本后InnoDB也支持了全文索引
6. InnoDB不只支持表锁还支持行锁
7. Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI
Innodb:frm是表定义文件,ibd是结构+数据文件
Myisam:frm是表定义文件,myd是数据文件,myi是索引文件
show variables like '%auto%'
关闭自动提交
set autocommit =0
开启手动事务
start TRANSACTION
编写事务的sql语句
...
提交或者回滚(如果在执行时有上行锁)
commit or rollback
举例
START TRANSACTION
select count(*) FROM departments
delete FROM departments where department_id = 1
select count(*) FROM departments
ROLLBACK
ACID:原子性、一致性、隔离性、永久性
1、查看当前会话的 隔离级别:
select @@tx_isolation;
2、查看系统的隔离级别:
select @@global.tx_isolation;
3、设置会话的隔离级别,隔离级别由低到高设置依次为:
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
4、设置当前系统的隔离级别,隔离级别由低到高设置依次为:
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
【注:】mysql默认的事务处理级别是'REPEATABLE-READ',而Oracle和SQL Server是READ_COMMITED
全网最全的一篇数据库MVCC详解,不全我负责-mysql教程-PHP中文网
SQL语句归根结底是读写操作,因而只有读写锁,而读写锁根据加锁范围又分为行锁和表锁:
行锁的劣势:开销大;加锁慢;会出现死锁
行锁的优势:锁的粒度细(就是管理范围,越细越小,越粗越大),发生锁冲突的概率低;处理并发的能力强
上锁方式:自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁;当然我们也可以显示的加锁:
1、读/共享锁:select * from tableName where … + lock in share more
2、写/独占锁/排他锁:select * from tableName where … + for update、UPDATE、DELETE 和 INSERT
上锁时机:SQL执行语句时
解锁时机:有事务的话要到commit/rollback时解锁,无事务执行完SQL时解锁
表锁的劣势:并发慢,锁冲突可能性大
表锁的优势:锁粒度粗,资源占用少
一般表锁上锁/解锁时机:
1、读/共享锁(S锁):lock/unlock table 表名 read
2、写/独占锁(X锁):lock/unlock table 表名 write
3、全部解锁:unlock tables,必须手动解锁,不像行锁,事务或语句结束后自动解锁
4、表锁的兼容性(即表级S、X兼容性)
S X
S + -
X - -
1、作用仅为了解决行锁和表锁冲突:例如事务A加了行锁,事务B想加表锁,B需要遍历每行查看是否有行锁,而等查到事务A的行锁时已经花了很长时间,为了解决这种问题引入了意向锁,意向锁是一种表级读写锁,加行锁前先加意向锁,这样当事务B要加表锁时只需要查看是否加了表锁或者意向锁即可,效率提升;
2、意向锁不操心行锁与行锁间的冲突:如果两个事务都申请意向锁,则意向锁间相互兼容(即 IS和IX、IS和IS、IX和IS 间相互兼容),因为1中已经说了在加行锁前才会加意向锁,所以当前实际是行锁间冲突,而行锁机制会解决并发问题,不需要意向锁来操心
3、意向锁IS、IX和表级锁S和X的冲突:
IS IX S X
IS + + + -
IX + + - -
S + - + -
X - - - -
上锁/解锁时机:MySQL加行锁前先自动加上意向锁,解锁时如果有事务的话commit/rollback时解锁,如果没有事务SQL语句结束就释放锁
读写锁:意向锁是表层级的读写锁,分为意向共享锁(IS锁)、意向独占锁(IX锁),只要有s锁就上is锁,共享s锁,隔离x锁、ix锁
上锁时机:SQL执行语句时
解锁时机:有事务的话要到commit/rollback时解锁,无事务执行完SQL时解锁
MySQL缓存https://zhuanlan.zhihu.com/p/55947158#:~:text=MySQL%E8%83%BD%E5%A4%9F%E7%BC%93%E5%AD%98%E7%9A%84%E6%9C%80%E5%A4%A7%E6%9F%A5%E8%AF%A2%E7%BB%93%E6%9E%9C%EF%BC%8C%E6%9F%A5%E8%AF%A2%E7%BB%93%E6%9E%9C%E5%A4%A7%E4%BA%8E%E8%AF%A5%E5%80%BC%E6%97%B6%E4%B8%8D%E4%BC%9A%E8%A2%AB%E7%BC%93%E5%AD%98%E3%80%82,%E9%BB%98%E8%AE%A4%E5%80%BC%E6%98%AF1048576%20%281MB%29%E5%A6%82%E6%9E%9C%E6%9F%90%E4%B8%AA%E6%9F%A5%E8%AF%A2%E7%9A%84%E7%BB%93%E6%9E%9C%E8%B6%85%E5%87%BA%E4%BA%86%E8%BF%99%E4%B8%AA%E5%80%BC%EF%BC%8CQcache_not_cached%E7%9A%84%E5%80%BC%E4%BC%9A%E5%8A%A01%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%9F%90%E4%B8%AA%E6%93%8D%E4%BD%9C%E6%80%BB%E6%98%AF%E8%B6%85%E5%87%BA%EF%BC%8C%E5%8F%AF%E4%BB%A5%E8%80%83%E8%99%91%E5%9C%A8SQL%E4%B8%AD%E5%8A%A0%E4%B8%8ASQL_NO_CACHE%E6%9D%A5%E9%81%BF%E5%85%8D%E9%A2%9D%E5%A4%96%E7%9A%84%E6%B6%88%E8%80%97%E3%80%82
MySQL缓存的应用场景https://www.jianshu.com/p/63ce65e4dc23
总的来说MySQL缓存用的比较少,在大数据量、小并发、变动小的系统中非常适用,高并发时还是用分布式缓存吧
以innoDB为例,创建表时通过B树实现了主键的聚簇索引,因此查询时想要发挥B树的快查威力就必须用主键查询,但一般SQL查询都是以主键以外的字段查询,因此需要以常用的查询字段建立非聚簇索引
① 普通索引
- create table t_dept(
- no int not null primary key,
- name varchar(20) null,
- sex varchar(2) null,
- info varchar(20) null,
- index index_no(no) //基于索引的colume设置索引名,比如no,则命名格式为index_no
- )
② 唯一索引
- create table t_dept(
- no int not null primary key,
- name varchar(20) null,
- sex varchar(2) null,
- info varchar(20) null,
- unique index index_no(no)
- )
③ 全文索引
- create table t_dept(
- no int not null primary key,
- name varchar(20) null,
- sex varchar(2) null,
- info varchar(20) null,
- fulltext index index_no(no)
④ 多列索引
- create table t_dept(
- no int not null primary key,
- name varchar(20) null,
- sex varchar(2) null,
- info varchar(20) null,
- key index_no_name(no,name)
- )
① 普通索引
- create index index_name //index_name是索引名
- on t_dept(name); //t_dept是表名
② 唯一索引
- create unique index index_name
- on t_dept(name);
③ 全文索引
- create fulltext index index_name
- on t_dept(name);
④ 多列索引
- create index index_name_no
- on t_dept(name,no)
① 普通索引
- alter table t_dept
- add index index_name(name);
② 唯一索引
- alter table t_dept
- add unique index index_name(name);
③ 全文索引
- alter table t_dept
- add fulltext index_name(name);
④ 多列索引
- alter table t_dept
- add index index_name_no(name,no);
以下情况适合创建索引
在经常需要搜索的列上,可以加快搜索的速度。
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构。
在经常用于连接两张表的列上,这些列主要是一些外键,可以加快连接的速度。
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的。
在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
以下情况不适合创建索引
表记录太少。
经常增删改的表。
数据重复且分布平均的表字段,因此应该只为最经常查询和经常排序的数据列建立索引(如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果)
索引覆盖:
索引结构完全能覆盖要查询的表参数,例如为student表创建index(name,age)索引,当执行select name from student where name = ... and age > ... 语句时就实现了索引覆盖
索引下推:
以往版本的复合索引只能第一个参数走索引,然后回表。存储引擎就负责执行这些步骤,然后将回表结果返回server层,server层再根据后续查询条件做筛选
5.6版本出现了索引下推,是指复合索引不再只有第一个参数走索引结构,后续查询参数也可以在符合索引规则的情况下走索引结构,这样就降低了回表的情况,实现了索引结构最大化利用。
有时候实际索引并不执行预期索引,比如根据id查询后再根据createtime排序,虽然有两个单索引(index_id和index_createtime)且预期执行index_id,但是优化器实际会执行index_createtime,如果想执意走index_id那么就需要为sql设置强制索引
select * force index index_name(id) from table
索引失效是指索引不支持查询条件导致全表扫描,索引失效有全局失效和局部失效。调优sql 需要清楚如何避免索引失效
- create table `tb_seller` (
- `sellerid` varchar (100),
- `name` varchar (100),
- `nickname` varchar (50),
- `password` varchar (60),
- `status` varchar (1),
- `address` varchar (100),
- `createtime` datetime,
- primary key(`sellerid`)
- )engine=innodb default charset=utf8mb4;
-
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('alibaba','阿里巴巴','阿里小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('baidu','百度科技有限公司','百度小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('huawei','华为科技有限公司','华为小店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itcast','传智播客教育科技有限公司','传智播客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itheima','黑马程序员','黑马程序员','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('luoji','罗技科技有限公司','罗技小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗舰店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('qiandu','千度科技','千度小店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('xiaomi','小米科技','小米官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
- insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('yijia','宜家家居','宜家家居旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
-
- // 如果在创表sql中创建索引还要刷新,插入完成再创省事
- create index idx_seller_name_sta_addr on tb_seller(name,status,address);
-
- // 全值匹配,对索引中所有列都指定具体值,执行效率高,避免索引失效
- explain select * from tb_seller where name='小米科技' and status='1'
- and address='北京市'\G;
- 最左匹配原则就是指在联合索引中,如果你的 SQL 语句中用到了联合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个联合索引去进行匹配。例如某表现有索引(a,b,c),现在你有如下语句:
-
- 1、select * from t where a=1 and b=1 and c =1; #可以利用到定义的索引(a,b,c),用上a,b,c
-
- 2、select * from t where a=1 and b=1; #可以利用到定义的索引(a,b,c),用上a,b
-
- 3、select * from t where b=1 and a=1; #可以利用到定义的索引(a,b,c),用上a,b(mysql有查询优化器)
-
- 4、select * from t where a=1; #也可以利用到定义的索引(a,b,c),用上a
-
- 5、select * from t where b=1 and c=1; #不可以利用到定义的索引(a,b,c),失效
-
- 6、select * from t where a=1 and c=1; #可以利用到定义的索引(a,b,c),只用a索引,回表查c
- 1、索引列参与计算,不走索引
- SELECT `username` FROM `t_user` WHERE age=20;-- 会使用索引
- SELECT `username` FROM `t_user` WHERE age+10=30;-- 不会使用索引!!因为所有索引列参与了计算
- SELECT `username` FROM `t_user` WHERE age=30-10;-- 会使用索引
- 2、索引列使用函数,可能不走索引
- -- 不会使用索引,因为使用了函数运算,原理与上面相同
- SELECT username FROM t_user WHERE concat(username,'1') = 'admin1';
- -- 会使用索引
- SELECT username FROM t_user WHERE username = concat('admin','1');
- 1、select * from t where a=1 and b>1 and c =1;
- a,b可以用到(a,b,c),c索引失效,需要回表。
- 这个原因可以从联合索引的结构来解释:
- 在a、b走完索引后,c肯定是无序了,所以c就没法走索引
- 但是如果是建立(a,c,b)联合索引,则a,b,c都可以使用索引,因为优化器会改写为:
- select * from t where a=1 and c=1 and b >1;
-
- 2、select * from table where a = '1' and b > '2' and c='3' 这种字符查询也只有a、b走索引。
- 原理同上
-
- 以index (a,b,c)为例建立这样的索引相当于建立了索引a、ab、abc三个索引。一个索引顶三个索引当然是好事,毕竟每多一个索引,都会增加写操作的开销和磁盘空间的开销。
- create table `tb_seller` (
- `sellerid` varchar (100),
- `name` varchar (100),
- `nickname` varchar (50),
- `password` varchar (60),
- `status` varchar (1),
- `address` varchar (100),
- `createtime` datetime,
- primary key(`sellerid`)
- index idx_name_status_address('name','status','address')
- )engine=innodb default charset=utf8mb4;
-
- // 索引列使用 like 语句,可能不走索引,以idx_name_status_address索引为例
- SELECT name,status FROM USER WHERE username LIKE 'mysql测试%' --走索引
- SELECT name,status FROM USER WHERE username LIKE 'my%测试%' --走索引
- SELECT name,status FROM USER WHERE username LIKE '%mysql测试' --不走索引
- SELECT name,status FROM USER WHERE username = 'mysql测试' and status like 't%ue' and address = '...' --走索引,注意如果是5.5版本则address会索引失效
-
- // 想让左匹配有效可以使用覆盖索引,即type=index
- // 左匹配需要挨个遍历,因此要用索引扫描,当扫描到关键词节点就可以使用b+树按序查找
-
- // 想让左匹配有效第一个查询必须是like(不区分左中右匹配),因为type要依靠第一个字段建立
- SELECT name,status FROM USER WHERE username LIKE '%mysql测试' --走索引
- SELECT name,status FROM USER WHERE status LIKE '%ue' --走索引
-
- // 前面有等值查询,则左匹配失效
- SELECT name,status FROM USER WHERE username = 'mysql测试' and status like '%rue' --name走索引,status失效
-
- 【注:】一个查询语句的查询type是唯一的
- SELECT * FROM t_user WHERE username IS NULL -- 不走索引
- SELECT * FROM t_user WHERE username IS NOT NULL -- 不走索引
SELECT * FROM t_user WHERE username <> 'mysql测试'
- // stock_code数据类型为varchar
- SELECT * FROM `stock_data` WHERE `stock_code` IN ('600538') -- 走索引
- SELECT * FROM `stock_data` WHERE `stock_code` IN ('600538','688663','688280') -- 走索引
- SELECT * FROM `stock_data` WHERE `stock_code` IN (>=6) -- 不走索引
- SELECT * FROM `stock_data` WHERE `stock_code` IN (600538) -- 不走索引
-
- // between也一样,当查到的数据量占比越小越可能走索引
- -- stock_code字符串类型带索引
- SELECT * FROM `stock_data` WHERE stock_code = '600538' --走索引
- SELECT * FROM `stock_data` WHERE stock_code = 600538 --不走索引
尽量避免 OR 操作,只要有一个字段没有索引就全局失效!!!
- -- stock_code带索引,open不带索引
- SELECT * FROM `stock_data` WHERE `stock_code` = '600538' OR `open` = 6.62 -- 不走索引
- -- stock_code带索引,up_down_pre带索引
- SELECT * FROM `stock_data` WHERE `stock_code` = '600538' OR `up_down_pre` = 5.1 -- 走索引
总结
1、按最左匹配设置好顺序
2、设置好逻辑查询
3、保证查询条件索引有效 ,实际上就是要符合type的查询规则,比如type是index,如果有左匹配查询,就需要第一个字段设置like
好文推荐
索引调优https://zhuanlan.zhihu.com/p/61687047
id select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。
select_type 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、DERIVED (临时查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等
table 输出结果集的表
type 表示查询算法,性能由好到差的连接类型为( system —> const -----> eq_ref ------> ref -------> index_merge ------> range -----> index ------> all )
possible_keys 表示查询时,可能使用的索引
key 表示实际使用的索引
key_len 索引字段的长度,在保证精度的前提下越少越好
ref ref指某个表的索引字段“=”号后的值,如果是常量就const,变量就字段名,范围查询是null
rows 扫描行的数量,越少越好
extra 执行情况的说明和描述
介绍
1、type是查询类型,是一种算法,在表和索引中依据查询条件查询数据;
2、每个查询语句的type是唯一的;
3、type要求查询条件必须符合其规则,不满足就会出现索引失效。以index为例:
第一个查询是指定type的关键,要么是空要么是like;
语句中如果有等值查询index也可以基于b+tree查询
type类型
null MySQL不访问任何表,索引,直接返回结果
system 表只有一行记录(等于系统表),这是const类型的特例,一般不会出现
const const 用于primary key 或者 unique 索引的常量匹配,如主键查询:where id =1,就是单表精确查询,业务中不可能见到
eq_ref 类似ref,区别在于使用的是主键、唯一索引的关联查询,关联查询出的记录只有一条。
ref 和 eq_ref 不同的是使用的是非唯一索引的关联查询。这种索引匹配某个常量值或者关联值的所有行。
index_merge 查询用到了多个索引时执行数据合并
range 对索引进行范围匹配,where 之后出现 between , < , > , in 等操作。
index index 与 all 的区别是 index 利用了索引高效遍历,而 all 是遍历全表。
all 非主键字段遍历全表找到匹配的行,优化方法是为指定字段设置index
【注:】eq_ref、ref本质是拿引用来匹配自身记录,而const是值本身来匹配自身记录
1、using filesort 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行排序, 称为 “文件排序”,效率低。
2、using temporary 使用了内存中的临时数据,常见于对子查询、临时表的查询,效率一般
3、using index 实现了索引覆盖,避免访问表的数据行, 效率高。
4、using index condition 实现了索引下推,可能回表, 效率高
5、using where 表示在对索引全扫描时使用条件过滤,注意这里是全扫描b+树而非利用结构高效查询
6、impossible where 查询歧义,如 id=1 and id=2,不可能同时满足两个要求
【注:】
using where;using index:表示在索引情况下执行了条件过滤
using where;using index condition:表示在索引下推情况下执行了条件过滤
using where:表示在对索引全扫描的情况下执行了条件过滤,因此效果自然不如上述走索引结构高效,应避免该情况
5.6之前版本:没有index condition操作,因为此时的复合索引逻辑是首个条件走索引,后续条件回表,因此5.6之前版本只要是复合索引基本都是using where,5.6版本才可能发生using where;using index或者using where;using index conditon
好文推荐
分析时主要关注id、type、key、key_len、ref、rows、extra
小表做驱动表,即外部for查询,大表做被驱表,即b+树查询;
优点:降低了连接数,减少了io使用;充分利用了b+树查询优势
应用:in、exists
大表 where 大表.id in (select * from 小表);
小表 exists (大表 where 大表.id=小表.id)
好文推荐
小表驱动大表https://blog.csdn.net/fly_miqiqi/article/details/90348800
slow.log查询慢sql,explain分析,最后show profiles测试
- // 创建测试表
- CREATE TABLE `test_order_copy1` (
- `id` int(11) NOT NULL,
- `platform_sn` varchar(64) DEFAULT NULL,
- `third_sn` varchar(64) DEFAULT NULL,
- `type` tinyint(1) DEFAULT NULL,
- `create_time` datetime DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `idx_type` (`type`),
- KEY `idx_type&platform_sn&third_sn&create_time` (`type`,`platform_sn`,`third_sn`,`create_time`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
-
- // 插入200万笔
- CREATE DEFINER=`root`@`%` FUNCTION `batchAddOrderData`() RETURNS int(11)
- BEGIN
- #Routine body goes here...
- DECLARE num INT DEFAULT 2000000;
- DECLARE i int DEFAULT 1;
-
- while i <= num do
- insert into
- test_order
- VALUES(i,concat('SN',i),UUID(),1,now());
- set i=i+1;
- end while;
-
- return i;
- END
select * FROM test_order_copy2 where type = 1 limit 1500000,50
- 执行结果
- select * FROM test_order_copy2 where type = 1 limit 1500000,50
- > OK
- > 时间: 7.154s
未使用索引;limit耗时
分析列表
1、建立索引
2、SQL
2.1、连接查询
- select * from test_order_copy1 o right join (
- select id FROM test_order_copy1 where type = 1 limit 1500000,50
- ) t on o.id = t.id
- 执行结果
- select * from test_order_copy1 o right join (
- select id FROM test_order_copy1 where type = 1 limit 1500000,50
- ) t on o.id = t.id
- > OK
- > 时间: 1.889s
2.2、嵌套查询
- select * from test_order_copy1 where type = 1 and id >=(
- select id FROM test_order_copy1 where type = 1 limit 1500000,1
- ) limit 50
- 执行结果
- select * from test_order_copy1 where type = 1 and id >=(
- select id FROM test_order_copy1 where type = 1 limit 1500000,1
- ) limit 50
- > OK
- > 时间: 2.261s
2.3、id限定
利用id关联分页,巧妙借id索引分页,但必须保证id是递增(只要是索引都可使用,以求稳定性最好是唯一索引)
- // 分析发现这条SQL都不用走type索引,完全走primary
- select * from test_order_copy2 where type = 1 and id >=1500000 limit 50;
- 执行结果
- select * from test_order_copy2 where type = 1 and id >=1500000 limit 50
- > OK
- > 时间: 0.202s
2.4、索引排序
是对id限定的补偿,如果不能保证索引是递增,就需要order by做一下重排,注意必须要给重排字段设置索引,order by可以借着索引快排,即using index,否则就是using filesort
- // 如果设置了复合索引或者覆盖索引,则该sql的优化逻辑为:
- // 利用索引叶子节点是id这点,通过索引做type、id查询;
- // 再根据id回表查询,然后再返回索引继续往下查询;
- // 这种查询会导致数据id无序,因此要排序,即order by id,然后limit 50
- select * from test_order_copy2 where type = 1 and id >=1500000
- ORDER BY id limit 50;
- 执行结果
- select * from test_order_copy2 where type = 1 and id >=1500000 ORDER BY id limit 50;
- > OK
- > 时间: 0.001s
3、总结
3.1、id限定最高效,较之原来提升了30倍;做了排序补偿的索引排序也不错
3.2、大数据量使用滑动分页,因为count查询是实打实的条件查询,速度干不过id限定,具体多大要看机器性能,比如三五百万
存储过程https://blog.csdn.net/qq_36777191/article/details/104171953
触发器https://blog.csdn.net/qq_36777191/article/details/104190013
即redolog、binlog、undolog
redolog
1、是innoDB的日志,物理存储;
2、任务是宕机重启后保证数据一致性;
3、记录事务,当事务结束时并不会立即刷新到磁盘,而是通过redo buffer记录到redolog,然后再异步刷进ibd;
4、redolog写入ibd的那部分数据就相当于过期了,redolog会用新数据覆盖那部分数据,也因此redolog日志是可重复使用的持久化文件
binlog
1、是MySQL的日志,逻辑存储;
2、任务是日志分析,主从同步,数据恢复;
3、记录数据变动,记录方式有row、statement和miexd
row:比如某行某个字段变为了***,优点是记录细致,缺点资源大
statement:就是SQL语句,优点省资源,缺点主从模式下如果有函数容易不一致,比如now(),uuid()
mixed:自动选择用row还是statement
redolog和binlog的区别
1、redo log是在InnoDB存储引擎层产生,而binlog是MySQL数据库的上层产生的,并且二进制日志不仅仅针对INNODB存储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生二进制日志。
2、两种日志记录的内容形式不同。MySQL的binlog是逻辑日志,其记录是对应的SQL语句。而innodb存储引擎层面的重做日志是物理日志。
3、两种日志与记录写入磁盘的时间点不同,二进制日志只在事务提交完成后进行一次写入。而innodb存储引擎的重做日志在事务进行中不断地被写入,并且日志不是随事务提交的顺序进行写入的。
4、binlog在写满或者重启之后,会新产生binlog文件,而redo log是循环使用。
5、binlog作为数据分析,数据恢复,主从同步使用,而redo log会参与到ibd的数据持久化流程,目的是异常宕机或者介质故障时恢复数据。
binlog在主从复制中的应用
可以看出redolog参与了ibd的数据持久化,redlolog的地位非常重要
undolog
1、实现事务的原子性:undo log可以用于实现事务的原子性, 如果事务处理过程出现一些特殊情况,比如sql错误等等情况,就要执行回滚(rollback)操作,mysql就可以利用undo log将数据恢复到事务开始之前
2、实现多版本并发控制(MVCC):undo log 在 MySQL InnoDB 存储引擎中用来实现多版本并发控制,事务没提交之前,undo日志可以作为高并发情况下,其它并发事务进行快照读
好文推荐
binloghttps://blog.csdn.net/Allenzyg/article/details/106446992 redolog的作用和原理https://blog.csdn.net/qq_42773863/article/details/120988441?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165324292716782425142146%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165324292716782425142146&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-120988441-null-null.142%5Ev10%5Econtrol,157%5Ev4%5Econtrol&utm_term=redolog+ibd&spm=1018.2226.3001.4187redolog和binlog的区别https://blog.csdn.net/wanbin6470398/article/details/81941586
undologhttps://blog.csdn.net/u014427391/article/details/109022361
三大日志https://javaguide.cn/database/mysql/mysql-logs.html#%E5%89%8D%E8%A8%80 集群
集群就是n主m从的布局,总的来说主从复制的逻辑在于主机写,从机读。从单体架构往集群架构发展的大概逻辑:
relay log
我们翻译成中文,一般叫做中继日志,一般情况下它在MySQL主从同步读写分离集群的从节点才开启。主节点一般不需要这个日志。
master主节点的binlog传到slave从节点后,被写道relay log里,从节点的slave sql线程从relaylog里读取日志然后应用到slave从节点本地。从服务器I/O线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后SQL线程会读取relay-log日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。
它的作用可以参考如下图,从图片中可以看出,它是一个中介临时的日志文件,用于存储从master节点同步过来的binlog日志内容,它里面的内容和master节点的binlog日志里面的内容是一致的。然后slave从节点从这个relaylog日志文件中读取数据应用到数据库中,来实现数据的主从复制。
好文推荐
主从复制https://blog.csdn.net/qq_21153619/article/details/81529880
主从复制https://blog.csdn.net/weixin_30539317/article/details/113281081?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%20MySQL%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-113281081.142%5Ev10%5Econtrol,157%5Ev4%5Econtrol&spm=1018.2226.3001.4187relaylog和binlog实现MySQL的主从同步原理https://blog.csdn.net/javaanddonet/article/details/112596148
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。