一. 数据库基础
1. 概念
-
数据库本质:一个文件系统,对数据的操作就是对文件的操作【IO操作】
-
数据库是什么:用于存储和操作数据的文件系统
-
MySQL:MySQL是关系型数据库,基于二维表来存储的
- 支持主键(Primary Key)和外键(Foreign Key)约束,确保数据的完整性和一致性。
- 主键用于唯一标识表中的每条记录,外键用于建立表与表之间的关系。
2. 库表结构操作
1.一般图形化工具
- MySQL:SQLyog,Navicat,DataGrail
- Oracle:Navicat,DataGrail,PL Store...
2.SQL语句
- 库操作: create database
sql
#1.创建一个简单的数据库:
CREATE DATABASE mydatabase;
#2.创建数据库并指定字符集和校对规则:
CREATE DATABASE mydatabase
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
#3.创建数据库并避免重复创建:
CREATE DATABASE IF NOT EXISTS mydatabase;
#4.创建数据库并指定字符集和校对规则(完整示例):
CREATE DATABASE IF NOT EXISTS mydatabase
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
- 表操作: create table,alter table,drop table,truncate table
sql
#1.CREATE TABLE 用于创建一个新的表,并定义表的结构,包括列名、数据类型、约束等。
CREATE TABLE 表名 (
列名1 数据类型 [约束],
列名2 数据类型 [约束],
...
[表级约束]
);
#示例
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY, -- 主键,自动递增
first_name VARCHAR(50) NOT NULL, -- 非空约束
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE, -- 唯一约束
age INT CHECK (age >= 18), -- 检查约束
hire_date DATE
);
#2.TRUNCATE TABLE 用于清空表中的所有数据,但保留表的结构。
TRUNCATE TABLE 表名;
#常见语法
#1.添加列
ALTER TABLE 表名 ADD COLUMN 列名 数据类型 [约束];
#2.删除列
ALTER TABLE 表名 DROP COLUMN 列名;
#3.修改列
ALTER TABLE 表名 MODIFY COLUMN 列名 新数据类型 [约束];
#4.重命名列
ALTER TABLE 表名 CHANGE COLUMN 旧列名 新列名 新数据类型 [约束];
#5.添加约束
ALTER TABLE 表名 ADD CONSTRAINT 约束名 约束类型;
#6.删除约束
ALTER TABLE 表名 DROP CONSTRAINT 约束名;
- PD(PowerDesingner)
- 数据库建模: CDM(概念数据模型),PDM(物理数据模型),LDM(逻辑数据模型)
- 表数据操作
- DQL(Select):
- 基础查询、条件查询、连接查询、分组与聚合、排序、分页查询、高级查询
- DML
- DQL(Select):
sql
#1.INSERT 语句用于向表中插入新的数据行。
INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);
#2.UPDATE 语句用于修改表中已存在的数据行。
UPDATE 表名
SET 列1 = 值1, 列2 = 值2, ...
WHERE 条件;
#3.DELETE 语句用于删除表中的数据行。
DELETE FROM 表名
WHERE 条件;
3. 实际开发
- 先建模
- PowerDesigner
- 创建CDM
- 概念数据模型
- 概念数据模型
- 由CDM生成PDM
- 物理数据模型
- 物理数据模型
- 由 PDM 生成 SQL
- SQL就可以直接导入到数据库
- SQL就可以直接导入到数据库
- 创建CDM
- PowerDesigner
- 步骤
- 通过静态原型,分析实体及属性,再通过CDM建出实体模型以及模型之间的关系,构建CDM,再通过CDM构建PDM,再通过PDM生成SQL文件
- 作用
- 方便我们去构建数据模型
- 再导库
- 微调
3.内容
1. 数据类型
-
常用
-
int
- tinyint:一般用于枚举值(如 性别 0女 1男 2保密)
- bigint:一般用于主键,雪花算法
-
varchar
- 变长字符串
- 实际长度是真正存储数据的长度
-
char
- 定长字符串
- 实际长度是设定的长度
-
date
- 一般用于出生年月日等字段
-
datetime
- 一般用于创建时间等
-
-
少用
- text:长文本
- double
- float
- decimal
2.CRUD
- insert
sql
insert into 表名 (字段列表) values (值列表)
insert into 表名 (字段列表) values (值列表),(值列表),(值列表)....
- delete
sql
delete from 表名 where 条件
# 逻辑删除:使用一个标记字段表示当前记录是否删除 is_delete【本质上是更新】
- update
sql
update 表名 set 字段1=值1, ... where 条件
- select
-
避免使用 SELECT *
- SELECT * 会检索表中的所有列,可能导致性能问题,尤其是当表中包含大量列或数据时。此外,它会增加网络传输的负担。
-
统计聚合
count(*)
-
单表(SQL执行顺序)
-
from --> where ---> group by ---> 聚合函数 ---> having ---> select ---> distinct ---> order by ---> limit
-
-
连表
-
SQL执行顺序:
from --> join...on ---> where ---> group by ---> 聚合函数 ---> having ---> select ---> distinct ---> order by ---> limit
-
内连接(交集)
inner join
---> 显示内连接where A.id = B.aid
---> 隐式内连接,等值连接得到结果,是满足条件的两个表的交集
-
左外连接
left [outer] join
跟内连接有什么区别?
- 内连接:显示两个表符合条件的数据
- 左外连接:左表显示全部,右表匹配条件则显示,不匹配以NULL值填充
-
子查询
-
能够使用连接查询的情况,尽量不要使用子查询
-
如果子查询能够减少数据量查询(减少笛卡尔积情况),建议使用
-
结果
一个值:聚合函数,使用 =
多个值:使用in
一个表:使用from
-
-
5.视图
-
伪表:数据来源于原表,本身不存储数据
-
对视图的增删改操作都是在操作原表数据
- 视图最大的目的是为了辅助查询,所以一般不对视图做增删改操作。设置为只读视图
- create view 视图名称 as select 语句
-
作用
-
辅助查询,提升查询性能
-
屏蔽敏感数据
-
主分同步
- 主:修改数据
- 分:查【数据会实时同步】
-
4.存储过程
- 是一组为了完成特定功能的SQL语句集合。例如:转账
- 阿里开发说:禁止使用存储过程,银行系统可能会用
5.触发器(了解)
- 一种特殊的存储过程
- 作用:在对表中数据做增删改操作之前或者之后的时机,触发一些事件【如:记录日志、数据校验等】
二.存储引擎
1.查看存储引擎
SHOW ENGINES
2.InnoDB - 默认的存储引擎
-
支持事务、支持物理外键【一般不用】 ---> 物理外键是在早先数据库设计三范式中必须遵循的。
-
天生支持行锁、手动支持表锁
-
使用聚簇(集)索引
- 索引跟数据在同一个文件(表名.ibd)
- 只有主键索引是聚簇索引,其他索引是非聚簇索引
3.MyISAM
- 不支持事务、不支持外键
- 只支持表锁,不支持行锁
-
使用非聚簇索引
- 索引跟数据不在一个文件
- 数据 ---> (表名.myd)
- 索引 ---> (表名.myi)
面试题:InnoDB 和 MyISAM 的区别?
-
事务支持
- MyISAM:不支持事务,强调的是性能。
- InnoDB:支持事务:支持4个事务隔离级别
-
表锁差异
- MyISAM:表级锁定形式,数据在更新时锁定整个表
- InnoDB:行级锁定,但是全表扫描仍然会是表级锁定
-
读写过程
-
MyISAM:数据库在读写过程中相互阻塞
- 会在数据写入的过程阻塞用户数据的读取
- 也会在数据读取的过程中阻塞用户的数据写入
-
InnoDB:读写阻塞与事务隔离级别相关
-
-
缓存特性
-
MyISAM:可通过key_buffer_size来设置缓存索引,提高访问性能,减少磁盘I/O的压力
- 但缓存只会缓存索引文件,不会缓存数据
-
InnoDB:具有非常高效的缓存特性:能缓存索引,也能缓存数据
-
-
存储方式
- MyISAM:釆用 MyISAM存储引擎数据单独写入或读取,速度过程较快且占用资源相对少
- InnoDB:表与主键以簇的方式存储
-
外键支持
- MyISAM:MyISAM存储引擎它不支持外键约束
- InnoDB:支持外键约束
-
全文索引
- MyISAM:只支持全文索引
- InnoDB:5.5以前不支持全文索引,5.5版本以后支持全文索引
-
在磁盘上的存储类型
-
MyISAM:每个 MyISAM在磁盘上存储成三个文件,每一个文件的名字以表的名字开始,扩展名指出文件类型
- MyISAM在磁盘上存储的文件
- .frm文件存储表定义
- 数据文件的扩展名为.MYD( MYData)
- 索引文件的扩展名是.MYI( MYIndex)
-
InnoDB:所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
-
-
存储空间
-
MyISAM:支持三种不同的存储格式:
- 静态表(默认,但是注意数据末尾不能有空格,会被去掉)
- 动态表
- 压缩表
- 当表在创建之后并导入数据之后,不会再进行修改操作,可以使用压缩表,极大的减少磁盘的空间占用
-
InnoDB:需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
-
-
表主键
- MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址
- InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值
-
表的具体行数
- MyISAM:保存有表的总行数,如果select count() from table;会直接取出出该值
- InnoDB:没有保存表的总行数,如果使用select count(*) from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样
-
读写性能
-
MyISAM:读取性能优越,但是写入性能差
- 如果执行大量的select,MyISAM是更好的选择 InnoDB:写入性能较强
-
如果执行大量的insert或者update,InnoDB是更好的选择
-
http
原文链接:https://blog.csdn.net/CN_TangZheng/article/details/103858444
三. 索引
1.概念
- 一种排好序,能够提升查询性能的数据结构
2.为什么使用索引
- 优点:
- 加快了数据的查询效率
- 将随机IO变成顺序IO,降低数据库的IO成本(降低CPU消耗,因为B+树的叶子是连在一起的)
- 加速表与表之间的连接
- 缺点:
- 从空间角度考虑,建立索引需要占用物理空间
- 从时间角度考虑,创建和维护索引都需要花费时间(例如:降低了insert、update、delete的效率。)
3.MySQL索引-结构
1. 二叉树
二叉查找树:左边的子节点比父节点小,右边的子节点比父节点大
- 当我们向二叉查找树保存数据时,是按照从大到小(或从小到大)的顺序保存的,此时就会形成一个单向链表,搜索性能会打折扣。
2.平衡二叉树
- 平衡二叉树是采用二分法思维,平衡二叉查找树除了具备二叉树的特点,最主要的特征是树的左右两个子树的层级最多相差1。在插入删除数据时通过左旋/右旋操作保持二叉树的平衡,不会出现左子树很高、右子树很矮的情况。(红黑树也是一棵平衡的二叉树)
- 平衡二叉树的问题
- 时间复杂度和树高相关。树有多高就需要检索多少次,每个节点的读取,都对应一次磁盘 IO 操作。树的高度就等于每次查询数据时磁盘 IO 操作的次数。在表数据量大时,查询性能就会很差。
- 平衡二叉树不支持范围查询快速查找,范围查询时需要从根节点多次遍历,查询效率不高。
B树
-
B树是一种多叉平衡查找树
-
B树的节点中存储着多个元素,每个内节点有多个分叉。
- 非叶子节点:父节点
- 叶子节点:没有子节点的节点
-
节点中的元素包含键值(索引值)和数据(行数据),节点中的键值从大到小排列。也就是说,在所有的节点都储存数据。
- 索引:8B
- 行数据:1K
- 总共1032B
-
父节点当中的元素不会出现在子节点中。
-
所有的叶子结点都位于同一层,叶节点具有相同的深度,叶节点之间没有指针连接。
-
在MySQL中
-
以页来存储数据,一页大小为 16K,而索引大小约8B,数据大小约1K,所以一页 【16 * 1024 / (8+1024)B = 1024B】,得到 15。
- 第一层:存 15个数据
- 第二层:存 15 * 15个数据
- 第三层:存 15 * 15 * 15个数据
- 以此类推
-
要想存储大量数据,树的高度会越来越高,那么在查询的时候,走的路径就越长,查询效率就越慢
-
4.B+树
-
B+树和B树最主要的区别在于非叶子节点是否存储数据的问题
-
非叶子节点:
-
存储的是索引值 和 指向下个索引值的指针
- 索引值:8B
- 指针:6B
- 总共 14B
-
一个节点可以存储 【16*1024B / 14B】= 1170
-
一层存储 1170
-
二层存储 1170*1170
-
三层如果存储是叶子节点
- 16*1024B / 1032B = 15个
- 存储的总数据量为 1170117015 = 2000W+的数据
-
-
-
叶子节点:
- 存储索引值和数据【8B + 1024B】
- 会冗余存储非叶子节点的索引值
- 节点之间有指针【双向链表】,提升区间访问效率
-
区别:
- B树:非叶子节点和叶子节点都会存储数据。
- B+树:只有叶子节点才会存储数据,非叶子节点至存储键值。叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。
为什么不选择 hash?
- Hash表在等值查询时效率很高 ,时间复杂度为O(1);但是不支持范围快速查找,范围查找时还是只能通过扫描全表方式。当哈希冲突很多的时候,效率会降低。
四.索引底层为什么采用B+树而不是B树
- B树适用于随机检索,而B+树适用于随机检索和顺序检索
- B+树的空间利用率更高,因为B树每个节点要存储键和值,而B+树的内部节点只存储键,这样B+树的一个节点就可以存储更多的索引,从而使树的高度变低,减少了I/O次数,使得数据检索速度更快。
- B+树的叶子节点都是连接在一起的,所以范围查找,顺序查找更加方便
- B+树的性能更加稳定,因为在B+树中,每次查询都是从根节点到叶子节点,而在B树中,要查询的值可能不在叶子节点,在内部节点就已经找到。
那在什么情况适合使用B树呢,因为B树的内部节点也可以存储值,所以可以把一些频繁访问的值放在距离根节点比较近的地方,这样就可以提高查询效率。综上所述,B+树的性能更加适合作为数据库的索引。
http
原文链接:https://blog.csdn.net/qq_35190492/article/details/109257302
五.主键索引
- InnoDB引擎的表一定需要主键,如果不创建,数据库会自动创建并维护一个主键索引。创建主键的列建议是没有业务意义的列
-
非叶子节点:存储主键值
-
叶子节点:存储行数据
-
所以通过主键可以直接在索引树上找到数据,不需要查表,速度非常快
-
推荐使用主键自增
- 因为插入的数据始终会放在最后面,可以快速的找到插入的位置,无需做额外的开销,如移动数据的位置,旋转树等
- 如果不是自增,那么就无法判断要插入的数据具体是插入到索引树的哪个一位置,所以也无法判断树中数据的变化与树的旋转。那么就会带来不必要的开销
六.非主键索引
- 普通索引
- 在普通字段上建立索引
-
非叶子节点:存储索引列值
-
叶子节点:
-
存储的数据是主键值
-
为什么非主键索引的叶子节点数据存储的是主键值?
- 因为如果不存主键值,那么就只能存储数据。
- 如果数据发生了变化,不仅要维护主键索引,同时还需要维护其他索引。表示修改了一个索引的数据,会同时修改其他索引的数据,这需要带来额外的开销
-
-
唯一索引
- 给唯一列创建索引(unique)
-
联合索引
- 给主键以外的多个列创建索引,也叫联合索引【组合索引】
- 表:id,name,age,phone
- 索引:name,age,phone
-
全文索引
- 因为Elasticsearch
- Elasticsearch 是一个功能强大的分布式搜索引擎,适用于需要高效搜索和数据分析的场景。
七.创建索引的要求?
- 单表索引不超过5个
- 联合索引的字段不超过5个
- 经常增删改的字段不适合创建索引
- 枚举值字段不适合创建索引
- 不经常修改,经常查询的字段适合创建索引
- 大长度的字段,可以设置前缀索引(为字段的前几个字符建立索引)
sql
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
email VARCHAR(255)
);
-- 创建前缀索引
CREATE INDEX idx_username_prefix
ON users (username(10));
八.创建索引语法
-
主键索引
- 命名要求:pk_列名
sql#建表时,主键默认为索引 create table user( id varchar(11) primary key, name varchar(20), age int ) #查看user表中的索引 show index from user;
- 普通索引
- create index 索引名称 on 表名(具体的列名)
- 命名要求:idx_列名
sql
#创建普通索引,只能包含一个字段
create index name_index on user(name);
- 唯一索引
- create unique index 索引名称 on 表名(列名)
- 命名要求:uk_列名
sql
#创建唯一索引,只能有一个列
create unique index age_index on user(age);
- 联合索引
- create index 索引名称 on 表名(列1,列2)
- 命名要求:idx列1列2
sql
#复合索引(联合索引)
create index name_age_index on user(name,age);
http
原文链接:https://developer.aliyun.com/article/831250
二.数据库优化
1. 合理设计表结构(参考58军规版)
- 字符类型:utf8、utf8mb3
- mtf8mb4:有特殊符号,用这个,如微信中的笑脸
- 字段的名称、数据类型、长度、是否为空、有无默认值
2.查询SQL的执行原理
3. 查询SQL优化
-
最终原则:
- 尽量让SQL命中索引,提升查询性能
- 但是MySQL会判断,如果不走索引比走索引还要快。他就不走索引了
-
最左前缀法则【模糊搜索和联合索引】
-
联合索引由多个列组成
-
在使用联合索引时,必须满足从左边开始匹配索引列
-
如 联合索引为 name, age, phone
- name有索引
- age有索引
- phone有索引
-
where name = 'aa' and age = 10 and phone = '13512341234'
- 命中全部索引
-
where name = 'aa' and age = 10
- 命中name和age索引
-
where name = 'aa' and phone = '13512341234'
- 只命中name索引
-
where name = 'aa' and phone = '13512341234' and age = 10
- 内部查询优化器会对该SQL进行优化
- 命中全部索引
-
where name = 'aa' and age = 10 or phone = '13512341234'
-
不走
-
or会让索引失效
-
修改一下,命中索引
- select * from 表 where name = 'aa' and age = 10 union select * from 表 where phone = '13512341234'
- union:合并去重
- union all:合并不去重
-
-
-
- 在写查询条件时,应该从联合索引的最左列开始依次编写
- 查询时,查询条件的列必须跟创建索引时的列顺序一致
-
索引失效的情况【8种】
-
使用 !=
-
使用 or
-
使用 is null 或者 is not null:null 做判断
-
条件索引列做运算,使用函数
- where age+1=20(索引失效)
- where age = 20-1(索引生效)
-
左%
- 如 name like '%a'
- 违反了最左匹配法则
-
字符串比较丢失引号
- 如 a = '1' 不能写成 a = 1
-
分页查询
-
数据量太大
- limit 99900, 100(先查出100000,再去掉前面的99900)
- 可以加上限制条件,譬如查第1000页数据,每页显示100条,那么可以加上 id > 98999 的条件
-
如果要用in
- 里面的元素个数不要超过1000
-
varchar类型,字符编码不一致
-
-
4.如何判断是否命中索引【Explain】
-
explain SQL
- SQL【查询、更新、删除】
- mysql 自带的SQL优化器
- 查看SQL的执行计划【通过返回的列去分析是否命中了索引】
-
explain 各列意思
const:主键跟常量去进行等值比较
ref
- 简单查询: 在简单查询中,使用了非主键索引做等值匹配
- 复杂查询:被关联查询的表使用了非主键索引作为关联条件
range:使用索引列作范围查询【数据量不能太小】
index:覆盖索引就会出现index
ALL:全盘扫描
- key_len 计算规则
- mysql5 -
- mysql8
- rows
- 可能需要查询的行数
- mysql5 -
- Using index:使用了覆盖索引
5.优化规则
-
尽量命中索引
-
使用联合索引时遵循最左前缀法则
-
避免索引失效的情况
-
分页查询优化
- select id, name from tb_user where id > 99899 limit 100【索引生效】
- select id,name from tb_user limit 99900, 100【索引失效】
-
避免回表
-
回表
-
查询时,先通过非主键索引,找到主键值,再通过主键值找到其他数据【回表】。
- 先走了普通索引,再走了主键索引
-
如:
- 表结构:id,name,age,phone
- 索引
- id列:主键索引
- name列:普通索引
-
查询语句
- select name, age from tb_user where name = 'aa'【出现回表】
- 先根据 name索引列找到 id列,再通过id列找到 age列,走了两次索引
-
-
使用覆盖索引
-
覆盖索引:查询的字段是索引列
-
解决上面SQL出现的回表现象
- 给 name,age 创建联合索引
- select name, age from tb_user where name='aa'
-
-
索引下推
-
MySQL5.6之后有的一个特征,自动会用【减少回表】
-
select name, age from tb_user where name='a%' and age>10
-
没有索引下推
- 先找到a开头的name值 --> 找到ID,回表查询
-
有索引下推
- 判断name,age是否都是索引列,如果是,就表示触发了覆盖索引,直接查询索引,不回表,返回数据
-
-
查看Extra中的值是 Using index condition,就表示触发了索引下推
-
-
6.线上慢SQL定位
-
发现问题 :一般项目部署上线以后,会有监控系统,专门监控SQL 。druid,监控发出告警信息
- 系统上可以设置SQL的执行时间来判断是否是一个慢SQL。如果出现了,则会发送告警信息
-
定位分析问题
-
慢SQL日志定位
-
启用慢查询日志:在MySQL配置中启用慢查询日志,这样可以记录执行时间超过阈值的查询语句。通过分析慢查询日志,可以找到执行时间较长的SQL语句。
-
使用EXPLAIN分析执行计划:对于慢查询的SQL语句,使用EXPLAIN命令来查看其执行计划。通过分析执行计划,确定查询是否有效利用了索引以及是否存在性能瓶颈。
-
检查索引使用情况:确保查询中涉及的列都有适当的索引,并且查询条件能够充分利用索引。可以使用SHOW INDEX命令或查询表的索引信息来检查索引情况。
-
分析查询语句:仔细分析查询语句本身,检查是否存在冗余的操作、重复的子查询、不必要的排序、大量的JOIN操作等。
-
-
查看执行计划
- 通过Explain查看SQL的执行计划
-
-
优化SQL
-
使用 explain 去分析
-
会得到一个结果
-
然后就根据这里面的字段进行分析
-
type
索引类型
- 遵循一个原则,尽量触发前面的
- 常用值:system > const > eq_ref > ref > range > index > ALL > * 尽量让这个值往前,如果可以的话,至少保证到 range
-
key
- 实际命中的索引
-
key_len
-
命中索引的字段的计算规则
-
key_len 计算规则
-
如果允许为NULL,则需要多加一个字节
-
mysql5.7
-
mysql8
-
加个1
- 如 int 4个字节【4 + 1】
- 如 varchar(20)【20 * 3 + 2 + 1】
-
-
-
-
rows
- 可能需要查询的行数
-
extra
- 可能需要查询的行数
-
-
-
根据规则去优化
-
-
关于项目中使用Druid数据库监控的配置
//TODO