MySQL(上)

1 记死的东西

 mysql -u root -p

 牛马规则:联合索引中,出现范围查询(<, >),范围查询右侧的列索引失效。可以用>=或者<=来规避索引失效问题。所以,在业务允许的情况下,尽可能的使用类似于 >= 或 或 < 。

2 占脑子的概念、屁用没有

数据定义数据操作@#%@……#*@¥…

 DDL数据定义语言(牛逼,作用在高层的语句)

干数据库

查询所有数据库

show databases ;

查询当前数据库

show database();

创建数据库

create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序规则 ] ;
# create database itheima default charset utf8mb4;

        在同一个数据库服务器中,不能创建两个名称相同的数据库,否则将会报错。可以通过if not exists 参数来解决这个问题,数据库不存在, 则创建该数据库,如果存在,则不 创建。

删除数据库

drop database [ if exists ] 数据库名 ;

切换数据

use 数据库名 ;

干表

查询当前数据库所有表

# use sys;
# show tables;

查看指定表结构

通过这条指令,我们可以查看到指定表的字段,字段的类型、是否可以为NULL,是否存在默认值等信息。

desc 表名 ;

查询指定表的建表语句

show create table 表名 ;

        通过这条指令,主要是用来查看建表语句的,而有部分参数我们在创建表的时候,并未指定也会查询 到,因为这部分是数据库的默认值,如:存储引擎、字符集等。

创建表结构
CREATE TABLE 表名(
    字段1 字段1类型 [ COMMENT 字段1注释 ],
    字段2 字段2类型 [COMMENT 字段2注释 ],
    字段3 字段3类型 [COMMENT 字段3注释 ],
    ......
    字段n 字段n类型 [COMMENT 字段n注释 ]
) [ COMMENT 表注释 ] ;

 [ ] 表示可选参数

改表

添加字段

ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];

改数据类型

ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度)

修改字段名和字段类型

ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];

删除某个属性 

ALTER TABLE 表名 DROP 字段名;

修改表名

ALTER TABLE 表名 RENAME TO 新表名;
删除表 
DROP TABLE [ IF EXISTS ] 表名

 删除指定表, 并重新创建表

TRUNCATE TABLE 表名;

3 DML数据操作语言

增  删  改

给指定字段添加数据

INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);

insert into employee(id,workno,name,gender,age,idcard,entrydate)
values(1,'1','Itcast','男',10,'123456789012345678','2000-01-01');

给all字段添加数据, 省略列名

INSERT INTO 表名 VALUES (值1, 值2, ...);

批量添加数据

INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值
1, 值2, ...) ;
INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...) ;
 修改数据UPDATE
UPDATE 表名 SET 字段名1 = 值1 , 字段名2 = 值2 , .... [ WHERE 条件 ] ;
# 将所有的员工入职日期修改为 2008-01-01
update employee set entrydate = '2008-01-01';
# 修改id为1的数据, 将name修改为小昭, gender修改为 女
update employee set name = '小昭' , gender = '女' where id = 1;
 删除数据
DELETE FROM 表名 [ WHERE 条件 ] ;
#  删除gender为女的员工
delete from employee where gender = '女';
# 删除所有员工!!!
delete from employee;

DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数 据。

DELETE 语句不能删除某一个字段的值(可以使用UPDATE,将该字段值置为NULL即 可)。 

4 DQL数据查询语言  SELECT

4.1 基本结构

SELECT 字段列表 FROM 表名列表
    WHERE    
        条件列表
    GROUP BY
        分组字段列表
    HAVING
        分组后条件列表
    ORDER BY
        排序字段列表
    LIMIT
        分页参数

 起别名

SELECT 字段1 [ AS 别名1 ] , 字段2 [ AS 别名2 ] ... FROM 表名;
SELECT 字段1 [ 别名1 ] , 字段2 [ 别名2 ] ... FROM 表名;

去重 

SELECT DISTINCT 字段列表 FROM 表名;

⭐4.2 条件查询 > <运算符

比较运算符功能
>大于
>=大于等于
<小于
<=小于等于
=等于
<> 或 !=不等于
BETWEEN … AND …在某个范围内(含最小、最大值)
IN(…)在in之后的列表中的值,多选一
LIKE 占位符模糊匹配(_匹配单个字符,%匹配任意个字符)
IS NULL是NULL
逻辑运算符功能
AND 或 &&并且(多个条件同时成立)
OR 或 ||或者(多个条件任意一个成立)
NOT 或 !非,不是
# 年龄等于 88 的员工
select * from emp where age = 88;
# 询年龄小于= 20 的员工信息
select * from emp where age <= 20;
# 没有身份证号的员工信息
select * from emp where idcard is null;
# 年龄在15岁(包含) 到 20岁(包含)之间的员工信息
select * from emp where age >= 15 && age <= 20;
select * from emp where age >= 15 and age <= 20;
select * from emp where age between 15 and 20;
# 性别为 女 且年龄小于 25岁的员工信息
select * from emp where gender = '女' and age < 25;
# 年龄等于18 或 20 或 40 的员工信息
select * from emp where age = 18 or age = 20 or age =40;
select * from emp where age in(18,20,40);


# 姓名为两个字的员工信息 _ %
select * from emp where name like '__';
#  查询身份证号最后一位是X的员工信息
select * from emp where idcard like '%X';
select * from emp where idcard like '_________________X';

4.3 聚合函数 count,avg,min

将一列数据作为一个整体,进行纵向计算

me: 查完一个列整体后做一个计算

函数功能
count统计数量
max最大值
min最小值
avg平均值
sum求和
SELECT 聚合函数(字段列表) FROM 表名 ;
#  NULL值是不参与所有聚合函数运算的。
# 统计该企业员工数量
select count(*) from emp; -- 统计的是总记录数
select count(idcard) from emp; -- 统计的是idcard字段不为null的记录数
# 西安地区员工的年龄之和
select sum(age) from emp where workaddress = '西安';
INSERT INTO 表名 VALUES (值1, 值2, ...);

4.4 分组查询  GROUP BY

SELECT 字段列表 FROM 表名 
    [ WHERE 条件 ] 
    GROUP BY 分组字段名 
    [ HAVING 分组后过滤条件 ];
where与having区别

执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;

                        而having是分组 之后对结果进行过滤。

判断条件不同:where不能对聚合函数进行判断,而having可以。

  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
  • 执行顺序: where > 聚合函数 > having 。
  • 支持多字段分组, 具体语法为 : group by columnA,columnB
# 根据性别分组 , 统计男性员工 和 女性员工的数量
select gender, count(*) from emp group by gender ;
#  根据性别分组 , 统计男性员工 和 女性员工的平均年龄
select gender, avg(age) from emp group by gender ;

# 查询年龄小于45的员工 , 并根据工作地址分组 , 获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from emp 
    where age < 45 
    group by workaddress 
    having address_count >= 3;

#  统计各个工作地址上班的男性及女性员工的数量
select workaddress, gender, count(*) '数量' from emp group by gender , workaddress;

4.5  排序 ORDER BY

SELECT 字段列表 FROM 表名 
    ORDER BY 字段1 排序方式1 , 字段2 排序方式2 ;
ASC : 升序(默认值)
DESC: 降序

4.6 分页查询  LIMIT

SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数 ;

• 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数。???看案例

• 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。

• 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。

# 查询第1页员工数据, 每页展示10条记录
select * from emp limit 0,10;
select * from emp limit 10;

#  查询第4页员工数据, 每页展示10条记录 --------> (页码-1)*页展示记录数
0-9, 10-19, 20-29
select * from emp limit 30,10;

⭐4.7 执行顺序  FROM -> WHERE -> GROUP BY -> SELECT -> ORDER BY -> LIMIT

 DCL 数据控制语言 、管理用户访问权限

管用户

查询用户

select * from mysql.user;

创建用户

CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
DROP USER '用户名'@'主机名' 

修改用户密码 

ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码' ;

 主机名可以使用 % 通配

 # 创建用户itcast, 只能够在当前主机localhost访问, 密码123456;
create user 'itcast'@'localhost' identified by '123456';
# 创建用户heima, 可以在任意主机访问该数据库, 密码123456;
create user 'heima'@'%' identified by '123456';
# 修改用户heima的访问密码为1234;
alter user 'heima'@'%' identified with mysql_native_password by '1234';

给权限

权限说明
ALL, ALL PRIVILEGES所有权限
SELECT查询数据
INSERT插入数据
UPDATE修改数据
DELETE删除数据
ALTER修改表
DROP删除数据库/表/视图
CREATE创建数据库/表

查权限

SHOW GRANTS FOR '用户名'@'主机名' ;

授予权限 

GRANT  权限列表 ON 数据库名.表名 TO   '用户名'@'主机名';
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
SHOW GRANTS FOR '用户名'@'主机名' ;

数据类型

整型

类型名称取值范围大小
TINYINT-128〜1271个字节
SMALLINT-32768〜327672个宇节
MEDIUMINT-8388608〜83886073个字节
INT (INTEGHR)-2147483648〜21474836474个字节
BIGINT-9223372036854775808〜92233720368547758078个字节

无符号在数据类型后加 unsigned 关键字。

如:
1). 年龄字段 -- 不会出现负数, 而且人的年龄不会太大
age tinyint unsigned
2). 分数 -- 总分100分, 最多出现一位小数
score double(4,1)

浮点型

类型名称说明存储需求
FLOAT单精度浮点数4 个字节
DOUBLE双精度浮点数8 个字节
DECIMAL (M, D),DEC压缩的“严格”定点数M+2 个字节

日期和时间

类型名称日期格式日期范围存储需求
YEARYYYY1901 ~ 21551 个字节
TIMEHH:MM:SS-838:59:59 ~ 838:59:593 个字节
DATEYYYY-MM-DD1000-01-01 ~ 9999-12-33 个字节
DATETIMEYYYY-MM-DD HH:MM:SS1000-01-01 00:00:00 ~ 9999-12-31 23:59:598 个字节
TIMESTAMPYYYY-MM-DD HH:MM:SS1980-01-01 00:00:01 UTC ~ 2040-01-19 03:14:07 UTC4 个字节
1). 生日字段 birthday
birthday date
2). 创建时间 createtime
createtime datetime

字符串

类型名称说明存储需求
CHAR(M)固定长度非二进制字符串M 字节,1<=M<=255
VARCHAR(M)变长非二进制字符串L+1字节,在此,L< = M和 1<=M<=255
TINYTEXT非常小的非二进制字符串L+1字节,在此,L<2^8
TEXT小的非二进制字符串L+2字节,在此,L<2^16
MEDIUMTEXT中等大小的非二进制字符串L+3字节,在此,L<2^24
LONGTEXT大的非二进制字符串L+4字节,在此,L<2^32
ENUM枚举类型,只能有一个枚举字符串值1或2个字节,取决于枚举值的数目 (最大值为65535)
SET一个设置,字符串对象可以有零个或 多个SET成员1、2、3、4或8个字节,取决于集合 成员的数量(最多64个成员)
1). 用户名 username ------> 长度不定, 最长不会超过50
username varchar(50)
2). 性别 gender ---------> 存储值, 不是男,就是女
gender char(1)
3). 手机号 phone --------> 固定长度为11
phone char(11)

二进制类型

类型名称说明存储需求
BIT(M)位字段类型大约 (M+7)/8 字节
BINARY(M)固定长度二进制字符串M 字节
VARBINARY (M)可变长度二进制字符串M+1 字节
TINYBLOB (M)非常小的BLOBL+1 字节,在此,L<2^8
BLOB (M)小 BLOBL+2 字节,在此,L<2^16
MEDIUMBLOB (M)中等大小的BLOBL+3 字节,在此,L<2^24
LONGBLOB (M)非常大的BLOBL+4 字节,在此,L<2^32

案例

设计一张员工信息表,要求如下:

1. 编号(纯数字)

2. 员工工号 (字符串类型,长度不超过10位)

3. 员工姓名(字符串类型,长度不超过10位)

4. 性别(男/女,存储一个汉字)

5. 年龄(正常人年龄,不可能存储负数)

6. 身份证号(二代身份证号均为18位,身份证中有X这样的字符)

7. 入职时间(取值年月日即可)

create table emp(
    id int comment '编号',
    workno varchar(10) comment '工号',
    name varchar(10) comment '姓名',
    gender char(1) comment '性别',
    age tinyint unsigned comment '年龄',
    idcard char(18) comment '身份证号',
    entrydate date comment '入职时间'
) comment '员工表';

函数

字符串函数

函数功能
CONCAT(s1, s2, …, sn)字符串拼接,将s1, s2, …, sn拼接成一个字符串
LOWER(str)将字符串全部转为小写
UPPER(str)将字符串全部转为大写
LPAD(str, n, pad)左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
RPAD(str, n, pad)右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
TRIM(str)去掉字符串头部和尾部的空格
SUBSTRING(str, start, len)返回从字符串str从start位置起的len个长度的字符串
REPLACE(column, source, replace)替换字符串

由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员 工的工号应该为00001。

update emp set workno = lpad(workno, 5, '0')

数值函数

函数功能
CEIL(x)向上取整
FLOOR(x)向下取整
MOD(x, y)返回x/y的模
RAND()返回0~1内的随机数
ROUND(x, y)求参数x的四舍五入值,保留y位小数

6位随机数,先生成小数0.273748329743,乘完保留0位小数,万一前边不够补0 

select lpad(round(rand()*1000000 , 0), 6, '0')

⭐日期函数

函数功能
CURDATE()返回当前日期
CURTIME()返回当前时间
NOW()返回当前日期和时间
YEAR(date)获取指定date的年份
MONTH(date)获取指定date的月份
DAY(date)获取指定date的日期
DATE_ADD(date, INTERVAL expr type)返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1, date2)返回起始时间date1和结束时间date2之间的天数

查询所有员工的入职天数,并根据入职天数倒序排序。

select name, datediff(curdate(), entrydate) as 'entrydays' 
    from emp 
    order by entrydays desc

流程函数

函数功能
IF(value, t, f)如果value为true,则返回t,否则返回f
IFNULL(value1, value2)如果value1不为空,返回value1,否则返回value2
CASE WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END如果val1为true,返回res1,… 否则返回default默认值
CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END如果expr的值等于val1,返回res1,… 否则返回default默认值

查询emp表的员工姓名和工作地址 (北京/上海 ----> 一线城市 , 其他 ----> 二线城市)

select
name,
( case workaddress 
    when '北京' then '一线城市' 
    when '上海' then '一线城市' 
    else '二线城市' 
    end ) as '工作地址成分'
from emp;

约束

约束是作用于表属性上的规则,用于限制存储在表中的数据。

约束关键字描述
非空约束NOT NULL限制该字段的数据不能为null
唯一约束UNIQUE保证该字段的所有数据都是唯一、不重复的
主键约束PRIMARY KEY主键是一行数据的唯一标识,要求非空且唯一
默认约束DEFAULT保存数据时,如果未指定该字段的值,则采用默认值
检查约束(ver 8.0.1+)CHECK保证字段值满足某一个条件
外键约束FOREIGN KEY用来让两张图的数据之间建立连接,保证数据的一致性和完整性
自动增长AUTO_INCREMENT
CREATE TABLE tb_user(
    id int AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
    name varchar(10) NOT NULL UNIQUE COMMENT '姓名' ,
    age int check (age > 0 && age <= 120) COMMENT '年龄' ,
    status char(1) default '1' COMMENT '状态',
    gender char(1) COMMENT '性别'
);

啥是外键约束

生怕你学会的定义:用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。

         在员工的信息中存储的是部门的ID dept_id,而这个部门的ID是关联的 部门表dept的主键id,那emp表的dept_id就是外键,关联的是另一张表的主键。

so you will be like them abandon me?

        删掉部门表的1后,部门表不存在id为1的部门,而在emp表中还有很多的员 工,关联的为id为1的部门,此时就出现了数据的不完整性。 而要想解决这个问题就得通过数据库的 外键约束

添加外键语法

CREATE TABLE 表名(
    字段名 数据类型,
    ...
    [CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名)
);


ALTER TABLE 表名 
    ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) ;

为emp表的dept_id字段添加外键约束,关联dept表的主键id。

alter table emp 
    add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);

不能删除或更新父表记录,因为存在外键约束。 

删除外键 

ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

删除/更新行为

行为说明
NO ACTION当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与RESTRICT一致)
RESTRICT当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与NO ACTION一致)
CASCADE当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在子表中的记录
SET NULL当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(要求该外键允许为null)
SET DEFAULT父表有变更时,子表将外键设为一个默认值(Innodb不支持)
ALTER TABLE 表名 
    ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCEE 主表名 (主表字段名) 
    ON UPDATE CASCADE 
    ON DELETE CASCADE;

多表查询-预防针章(瞎贾巴乱连)

1VS多

关系: 一个部门对应多个员工,一个员工对应一个部门

实现: 在多的一方建立外键,指向一的一方的主键

多VS多

一个学生可以选修多门课程,一门课程也可以供多个学生选择

实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键 

1V1

一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另 一张表中,以提升操作效率

实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)

就瞎胡设

 多表查询

笛卡尔积

select * from emp , dept;

 加条件

select * from emp , dept where emp.dept_id = dept.id;

 内外连接,左外右外自连接

内连接-显式/隐士

 隐式内连接

SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 ... ;

显式内连接

SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;

外连接-左右,哪个外就保留哪个全部         

左外

SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;

右外

SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;

自连接

SELECT 字段列表 FROM 表AAAA 别名A JOIN 表AAAA 别名B ON 条件 ... ;

在自连接查询中,必须要为表起别名,要不然我们不清楚所指定的条件、返回的字段,到底 是哪一张表的字段。 

联合查询-就是拼接

花里胡哨,就是把多次查询的结果合并起来,形成一个新的查询结果集。

SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;

对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。

union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。

⭐子查询  SELECT套SELECT

SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 )

标量子查询(子查询结果为单个值)

-- 查询销售部所有员工
select id from dept where name = '销售部';
-- 根据销售部部门ID,查询员工信息
select * from employee where dept = 4;
-- 合并(子查询)
select * from employee where dept = (select id from dept where name = '销售部');
-- 查询xxx入职之后的员工信息
select * from employee where entrydate > (select entrydate from employee where name = 'xxx');

列子查询(子查询结果为一列)

操作符描述
IN在指定的集合范围内,多选一
NOT IN不在指定的集合范围内
ANY子查询返回列表中,有任意一个满足即可
SOME与ANY等同,使用SOME的地方都可以使用ANY
ALL子查询返回列表的所有值都必须满足
-- 查询销售部和市场部的所有员工信息
select * from employee where dept in (select id from dept where name = '销售部' or name = '市场部');
-- 查询比财务部所有人工资都高的员工信息
select * from employee where salary > all(select salary from employee where dept = (select id from dept where name = '财务部'));
-- 查询比研发部任意一人工资高的员工信息
select * from employee where salary > any (select salary from employee where dept = (select id from dept where name = '研发部'));

行子查询(子查询结果为一行)

-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);
select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');

表子查询(子查询结果为多行多列)

-- 查询与xxx1,xxx2的职位和薪资相同的员工
select * from employee where (job, salary) in (select job, salary from employee where name = 'xxx1' or name = 'xxx2');
-- 查询入职日期是2006-01-01之后的员工,及其部门信息
select e.*, d.* from (select * from employee where entrydate > '2006-01-01') as e left join dept as d on e.dept = d.id;

事务

        事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

控制事务1

-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';
-- 查看事务提交方式
SELECT @@AUTOCOMMIT;
-- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效
SET @@AUTOCOMMIT = 0;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 设置手动提交后上面代码改为:
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;

控制事务2

start transaction;
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;

四大特性ACID(死概念)

  • 原子性(Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败
  • 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

并发事务

问题描述
脏读一个事务读到另一个事务还没提交的数据
不可重复读一个事务先后读取同一条记录,但两次读取的数据不同
幻读一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在

脏读:还没更新呢就被别人读走了旧数据

 不可重复读:说的花里胡哨,就是一个事务应该可以重复读一个数,但是有了问题,这个数据被改了,一个事务读一个数两次,居然不一样

幻读:瞎起名,就是没有读到,想插又插不进去

隔离级别

隔离级别脏读不可重复读幻读
Read uncommitted出现出现出现
Read committed×出现出现
Repeatable Read(默认)××出现
Serializable×××

先给人一张表能知道什么啊?

查看事务隔离级别:

​SELECT @@TRANSACTION_ISOLATION;

设置事务隔离级别:

SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION
     LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };

SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效

当SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

 

不可重复读:看起来很正常,但是问题在于,左边的事务没有提交呢,一个事务内没有进行修改操作,读出来的数据居然就不一样了,相当于存在被入侵嫌疑 


       =======进阶======


存储引擎

         存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表而不是基于库的,所以存储引擎也可以被称为表引擎。
        默认存储引擎是InnoDB。

-- 查询建表语句
show create table account;
-- 建表时指定存储引擎
CREATE TABLE 表名(
    ...
) ENGINE=INNODB;
-- 查看当前数据库支持的存储引擎
show engines;

 存储引擎

InnoDB

InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB 是默认的 MySQL 引擎。

特点:

  • DML 操作遵循 ACID 模型,支持事务
  • 行级锁,提高并发访问性能
  • 支持外键约束,保证数据的完整性和正确性

文件:

  • xxx.ibd: xxx代表表名,InnoDB 引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。

        可以看到里面有很多的ibd文件,每一个ibd文件就对应一张表,比如:我们有一张表 account,就 有这样的一个account.ibd文件,而在这个ibd文件中不仅存放表结构、数据,还会存放该表对应的 索引信息。 而该文件是基于二进制存储的,不能直接基于记事本打开,我们可以使用mysql提供的一 个指令 ibd2sdi ,通过该指令就可以从ibd文件中提取sdi信息,而sdi数据字典信息中就包含该表的表结构。 

参数:innodb_file_per_table,决定多张表共享一个表空间还是每张表对应一个表空间

知识点:

查看 Mysql 变量:
show variables like 'innodb_file_per_table';

从idb文件提取表结构数据:(在cmd运行)
ibd2sdi xxx.ibd

InnoDB 逻辑存储结构:

InnoDB逻辑存储结构

Memory

Memory 引擎的表数据是存储在内存中的,受硬件问题、断电问题的影响,只能将这些表作为临时表或缓存使用。

特点:

  • 不支持事务,不支持外键
  • 支持表锁,不支持行锁
  • 访问速度快

文件:

  • xxx.sdi:存储表结构信息
  • xxx.MYD: 存储数据
  • xxx.MYI: 存储索引

特点InnoDBMyISAMMemory
存储限制64TB
事务安全支持--
锁机制行锁表锁表锁
B+tree索引支持支持支持
Hash索引--支持
全文索引支持(5.6版本之后)支持-
空间使用N/A
内存使用中等
批量插入速度
支持外键支持--

 选择困难症

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

  • InnoDB: 如果应用对事物的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,则 InnoDB 是比较合适的选择
  • MyISAM: 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不高,那这个存储引擎是非常合适的。
  • Memory: 将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。Memory 的缺陷是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性

电商中的足迹和评论适合使用 MyISAM 引擎,缓存适合使用 Memory 引擎。

索引

啥啊

        索引是帮助 MySQL 高效获取数据数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查询算法,这种数据结构就是索引。

优点:废话废话废话废话废话废话废话废话废话

  • 提高数据检索效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

缺点:

  • 索引列也是要占用空间的
  • 索引大大提高了查询效率,但降低了更新的速度,比如 INSERT、UPDATE、DELETE

从上到下查到底VS建立一个二叉树索引(空间换时间)

 索引种类

索引结构描述
B+Tree最常见的索引类型,大部分引擎都支持B+树索引
Hash底层数据结构是用哈希表实现,只有精确匹配索引列的查询才有效,不支持范围查询
R-Tree(空间索引)空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full-Text(全文索引)是一种通过建立倒排索引,快速匹配文档的方式,类似于 Lucene, Solr, ES
索引InnoDBMyISAMMemory
B+Tree索引支持支持支持
Hash索引不支持不支持支持
R-Tree索引不支持支持不支持
Full-text5.6版本后支持支持不支持

B+树

二叉树坏就坏在二太少了!

B树生成可视化:B-Tree Visualization

B+树可视化:B+ Tree Visualization

更多可视化:Data Structure Visualization

B

 B+

 MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,提高区间访问的性能

就是循环双向链表!!!

Hash

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

特点:

  • Hash索引只能用于对等比较(=、in),不支持范围查询(betwwn、>、<、…)
  • 无法利用索引完成排序操作
  • 查询效率高,通常只需要一次检索就可以了,效率通常要高于 B+Tree 索引

索引作用在哪

分类含义特点关键字
主键索引针对于表中主键创建的索引默认自动创建,只能有一个PRIMARY
唯一索引避免同一个表中某数据列中的值重复可以有多个UNIQUE
常规索引快速定位特定数据可以有多个
全文索引全文索引查找的是文本中的关键词,而不是比较索引中的值可以有多个FULLTEXT

 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种: 

分类含义特点
聚集索引(Clustered Index)将数据存储与索引放一块,索引结构的叶子节点保存了行数据必须有,而且只有一个
二级索引(Secondary Index)将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键可以存在多个

聚集索引:就是又索引又存实际信息 

 二级索引:每个列都可以有一个,比如字母,是按照首字符ABCDE排序的

 SELECT * FROM USER WHERE NAME = 'CXK',查询时先查二级索引C,找到行号,转到聚集索引获取实际数据本体(花里胡哨专业名词:回表查询

聚集索引选取规则:

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
  • 如果表没有主键或没有合适的唯一索引,则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引

语法

创建、查看、delete

CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...);
SHOW INDEX FROM table_name;
DROP INDEX index_name ON table_name;

案例

-- name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
create index idx_user_name on tb_user(name);
-- phone手机号字段的值非空,且唯一,为该字段创建唯一索引
create unique index idx_user_phone on tb_user (phone);
-- 为profession, age, status创建联合索引
create index idx_user_pro_age_stat on tb_user(profession, age, status);
-- 为email建立合适的索引来提升查询效率
create index idx_user_email on tb_user(email);
-- 删除索引
drop index idx_user_email on tb_user;

使用原则

最左前缀法则

如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。
如果跳跃某一列,索引将部分失效(后面的字段索引失效)。

牛马规则:联合索引中,出现范围查询(<, >),范围查询右侧的列索引失效。可以用>=或者<=来规避索引失效问题。所以,在业务允许的情况下,尽可能的使用类似于 >= 或 或 < 。

Me:ABC三列建了个联合索引,条件查询查ABC、AB、A都可以走索引,但是查BC,B,C,索引没用了

索引失效情况

  1. 在索引列上进行运算操作,索引将失效。如:explain select * from tb_user where substring(phone, 10, 2) = '15';
  2. 字符串类型字段使用时,不加引号,索引将失效。如:explain select * from tb_user where phone = 17799990015;,此处phone的值没有加引号
  3. 模糊查询中,如果仅仅是尾部模糊匹配,索引不会是失效;如果是头部模糊匹配,索引失效。如:explain select * from tb_user where profession like '%工程';,前后都有 % 也会失效。
  4. 用 or 分割开的条件,如果 or 其中一个条件的列没有索引,那么涉及的索引都不会被用到。
  5. 如果 MySQL 评估使用索引比全表更慢,则不使用索引。

SQL 提示

是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

例如,使用索引:
explain select * from tb_user use index(idx_user_pro) where profession="软件工程";
不使用哪个索引:
explain select * from tb_user ignore index(idx_user_pro) where profession="软件工程";
必须使用哪个索引:
explain select * from tb_user force index(idx_user_pro) where profession="软件工程";

use 是建议,实际使用哪个索引 MySQL 还会自己权衡运行速度去更改,force就是无论如何都强制使用该索引。

覆盖索引

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能找到),减少 select *。

explain 中 extra 字段含义:
using index condition:查找使用了索引,但是需要回表查询数据
using where; using index;:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询

        如果在聚集索引中直接能找到对应的行,则直接返回行数据,只需要一次查询,哪怕是select *;

        如果在辅助索引中找聚集索引,如select id, name from xxx where name='xxx';,也只需要通过辅助索引(name)查找到对应的id,返回name和name索引对应的id即可,只需要一次查询;

        如果是通过辅助索引查找其他字段,则需要回表查询,如select id, name, gender from xxx where name='xxx';

所以尽量不要用select *,容易出现回表查询,降低效率,除非有联合索引包含了所有字段

面试题:一张表,有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:
select id, username, password from tb_user where username='itcast';

解:给username和password字段建立联合索引,则不需要回表查询,直接覆盖索引

前缀索引

        当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让 索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

create index idx_email_5 on tb_user(email(5));

        看看截取几个占比最高,选择性1证明没重复,这样最好,截取的也最多,截取的少可能重复,查询效率降了一点,自己平衡 

select count(distinct substring(email, 1, 5)) / count(*) from tb_user;

设计原则

  1. 针对于数据量较大,且查询比较频繁的表建立索引
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段长度较长,可以针对于字段的特点,建立前缀索引
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率
  7. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询

 性能分析

查看各种语句执行多少次

SHOW GLOBAL|SESSION STATUS LIKE 'Com_______';

不知道为啥前边会跟一个Com_

慢查询日志

        慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
        MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

# 开启慢查询日志开关
slow_query_log=1
# 设置慢查询日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
更改后记得重启MySQL服务,日志文件位置:/var/lib/mysql/localhost-slow.log

查看慢查询日志开关状态:
show variables like 'slow_query_log';

 profile

慢查询是看特别慢的,这个是看有点慢的,卡阈值的那些

#show profile 能在做SQL优化时帮我们了解时间都耗费在哪里。通过 have_profiling 参数,能看到当前 MySQL 是否支持 profile 操作:
SELECT @@have_profiling;
#profiling 默认关闭,可以通过set语句在session/global级别开启 profiling:
SET profiling = 1;

#查看所有语句的耗时:
show profiles;
#查看指定query_id的SQL语句各个阶段的耗时:
show profile for query query_id;
#查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;

 explain

        获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。

# 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;

EXPLAIN 各字段含义:

  • id:select 查询的序列号,表示查询中执行 select 子句或者操作表的顺序(id相同,执行顺序从上到下;id不同,值越大越先执行
  • select_type:表示 SELECT 的类型,常见取值有 SIMPLE(简单表,即不适用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
  • type:表示连接类型,性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all
  • possible_key:可能应用在这张表上的索引,一个或多个
  • Key:实际使用的索引,如果为 NULL,则没有使用索引
  • Key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好
  • rows:MySQL认为必须要执行的行数,在InnoDB引擎的表中,是一个估计值,可能并不总是准确的
  • filtered:表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值