GenweiWu / Blog

个人技术能力提升
MIT License
4 stars 0 forks source link

2021_Mysql数据库总结 #75

Open GenweiWu opened 3 years ago

GenweiWu commented 3 years ago

三大范式

第一范式

表的各个字段具有原子性,不可以再分解成多个字段

如学生(学号,姓名,性别,出生年月日),如果认为最后一列要再分成(出生年,出生月,出生日),它就不是一范式了

第二范式

表要有主键,且所有列都只依赖主键,不能存在部分依赖

比如 (学号,学生名,课程名,课程成绩)中, 学号->学生名 学号+课程名->课程成绩 这个时候主键是学号,但是成绩依赖于 学号+课程名,会导致出现学号这个主键出现多次,显然不合理

学号 课程名 课程成绩
001 语文 99
001 数学 97

第三范式

每一列都跟主键是直接相关,而不是间接相关,即不要有依赖传递

比如员工表(工号,部门编号,部门名称)里,有依赖关系:工号->部门编号->部门名称
而部门名称依赖于部门编号,而不是直接依赖工号,违反三范式

GenweiWu commented 3 years ago

sql中的4种连接

# 1.cross join/交叉连接/笛卡尔集:排列组合所有可能性m*n
select * from t_left,t_right;
# mysql不支持cross join写法
#selcet * from t_left cross join t_right on t_left.b=t_right.b

#2.inner join
select * from t_left join t_right on t_left.b=t_right.b;

#3.left join,right join
select * from t_left left join t_right on t_left.b= t_right.b;
select * from t_left right join t_right on t_left.b= t_right.b;

#4.full join:
# mysql不支持full join,可以通过 leftjoin + union + rightjoin
select * from t_left left join t_right on t_left.b= t_right.b
union 
select * from t_left right join t_right on t_left.b= t_right.b;

测试一下

t_left表 a b
a1 b1
a2 b2
a3 b4

t_right

c b
c1 b1
c2 b2
c3 b5
  1. 交叉连接cross join笛卡尔集 select * from t_left,t_right;
a b c b(1)
a1 b1 c1 b1
a2 b2 c1 b1
a3 b4 c1 b1
a1 b1 c2 b2
a2 b2 c2 b2
a3 b4 c2 b2
a1 b1 c3 b5
a2 b2 c3 b5
a3 b4 c3 b5
  1. inner join select * from t_left join t_right on t_left.b=t_right.b;
a b c b(1)
a1 b1 c1 b1
a2 b2 c2 b2
  1. left join select * from t_left left join t_right on t_left.b= t_right.b;
a b c b(1)
a1 b1 c1 b1
a2 b2 c2 b2
a3 b4 (Null) (Null)
  1. right join select * from t_left right join t_right on t_left.b= t_right.b;
a b c b(1)
a1 b1 c1 b1
a2 b2 c2 b2
(Null) (Null) c3 b5
  1. full join select from t_left left join t_right on t_left.b= t_right.b union select from t_left right join t_right on t_left.b= t_right.b;
a b c b(1)
a1 b1 c1 b1
a2 b2 c2 b2
a3 b4 (Null) (Null)
(Null) (Null) c3 b5
GenweiWu commented 3 years ago

联合查询:union 和 union all

  1. union:合并重复行+默认排序
  2. union all:不合并默认行+不排序

测试下

t_left表

a b
a1 b1
a2 b2
a3 b4

t_right

c b
c1 b1
c2 b2
c3 b5

1.union:合并重复行+默认排序 select t_left.b from t_left union select t_right.b from t_right;

b
b1
b2
b4
b5

2.union all:不合并默认行+不排序 select t_left.b from t_left union all select t_right.b from t_right;

b
b1
b2
b4
b1
b2
b5
GenweiWu commented 3 years ago

mysql有关权限的表有哪些?

1. mysql.user表: 记录可以连接到服务器上的用户信息,里面的权限时全局权限

Host User Select_Priv
% test_mysql Y
localhost test_mysql N

2. mysql.db表: 用来管理用户database级别的权限

Host Db User Select_priv
localhost request_service test_mysql N

3. mysql.tables_priv表: 用来管理

Host Db User Table_name Grantor Timestamp Table_priv Column_priv
% performance_schema test_mysql replication_group_members test_mysql@localhost 0000-00-00 00:00:00 Select

4. mysql.columns_priv权限表

Host Db User Table_name Column_name Timestamp Column_priv
GenweiWu commented 3 years ago

mysql常见数据类型

1、整数类型:tinyint和int

2、实数类型:float、double 、dicimal

3、字符串:char、varchar

char(n)和varchar(n)中的n表示的是字符不是字节

varchar用于可变字符串,一般是varchar(64),varchar(128)

char是定长的,一般是char(4)这种写法 char适合存储很短的字符串,或者所有值长度相近

4、日期类型:datetime、timestamp

  1. timestamp占用的字节少
  2. timestamp是以utf时间进行存储的,查询的时候会自动进行时区转换
类型 占据字节 表示形式
datetime 8 字节 yyyy-mm-dd hh:mm:ss
timestamp 4 字节 yyyy-mm-dd hh:mm:ss
GenweiWu commented 3 years ago

mysql 有两种数据库引擎

一种是 MyISAM,一种是 InnoDB

MyISAM 发音为 "my-z[ei]m"; InnoDB 发音为 "in-no-db"

主要区别是:

  1. innodb支持事务,支持外键,MyISam不支持
  2. innodb支持行级锁,表级锁,锁力度小并发能力强;MyIsam只能全表锁
GenweiWu commented 3 years ago

索引

什么是索引?对数据库字段进行预排序的数据结构

索引是关系型数据库中,针对1个或多个字段进行预排序处理;这样后面查询的时候,就不用去全表查询,从而加快后续的查询速度 就类似于数据的目录,可以更快的访问书中的指定内容

索引的优缺点

索引使用的场景‘

  1. where

where id='xx'中的id

img

  1. oder by xxx中的xxx

  2. join on xx中on涉及的字段

索引类型

  1. 主键索引:数据列不能为NULL,不能重复,一张表只能有一个主键

    primary key(column1)
    primary key(column1,column2)
  2. 唯一索引:数据列可以为NULL,不能重复,一张表可以建多个唯一索引

    alter table tableName1 ADD UNIQUE(column1) 
    # 唯一组合索引
    alter table tableName1 ADD UNIQUE(column1,column2)
  3. 普通索引:数据列可以为NULL,可以重复,一张表可以建多个普通索引

    alter table tableName1 ADD INDEX index_name(column1)
    alter table tableName1 ADD INDEX index_name(column1,column2)
  4. 全文索引

    alter table tableName1 ADD FULLTEXT(column1)

唯一索引 允许 多个NULL值存在(即不认为两个NULL为重复) https://blog.csdn.net/qq_35387940/article/details/109174748

索引设计的原则

  1. 不要过度设置索引:过多字段设置索引,会拖慢性能
  2. 索引列的值不宜过长:对长字符串进行索引,会造成索引文件大,索引维护时间长
  3. 索引对应字段最好是NOT NULL类型,实现不行可以设置个默认值;mysql中含有空值的列很难索引优化
  4. 索引对应的值要分散,不宜大量重复:比如性别就是男女这种,没必要设置索引
  5. 针对索引生效创建,比如where,order by,join on等涉及字段设置索引
  6. 字段频繁被查询,而不是频繁被修改的字段
  7. 尽量去扩展索引,而不是新增索引:比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
  8. 不要去列上进行运算,会导致索引失效
    select * from user where YEAR(birthday) < 1990
    -- 建立改造成
    select * from users where birthday <’1990-01-01′
  9. 利用好索引覆盖 如果select的字段都覆盖了索引,能提升搜索效率,所以建立不要用select * 而是指定select的列

索引的创建方式

1. 创建表的时候

CREATE TABLE user_index2 (
    id varchar(32),
    first_name VARCHAR (16),
    last_name VARCHAR (16),
    id_card VARCHAR (18),
--  主键
    primary key  (id), 
--  唯一索引
    UNIQUE (id_card),
--  普通索引
    INDEX index_name (last_name),
--  全文索引
    FULLTEXT (first_name)
);

2. alter table add

CREATE TABLE user_index2 (
    id varchar(32),
    first_name VARCHAR (16),
    last_name VARCHAR (16),
    id_card VARCHAR (18)
);

-- 主键
alter table user_index2 add primary key(id);
-- 唯一索引
alter table user_index2 add unique(id_card);
-- 普通索引:索引名称是可选的
alter table user_index2 add index index_222(last_name);
-- 全文索引
alter table user_index2 add FULLTEXT(first_name);

等同于

CREATE TABLE `user_index2` (
`id` varchar(32) NOT NULL,
`first_name` varchar(16) DEFAULT NULL,
`last_name` varchar(16) DEFAULT NULL,
`id_card` varchar(18) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_card` (`id_card`),
KEY `index_222` (`last_name`),
FULLTEXT KEY `first_name` (`first_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3. create index方式:必须设置索引名+不支持创建主键类型

CREATE TABLE user_index2 (
    id varchar(32),
    first_name VARCHAR (16),
    last_name VARCHAR (16),
    id_card VARCHAR (18)
);

-- 主键:不支持
-- XXX create primary key on user_index2(id);
-- 唯一索引:必须设置索引名
create unique index unique_name on user_index2(id_card);
-- 普通索引:必须设置索引名
create INDEX index_name on user_index2(last_name);
-- 全文索引:必须设置索引名
create fulltext index key_name4 on user_index2(first_name);

删除索引


CREATE TABLE user_index2 (
    id varchar(32),
    first_name VARCHAR (16),
    last_name VARCHAR (16),
    id_card VARCHAR (18),
--  主键
    primary key  (id), 
--  唯一索引
    UNIQUE (id_card),
--  普通索引
    INDEX index_name (last_name),
--  全文索引
    FULLTEXT (first_name)
);

-- 查看所有索引类型
show index from user_index2;

-- 删除主键
alter table user_index2 drop primary key;
-- 删除唯一索引,普通索引,全文索引
alter table user_index2 drop key id_card;
alter table user_index2 drop key index_name;
alter table user_index2 drop key first_name;
GenweiWu commented 3 years ago

索引一定能提升查询性能吗?为什么?

  1. 一般来说,索引可以提升查询性能,但是会导致增删改性能的损耗

  2. 索引范围查询不一定能提升性能

    范围查询(>、<、between、like)

    # 索引范围查询适用于:
    1. 基于范围的搜索,结果只有不到30%是在范围内的
    2. 基于非唯一性索引:男女性别这种,比全表扫描好不到哪儿去

百万级别的数据如何删除

由于索引增删改耗时,可以

  1. 先删除索引
  2. 再去删数据
  3. 重新建立索引
  4. 比直接删除要快!
GenweiWu commented 3 years ago

前缀索引

语法

index(column(10)) 表示使用column字段的前10个字符建立索引,而默认是使用column字段的全部内容建立索引的

前提

前缀的标识度比较高:能够有效区分不同的字段值,如果前缀都类似则不合适

建立前缀索引的难度在于

如何决定前缀索引的长度,是index(column(5)还是index(column(10)))

可以通过计算 select count(*) / select count(distinct left(column,长度))的值,长度可以从1一直递增,知道表达式的值趋向于1,表示可以通过前缀能确定对应数据了

select count(*) from table1
select count(distinct left(column,4))
GenweiWu commented 3 years ago

最左匹配原则

https://blog.csdn.net/qq_39390545/article/details/108540362

1. 什么是最左匹配原则

1.1 联合索引

联合索引是指多个字段组成的索引,比如(a,b),(a,b,c),注意(a,b)和(b,a)是不同的联合索引,因为顺序不同

1.2 最左匹配原则

就是以左边为起点,任何连续的索引都能匹配上,但是一旦遇到范围查询(>、<、between、like),之后的索引就会失效

比如索引(a,b,c)

-- 下面这些sql都会走索引
select * from table where a=1
select * from table where a=1 and b=2
select * from table where a=1 and b=2 and c=3
--即索引(a,b,c)实际也包含了索引(a)和(a,b)和(a,b,c)
-- 这些没法走索引
select * from table where c=3
select * from table where b=2 and c=3

2. 违背最左匹配原则的场景

针对联合索引(a,b,c)

2.1. 查询条件中,缺少了最左的a字段

-- 缺少了最左的a导致下面无法使用联合索引(a,b,c)
select * from table where b=2 and c=3

2.2 查询条件中,缺少了居中的b 字段

-- 此时只能使用a的索引效果,无法继续使用联合索引
select * from table where a=1 and c=3

3. 自动优化场景

3.1 =in可以乱序

比如a=1 and b=2 and c=3,建立索引时abc的顺序可以随意,mysql查询优化器会自动优化 比如 a=1 and b in(2,3),建立(a,b)还是(b,a)都是可以的

3.2 mysql遇到范围匹配会停止继续使用索引,但是可以通过优化索引顺序来优化

比如a=1 and b=2 and c>3 and d=4,如果建立的是(a,b,c,d)的索引则只能用到abc字段;但是如果建立的是(a,b,d,c)顺序的索引,则可以用到abcd所有字段

3.3 like的索引

如果通配符不出现在开头,可以使用索引

-- 可以使用索引
like 'xxx%'`
-- 不可以使用索引
like '%xxx'
like '%xxx%'

4. 了解

4.1 利用索引排序了,就不用再order by

-- 这里如果sex已经利用了索引进行查询,就没必要order by了
SELECT sex, price, name FROM LOL where sex = 1 ORDER BY sex ;
GenweiWu commented 3 years ago

数据库事务

事务的四大特性 ACID

https://www.cnblogs.com/kismetv/p/10331633.html


- Isolation隔离性:
多个事务之间的相关隔离的,不能互相影响

比如A事务修改了数据没提交呢,B事务读取了数据,这就互相影响了


- Durability:持久性

对数据库的保存是持久的:

因为数据库为了避免每次都写磁盘IO,实际上它是有内存缓存的,如果数据库突然宕机了,岂不是丢失了? 方案:数据库用redo Log保证了宕机后,会根据read log来恢复



## 脏读、不可重复读、幻读

> https://www.cnblogs.com/kismetv/p/10331633.html

### 脏读: 读取了其他事务未提交的数据

![img](https://img2018.cnblogs.com/blog/1174710/201901/1174710-20190128201003630-2050662608.png)

### 不可重复读: 一个事务中两次读取的数据不一样

![img](https://img2018.cnblogs.com/blog/1174710/201901/1174710-20190128201011603-1317894910.png)

### 幻读:一个事务中两次读取的数据返回的数量不同

![img](https://img2018.cnblogs.com/blog/1174710/201901/1174710-20190128201021606-1089980279.png)

## 事务隔离级别

|隔离级别|  脏读 | 可重复读|幻读|
|--|--|--|--|
|Read Uncommitted 读未提交|V|V|V|
|Read Committed 读已提交|x|V|V|
|Repeatable Read 可重复读|x|x|V|
|Serializable 可串行化|x|x|x|

- Mysql默认级别是 Repeat Read,oracle默认级别是 Read Commited
- 事务隔离机制的实现,是基于锁机制和并发机制,其中并发机制是依赖于MVCC
GenweiWu commented 3 years ago

对Mysql的锁了解吗?

多个事务并发的时候,可能会互相影响,所以要使用锁机制。

按照粒度大小分,锁有哪些?

行锁 < 页锁 < 表锁

不同搜索引擎

Innodb使用行锁+表锁,默认行锁 MyIsam使用表锁

按照类别分

就是说,可以多个同时来读;但是一旦有写操作,另外的读写都不可以了

不同隔离级别 与 锁的关系(了解)

Read Uncommitted 读数据不加共享锁
Read Committed 读操作加锁,但执行完语句就释放(还没提交时区)
Repeatable Read 读操作加锁,事务提交之后才释放锁
Serializable 一直持有锁

乐观锁、悲观锁

MVCC

https://www.cnblogs.com/kismetv/p/10331633.html

Multiple Version Concurrency Control 多版本并发控制

GenweiWu commented 3 years ago

什么是视图?为什么要用视图?

数据库的表成为基础表,那么视图就是虚拟表

视图感觉就是一段select语句逻辑保存成视图,后面可以进行复用

视图本身不保存数据,而是每次使用的时候动态去查询

-- 创建视图:create view `view_name` as
create view document_view as 
SELECT * FROM `t_document`
where CATA_ID='2001b'

-- 视图的使用:select 
select * from document_view
where DOC_ID='1'

-- 可以更新视图数据,会影响基本表
update document_view
set FILE_TITLE='local搜索测试-视图更新'
where DOC_ID='1'

视图的特点

使用场景

  1. 复用sql语句
  2. 使用的时候不需要关注细节
  3. 使用表的部分列而不是所有列
  4. 保护数据,可以让用户能访问表的特定部分,而不是整张表

优点、缺点

优点

  1. 简化查询
  2. 保护数据安全性

缺点

  1. 性能: 复杂的视图性能差
  2. 修改限制: 复杂的视图无法直接更新数据,而要去修改基本表
GenweiWu commented 3 years ago

存储过程

存储过程是一个预编译的sql语句,创建之后可以被多次调用

如果要多次调用sql,可以考虑用存储过程替换

优点

  1. 预编译过,执行效率高
  2. 可以重复使用
  3. 安全性高,执行存储过程要求用户有一定的权限

缺点

  1. 调试麻烦,一般是打日志
  2. 跟数据库系统相关,切换数据库的话要同步修改
  3. 维护麻烦:存储过程相关的表修改,要重新编译存储过程

什么是游标

游标是设置一个数据缓冲区,存放sql执行结果;游标可以通过逐行读取记录并赋值给变量。 一般用在存储过程中

触发器

对某个表执行一段增删改操作时,自动去执行一段代码。 比如文档信息和文档的附件信息,删除文档时同步删除文档附件信息

使用场景

6种触发器

GenweiWu commented 3 years ago

超键、候选键、主键、外键

比如说用户表,ID,用户名称,用户身份证号 其中ID和身份证号都是唯一标识

主键也是候选键,候选键也是超键

SQL约束有哪些!

GenweiWu commented 3 years ago

子查询

在select语句里面嵌套使用select语句,里面的select语句就是子查询

子查询常见的3种情况

1. 子查询返回1行1列=即返回一个值

可以使用=,<,>,>=,<=,<>和子查询结果进行比较

-- 查询年龄是部门年龄最大的员工
select * from employee 
where age = 
(
select max(age) from employee '
)

2. 子查询返回多行1列

一般使用in,any,all

-- 查询出2010年以后新建的部门的员工
select * from employee 
where d_id in 
(
select d_id from department
where createDate > '2010-00-00'
)

3. 子查询返回多行多列

select * from department d,(select * from employee where join_data>'2020-1-1') e where d.id = e.deparment_id

-- 等同于
select * from department d join employee e on d.id = e.department_id where e.join_data>'2020-1-1'

in和exists

-- 查询出2010年以后新建的部门的员工
select * from employee 
where d_id in 
(
select d_id from department
where createDate > '2010-00-00'
)

-- exists
select * from employee 
where exists 
(
select d_id from department 
where employee.d_id=department.d_id and d.createDate > '2010-00-00'
)

in和exists何时使用

GenweiWu commented 3 years ago

varchar vs char

char varchar
长度 定长字符串,长度固定 可变长字符串,长度可变
是否填充 数据长度小于长度时,填充空格 数据长度是多少,就占用多长空间
性能 固定长度,存储速度快 可变长度,读取速度慢
空间 占空间 节省空间

varchar(50)的50的含义

  1. 字符最长50
  2. 早期mysql版本中表示50字节,现在表示50字符(50个中文字符/50英文字符,占用字节是不同的)
  3. varchar(50)和varchar(100)如果保存hello的话,则占用空间相同;但是order by时varchar(50)更快

int(20)中20的含义

  1. int是4个字节长度,存储范围不变
  2. 20是最长显示长度为20,开启zerofill补零会导致前边补0,仅用于展示

int(10)和char(10)和varchar(10)的区别

drop、delete、truncate

-- 删除表,包括表结构+表数据
drop table t_table;
drop table if exists t_table1;

-- 删除表部分数据
delete from t_table1 where id>10

-- 清空表数据
truncate table t_table1
Delete Drop Truncate
删除的内容 删除符合条件的行 删除表结构和表数据 清空表,保留表结构
是否可回滚 可回滚 不可回滚 不可回滚
删除速度 慢,逐行删除 最快
类型 DML DDL DDL

DDL等

# 速记
DDL: D表示defination定义,比如create和drop
DML: M表示Modify修改,比如insert、update、delete
DQL: Q表示查询,比如select
DCL:C表示控制,比如commit、rollback
GenweiWu commented 3 years ago

如何sql性能优化

使用explain查看sql执行计划

possiable keys说明可能能用到的索引 key表示实际用到的索引 key_len表示索引长度 rows表示实际扫描的行数,出现Using fifesortUsing temporary

优化方法

  1. 建索引
  2. 避免select *而用具体字段,这样来最大限度来进行索引覆盖

in和exists相关

  1. in里面的值不宜太多;
  2. in和exists: 如果是in子查询的话,那么子查询返回的值多的话用exists,子查询返回内容少的用in
  3. not exists的效率比not in好

联合索引

  1. 针对联合索引,要关注where后面的写法是否满足 最左匹配原则
  2. 针对联合索引,要避免范围匹配
  3. 范围匹配中的like '%'的通配符不在前面的模糊搜索可以使用索引

普通索引

  1. 索引对应字段不宜太长
  2. 针对长字段建立前缀索引
  3. 针对离散度搞的字段建索引,比如性别男女这种不建议
  4. 避免NULL: 不建议在where语句判断字段 IS NULLIS NOT NULL,索引无法生效
  5. 避免数学计算:不要where子句中进行数学计算 ; 比如age/2>10索引失效
  6. 避免类型强制转换: 避免where子句中的参数进行类型转换,比如数字类型='12'会使用索引失效

between、uniton、limit、ordre by

  1. 对于连续的条件,优先用between来替换in
  2. 用union all来替代union,用inner join替代left joina/right join
  3. 当只需要一条数据时,指定limit 1
  4. 如果order by的字段没有索引,尽量不去排序
GenweiWu commented 3 years ago

索引失效原理(了解)

B+树,联合索引(a,b)对应B+树上,是a有序,a相等的情况下b有序。

https://cloud.tencent.com/developer/article/1704743