提示:适用于想要学习测开方向的MySQL知识的学习,完全够用
SQL语句分类
DDL:数据定义语言,用来定义数据库对象(库,表,字段)
DML:数据操作语言,增删改
DQL:数据查询语言,查询数据库中表中的记录
DCL:数据控制语言,创建数据库用户,控制用户访问权限
查询字段去除重复数据
selectdistinct字段 from 表名
数值函数
ceil(x) 向上取整
floor(x) 向下取整
mod(x,y) 返回x/y的模
rand() 生成0-1之间的随机数
round(x,y) 求参数四舍五入的值,保留y位小数
字符串函数
字符串截取之substring_index(str,delim,count)
str:要处理的字符串
delim:分隔符
count:计数
日期函数
curdate() 获取当前的年月日
curtime() 获取当前的时分秒
now() 获取当前的年月日-时分秒
year(date) 获取指定date的年份
month(date) 获取指定date的月份
day(date) 获取指定date的日数
date_add(date,interval expr type) 在指定的date上增加一个时间间隔expr后的时间值(ytpe单位,年月日)
datediff(date1,date2) 返回两个日期之间相差的天数
流程控制函数
if(value,t,f) 如果value为真则输出t否则f
ifnull(value1,value2) 若value1不为空,返回value1,否则返回value2
case when [val] then [1] else [2] end 若val为真则返回1否侧返回2
case [expr] when [val1] then [1] else [2]
约束
非空约束 not null 限制阻断数据不能为null
唯一约束 unique 保证字段的数据都唯一,不重复
主键约束 primary key (配用auto_increment 一行数据的唯一标识,非空且唯一
外键约束 foreign key 用来让两张表之间建立连接,抱持数据的完整性和一致性
这里需要注意的是,有具有外键的表为子表,外键所关联的表为父表
添加外键:
1.create table 表名 (
字段1 类型 constrant [外键名称] foreign kry (外键字段名) references 主表(字段)
)
2.alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(关联字段)
删除外键:
alter table 表名 drop foreign key 外键名称;
删除/更新行为
alter table 表名 add constraint 外键名称 foreign key (外键字段名称) references 主表(关联字段)
默认约束 default 保存数据时,如果未指定字段值的值,则用默认值
检查约束 check 抱持字段满足某一个条件
多表查询
一对多(多对一)
例如:员工和部门之间的关系
解决方案:添加外键关联
多对多
例如:学生和选课之间的关系
添加中间表对应
一对一
例如:个人基本信息和个人其他信息的关系
多表查询分类:
连接查询:
内连接:
查询表A,B的交集部分
隐式内连接:
select 字段列表 from 表1,表2 where 条件;
显示内连接:
select 字段列表 from 表1 [inner] join 表2 on 条件;
外连接:
左外连接:
select 字段列表 from 表1left[outer] join 表2 on 条件;
(查询的是表1和表2共同的数据,以及表1的所有数据)
右外连接:
select 字段列表 from 表1right[outer] join 表2 on 条件;
(查询的是表1和表2共同的数据,以及表2的所有数据)
自连接:
当前表与自身连接查询,自连接必须用别名
select 字段列表 from 表1 别名1 join 表1 别名2 on 条件;
自连接可以是内自连接也可以是外自连接
连结查询
select 字段列表 from 表A
union [all]
select 字段列表 from 表B
union all / union
union all 数据整合不去重
union 去重
注意:连结查询的多张表必须字段列数抱持一致,字段类型也抱持一致。
子查询
标量子查询:子查询的结果为的单行单列(单个值)
常见的操作符为:> ,>=,< ,<=,=,<>
列子查询:子查询的结果为一列(可以是多行)
常见的操作符为:in,not in,any=some(子查询返回的列表中,有一个满足即可),all(子查询返回的列表中,必须所有的满足)
行子查询:子查询的结果为一行(可以是都多列)
常见的操作符为:=,<>,in not in
表子查询:子查询的结果为多行多列
常见的操作符为:in
---------------------------------------------------------------------------------------------------------------------------------
这里我们重点讲解一下分组查询:group by
可能在进行多表连结完之后再进行分组完之后,对于初学者来说可能会犯蒙,并且只有select指定的字段时才能查看其图,但是我们对它原本分组之后的构造却不清晰,这里我们来讲解一下。
首先第一点:分组之后select的字段的条件:
1.必须是group by 分组字段
2.聚合函数(count/avg/distinct)
这一点很重要,查询其他的字段则会报错
第二点:这里我们举例子讲解
用户信息表 user_profile:
| device_id | university |
| 2138 | 北京大学 |
| 3214 | 复旦大学 |
| 6543 | 北京大学 |
| 2315 | 浙江大学 |
| 5432 | 山东大学 |
| 2131 | 山东大学 |
| 4321 | 复旦大学 |
答题情况明细表question_practice_detail,其中question_id是题目编号,result是答题结果。
| device_id | question_id |
| 2138 | 111 |
| 3214 | 112 |
| 3214 | 113 |
| 6543 | 111 |
| 2315 | 115 |
| 2315 | 116 |
| 2315 | 117 |
| 5432 | 118 |
| 5432 | 112 |
| 2131 | 114 |
| 5432 | 113 |
要求:取出学校答过题的用户平均答题数量情况数据
1.首先进行内连接之后再分组
select ? from user_profile as us ,question_practice_detail qpd where us.device_id=qpd.device_id group by university;
那么上方分组后的逻辑到底是怎样的呢?
其实分组之后你不取select特定的字段,它不算一个表,而是四个独立的组,它只是一个逻辑
| 分组名称(university) | 组内包含的所有原始行(共 11 行,按学校拆分) | 组内关键统计(为后续计算用) |
| 北京大学 | 行 1:2138、北京大学、111 行 2:6543、北京大学、111 | 总行数 = 2 去重 device_id 数 = 2 |
| 复旦大学 | 行 3:3214、复旦大学、112 行 4:3214、复旦大学、113 | 总行数 = 2 去重 device_id 数 = 1 |
| 浙江大学 | 行 5:2315、浙江大学、115 行 6:2315、浙江大学、116 行 7:2315、浙江大学、117 | 总行数 = 3 去重 device_id 数 = 1 |
| 山东大学 | 行 8:5432、山东大学、118 行 9:5432、山东大学、112 行 10:2131、山东大学、114 行 11:5432、山东大学、113 | 总行数 = 4 去重 device_id 数 = 2 |
👉 核心理解:
- 分组后不存在 “新的行”,而是把原始行 “归类到不同的组里”;
- 每个组的边界是
university,组内是该学校的所有答题记录; - 数据库执行
group by后,会 “以组为单位” 处理数据(比如用聚合函数统计每组的总行数、去重用户数)。
事务
简介:事务是一组操作的集合,是一个不可分割的单位。它可以把一系列操作作为一个整体向系统发起提交或者撤回。要么同时成功,要么同时失败。
(注意:默认的MySQL是自动提交的,也就是说,当执行一条DML语句,MySQL会隐式提交。)
事务的操作:
查看事务的提交方式:
select @@autocommit;
设置事务的提交方式:
set @@autocommit=0(手动提交,提交是需要commit;)
此处需要注意的是,一但开启手动提交,则当前会话的所有的提交都为手动提交
若只想开启一个临时的事务块,则:
start transaction;(或者begin) ---仅对事务开启后,commit/rollback之前的语句有效
回滚:
rollback;
事务的四大特性:
原子性:事务是不可分割的最小操作单元,要么全部成功,要么全部失败
一致性:事务完成时,必须使所有数据保持一致状态
隔离性:数据库事务的隔离机制,保证事务不受外部并发操作的影响
持久性:一旦提交或者回滚,对数据的操作是永久的
并发事务的问题:
脏读:一个事务读取到另一个事务还没有提交的数据
不可重复读:一个事务先后读取同一条数据,但是两次的数据不一样
幻读:一个事务内多次执行同一范围查询,但两次查询之间,另一个事务提交了插入 / 删除操作,导致查询结果集的行数变化(核心是 “插入 / 删除” 操作引发)
事务隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 并发性能 | 适用场景 |
|---|---|---|---|---|---|
| READ UNCOMMITTED(读未提交) | ✅ | ✅ | ✅ | 最高 | 几乎不用(仅测试) |
| READ COMMITTED(读已提交) | ❌ | ✅ | ✅ | 较高 | 多数 OLTP 场景(Oracle 默认) |
| REPEATABLE READ(可重复读) | ❌ | ❌ | ✅ | 中等 | MySQL 默认(推荐) |
| SERIALIZABLE(串行化) | ❌ | ❌ | ❌ | 最低 | 数据一致性要求极高的场景 |
READ UNCOMMITTED:会出现脏读问题,事务A会读取事务B未commit的数据
READ COMMITTED(读已提交):解决事务的脏读问题。但是会出现不可重复读问题。同样一个SQL在同一个事务中查询不一致。
REPEATABLE READ:解决不可重复读问题。即使事务B对数据库做出操作,但是事务A两次查询结果都一样,只有事务A最后commit才最终修改。但是解决不了幻读问题。
serializable(串行化):解决幻读问题。强行阻塞一个事务A的运行,只有等事务B commit之后才显示SQL结果
-- 查看事务的隔离级别:
select @@transaction_isolation
-- 设置事务的隔离级别
set [session/global] transaction isolation level [read uncommited/read commited/repeatable read/serializable]
存储引擎
MySQL体系结构:
客户端连接器:
是什么:用户(或程序)连接MySQL的“桥梁”,比如Python/PHP/Java代码、Navicat工具、甚至Excel插件。
类比:你用“图书馆APP”“实体借书卡”“微信小程序”借书——不同工具对应不同的“连接方式”(图中的
Native C API、JDBC、ODBC、.NET、PHP、Python等)。作用:把用户的请求(比如“查用户表”)转换成MySQL能理解的格式,传给下一层。
连接层:
包含认证、线程复用、连接限制、内存检查、缓存:
认证:验证“读者身份”——比如你用MySQL账号
root/123456登录,连接池会查“用户表”确认密码对不对。线程复用:避免“每个请求都开新线程”——比如100个读者同时查书,接待处不会开100个新员工,而是用“复用线程”(比如5个员工轮流处理),节省资源。
连接限制:控制“同时进馆人数”——比如MySQL设置
max_connections=100,超过100人就排队,防止服务器崩溃。缓存:存“常用信息”——比如“热门查询的结果”“用户权限”,不用每次都查数据库。
服务层:
这是MySQL的“大脑”,处理所有请求的核心逻辑,包含4个模块:
① SQL接口:“接收读者的问题”——处理所有SQL语句(比如
SELECT/INSERT/UPDATE)、存储过程、视图、触发器。② 解析器:“翻译+权限检查”——把你的SQL翻译成MySQL内部指令(比如
SELECT * FROM user→“查user表的所有列”),还要检查你有没有权限(比如“普通用户不能查管理员表”)。③ 查询优化器:“找最快的找书路线”——比如你要查“年龄>18的用户”,优化器会选“用年龄索引”还是“全表扫描”(就像导航选“地铁”还是“打车”),目标是最快返回结果。
④ 缓存:“常用结果暂存”——如果100个人都查“热门书籍”,缓存会把结果存起来,第101个人查的时候直接返回,不用再查书库。
引擎层:
可插拔存储引擎,是MySQL最核心的“数据存储层”,包含InnoDB、MyISAM、Memory等引擎:
是什么:不同的“书库管理方式”——比如:
InnoDB:支持事务(比如“买东西时,扣钱和加库存必须同时成功/失败”)、行锁(改一行数据时,不影响其他行),是现在的默认引擎(比如淘宝、微信的用户数据都用它)。MyISAM:不支持事务,但查询快(比如以前的论坛帖子,主要是查,不用改),但崩溃后容易丢数据。Memory:数据存在内存里(比如临时统计数据),速度极快,但重启MySQL就没了。
关键特点:可插拔——你可以随时把“文学书库”换成“工具书库”(比如把表引擎从InnoDB改成MyISAM),不用改上面的服务层。
存储层:
包含系统文件和文件日志,是数据的“物理存储”:
- 系统文件:比如
NTFS、ext2/3文件系统(Windows/Linux的磁盘格式),就像图书馆的“书架材质”——用什么方式存文件。 - 文件日志:记录所有操作的“历史账本”,包括:
Redo日志(重做日志):比如你改了一条数据,先写Redo日志,再写数据文件——万一断电,用Redo恢复数据(就像图书馆的“操作记录”,万一书被烧了,能重新买一本)。Undo日志(回滚日志):比如你执行UPDATE没提交,想撤销——用Undo恢复到之前的状态(就像“撤销键”)。Binary日志(二进制日志):记录所有“修改数据的操作”(比如INSERT/UPDATE/DELETE),用来做主从复制(主库的日志传给从库,从库重放操作,保持数据一致,就像图书馆的“副本账本”)。
用“查用户数据”的场景,串起全流程
现在你用Python程序查“id=1的用户姓名”,整个过程是这样的:
客户端连接器:Python通过
JDBC连接MySQL(就像用APP连图书馆)。连接层:连接池验证Python的账号密码(认证),分配一个线程(接待员),检查连接数没超限制。
服务层:
- SQL接口接收请求:
SELECT name FROM user WHERE id=1。 - 解析器翻译SQL,检查你有没有查
user表的权限。 - 查询优化器决定:用
id的索引(如果有的话),比全表扫描快。 - 缓存检查:有没有人之前查过这个id?如果有,直接返回缓存结果。
- SQL接口接收请求:
引擎层:如果缓存没有,InnoDB引擎去存储层找
id=1的记录(就像去书库找书)。存储层:从磁盘文件里读出数据,返回给引擎层,再传给服务层,最后给Python程序。
存储引擎简介:
创建表的时候指定存储引擎:
create table 表名(
字段1,类型,【注释】
...
)engine=innodb [注释]
查看数据库支持的引擎:
show engines;
InnoDB存储引擎:
特点:支持事务(ACID),行级锁(提高并发访问性能),外键(抱持数据的完整性一致性)
文件:xxx.ibd(xxx为表名)
MySIAM存储引擎:MySQL早期默认存储引擎
特点:
不支持事务
不支持行级锁,支持表锁,
没有外键
访问速度快
文件:
xxx.sdi:存储表结构信息
xxx.MYD:存储数据
xxx.MYI:存储索引
Memory存储引擎:表数据存储在内存中,受硬件,断电影响,只能将这些表作为临时表或者缓存使用
特点:
内存存放
hash索引
文件:
xxx.sdi:存储表结构信息
索引:帮助MySQL高效获取数据的数据结构
优点:提高查询效率,排序效率
索引结构:
B+Tree索引:最常见的索引,大部分引擎都支持B+树索引
hash索引:底层数据结构使用hash表实现的,只有精确匹配索引列的查询有效,不支持范围查询(between ,<,>,...)
R-Tree索引(空间索引):MySIAM引擎支持的特殊索引类型,主要用于地理空间数据类型,使用叫较少
Full-text索引(全文索引):是一种建立倒排索引,快速匹配文档的方式
索引的分类:
按照【物理存储】分类(InnoDB专属)
聚集索引(必须有,且只能有一个):将数据存储和索引放到一块,索引结构的叶子节点保留了行数据
二级索引(可以存在多个):将索引和数据分开存储,索引结构的叶子节点关联的对应的主键
聚集索引的选取规则:
如果存在主键,则主键索引就是聚集索引
如果没有主键,则第一个唯一索引(unique)作为聚集索引
如果没有主键,也没有合适的唯一索引,则INnoDB会自动生成一个rowid作为隐藏的聚集索引
最后,我们来讲解一下,到底什么是索引?我们可以想象一下,如果我们没有创建索引,也就是只有一张表,那么我们查找某些数据的时候就是全局扫描,查找效率很慢。但是如果我们对一张表创建了索引的话,索引就是一种数据结构,一种算法,来帮助我们快速检索,提高查找速率。
索引语法
创建索引:
create [unique/fulltext] index 索引名称 on 表名(字段1,...);
(删除索引:drop index 索引名称 on 表名)
查看索引:
show create index from 表名;
删除索引:
drop index 索引名称 on 表名;
索引-性能分析
SQL执行频率
show [global/session] status like 'com_______(七个下划线)';
慢查询日志
查询慢查询日志是否开启:
show variables like 'slow_query_log';
开启MySQL慢查询日志:
show_slow_log=1;
设置慢日志的时间为2秒,如果查询时间超过2秒,则会被记录:
long_query_time=2;
profile详情
show profile能够帮助我们在做SQL优化的时候时间都消耗在哪里,通过have_profile参数,能够查看当前MySQL是否支持profile参数
select @@have_profiling;
select @@profiling;(0表示关闭,1表示开启)
默认的profiling是关闭的,我们可以通过set语句在[global/session]级别开启profiling
set 【global/session】 profiling=1;
查看每一条SQL语句的耗时情况:
show profiles;
查看指定的query_id在各个阶段的耗时情况::
show profile for query query_id;
查看指定的query_id 在cpu的使用情况:
show profile cpu for query query_id;
explain执行计划:
explain/desc可以详细查看select的执行语句,包括如何连接和连接顺序
直接在select语句前加上explain执行语句
索引的使用
最左前缀法则
如果使用了联合索引,就要遵守最左前缀法则,即查询从索引的最左侧的字段开始,并且不跳过某一列,如果跳过了,则会部分索引失效(后面的字段索引失效)
注意:最左侧的列必须存在,如果不存在,则全部的索引失效
不能在索引列上进行运算操作 ,否则索引将失效
字符串索引使用时,不加引号,索引失效
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
所以,在业务允许的情况下,尽可能的使用类似于>=或<=这类的范围查询,而避免使用>或<
尾部模糊查询,索引不会失效,首部模糊查询,索引会失效
如果用or的连接条件,只要有一侧没有索引,则整个索引都会失效
数据评估影响:如果MySQL优化器在评估我们的sql语句时,使用索引比全表更慢,则不使用索引
SQL提示:是优化数据库的重要手段,通过加入一些人为提示来达到SQL优化的目的
user index 建议使用
explain select * from 表名 use index(索引名称) where ...
ignore index 不使用
explain select * from 表名 ignore index(索引名称) where ...
force index 强制使用
explain select * from 表名 force index(索引名称) where ...
覆盖索引:
在查询的时候,尽量使用覆盖索引,即查询的字段在索引都都可以找到,避免使用*查询
前缀索引:
当字段类型为字符串的时候,有时候需要索引很长的字符串,会让索引变得很大,查询时浪费磁盘I/O资源。此时可以只取字符串的一部分作为前缀,节省索引空间,提高索引效率
语法:create index 索引名称 on 表名(字段(n));
索引长度可根据选择性决定。选择性越高,索引效率越高。唯一索引的选择性是1,性能最好
select count(distinct 字段 )/count(*) from 表名 ;
select count(distinct substring(字段名,起始位置,截至位置))/count(*) from 表名;
SQL优化
插入数据
1.批量插入
2.手动提交事务
3.主键顺序插入
如果一次性插入大量数据,使用insert语句效率会很低,此时可以使用MySQL提供的load指令
##客户端连接MySQL服务器时,加上参数--local-infile(MySQL 默认出于安全考虑,禁止客户端读取本地文件并发送给服务器,所以必须加这个参数才能用批量导入功能)
mysql--local-infile -u root -p
##设置全局参数为1,开启从本地读取数据的开关
(查看开关是否开启:select @@local_infile;)
set global local_infile=1;
##执行load指令将准备好的数据加载到表结构中
load data local infile '要读取的文件地址' into table 表名 fileds terminated by '字段之间如何分割' lines terminated by '每行之间如何分割'
主键优化
1.满足业务需求的情况下,尽量降低主键的长度
2.主键顺序插入,选择auto_increment自增
3.尽量不要使用UUID(不重复的无序的随机字符串)或者其他作为主键,比如身份证号
4.业务操作时,避免对主键的修改
order by优化
using filesort:不是利用索引排序的,而且在缓冲区sort_buffer中排序然后再返回
using index:利用索引本身的有序性
1.根据排序字段建立合适的索引,多字段排序时,也需要遵循最左前缀法则
2.尽量使用覆盖索引
3.多字段排序(一个升序,一个降序)此时需要注意联合索引在创建的时候的规则
4.如果不可避免出现filesort,大量数据排序时,可以适当增加缓冲区大小
group by优化
1.在分组时可以建立索引来提高效率
2.分组操作时,索引的使用也满足最左前缀法则
limit优化
如果查询分页的数据特别大时,比如limit 2000000,10,此时MySQL会优先对前200000条数据进行扫描丢弃,,然后返回第20000001到第20000010条记录,查询排序的代价非常大
优化思路:覆盖索引+子查询
count优化
按照效率排序:
count(字段)<count(主键)<count(1)<count(*)
update优化:
InnoDB引擎针对索引加的是行锁,如果更新的字段没有加索引,则会升级为表锁,把整张表都锁住。一旦升级为表锁,则并发性能会降低
视图:
视图是一种虚拟存在的表,行和列数据来自于创建视图时所查询的表,并且是在使用视图时动态生成的。
创建:
create view 视图名称(列名列表) as select语句 【with (cascaded/local) check option】
查询视图:
查看创建视图语句:show create view 视图名称;
查看试图数据:select * from 视图名称;
修改视图:
alter view 视图名称(列名列表) as select语句 【with (cascaded/local) check option】;
replace view 视图名称(列名列表) as select语句 【with (cascaded/local) check option】;
删除视图:
drop view [if exists] 视图名称;
视图-检查选项
with check option:
限制通过视图插入 / 更新的数据必须满足视图的WHERE条件(防止插入 / 更新后数据 “消失” 在视图中)。
cascaded(级联,MySQL 默认):
当视图基于其他视图创建时,会检查本次以及所有底层视图的 WHERE 条件(当前视图 + 所有依赖的视图)。
无with check option(即无 cascaded):
不做任何条件检查,可插入 / 更新不符合视图条件的数据(插入后数据不会出现在视图中,但会写入基表)。
关键规则:即使某个视图没有with check option,但如果它基于的上游视图有check option,那么上游视图的检查规则仍然会生效。
向视图中插入数据,由于视图是不存在的,是基于基表创建的,所以实际是向基表中插入数据
1.cascaded(级联的)
2.global:不会将这个检查效果传递给其他视图
视图:更新及作用
视图的更新:
要是视图可以更新,必须使视图中的行与基础表中的行存在一对一的关系
视图的作用:
简单:简化用户操作。那些经常被使用的操作可以定义为视图,从而用户不必为以后的操作指定全部的条件
安全:通过视图用户只能查看和修改用户权限所能看到的数据
数据独立:视图可以帮助用户屏蔽基础表的变化
锁
锁的本质:
锁是mysql为了解决并发访问数据冲突而设计的机制
锁的分类:
按照锁的粒度分:
全局锁:每次锁定数据库中所有的表
表级锁:只锁定一张表
行级锁:只锁定一行数据
全局锁:
全局锁的主要用途是全库逻辑备份(比如用mysqldump导出全部数据),目的是保证备份过程中数据的一致性。仅允许读,阻塞所有写
加锁:flush tables with read lock;
备份数据:mysqldump -uroot -p(密码)数据库名 > 所要导入的文件名.sql
解锁:unlock tables;
缺点:全局业务锁定,不能进行插入更新,业务基本停摆
代替:在innodb引擎中,我们可以添加参数single-transaction来完成无锁备份(MVCC(多版本并发控制)+一致性快照)
mysqldump --single-transaction -uroot -p(密码)数据库名 > 所要导入的文件名.sql
表级锁:
发生锁冲突的概率最大,并发度最低,应用在myisam,innodb中
分类:
表锁:
表共享读锁(read lock):
只能读,不能写
表独占写锁(write lock):
只有自己可以读和写,其他人不能读写
语法:
加锁:lock tables 表名...read/write;
解锁:unlock tables /断开客户端连接
元数据锁(meta data lock MDL):
隐藏的表级锁:MySQL 5.5 + 默认自动加,无需手动操作,用于保护表结构(防止读数据时表结构被修改)
触发场景:
执行select/insert/update/delete时,自动加 MDL 读锁(注意与全局锁的读锁区分);
执行altertable/drop table时,自动加 MDL 写锁(注意与全局锁的写锁区分);
核心特点:MDL 读锁之间不互斥,但 MDL 写锁会阻塞所有 MDL 读锁(比如改表结构时,所有读 / 写操作都会被阻塞)。
意向锁:
┌─────────────────────────────────────────────────────────┐ 核心特点 │ ├─────────────────────────────────────────────────────────┤ ✅ 表级锁(锁的是整个表,但目的是声明行锁意图) │
│ ✅ 不与行级锁冲突(这是关键!) │
✅ 自动加锁,无需手动干预 │
│ ✅ 提高锁冲突判断效率 │ └─────────────────────────────────────────────────────────┘
主要解决表锁和行锁的冲突问题
意向共享锁(IS):与表级共享锁(read)兼容,与表级排他锁(write)互斥
意向排他锁(IX):与表级共享锁(read)和排他锁(write)都互斥
行锁:
每次操作锁住对应行的数据,锁粒度最低,并发程度最高,发生锁冲突概率最低。innodb专属
innodb的数据是基于索引所致的,行锁是对索引项加锁,而不是对记录加锁
分类:
行锁:
锁定单个行记录,防止其他事务进行update和delete,在repeatable read和repeatable comment隔离级别支持
1.共享锁(S):允许一个读,不允许排他锁
2.排他锁(X):不允许共享锁和排他锁
innodb引擎的行锁是对索引加的锁,如果没有索引,则innodb会升级为表锁
间隙锁:
锁定索引之间的间隙,防止其他事务在这个间隙之间insert,产生幻读。在repeatable read的隔离级别下支持
临键锁:
行锁和间隙锁的组合。在RR隔离级别下支持
InnoDB引擎
逻辑存储结构
架构
缓冲池(Buffer Pool)是什么?
缓冲池就是内存里的一块“临时仓库”。
它用来存放磁盘上的真实数据,这样当你读、改、删数据时,不必每次都去慢慢读硬盘。
操作顺序:
先看缓冲池里有没有数据。
如果没有,就从磁盘读入并缓存。
然后再按一定频率,把修改后的数据刷回磁盘。
好处:减少磁盘操作,提高处理速度。
2. 缓冲池是怎么管理数据的?
缓冲池按页(Page)来管理数据,每页是固定大小的一块数据。
每页可以是三种状态:
空闲页(free page):这页没用过,可以直接存新数据。
干净页(clean page):这页已经用过,但是数据没有改过,和磁盘上的数据是一致的。
脏页(dirty page):这页被修改过了,内存里的数据和磁盘上的不一致,需要以后刷新回磁盘。
3. 图中几个部分
Buffer Pool(缓冲池):存放数据库数据的内存区域。
Change Buffer(变化缓冲区):暂存一些修改操作。
Log Buffer(日记缓冲区):记录数据变化,保证数据安全。
简单说,缓冲池就像内存里的小仓库,帮你快速操作数据,减少慢慢访问硬盘的次数。不同的页状态就是为了知道哪些数据可以直接用,哪些需要更新到硬盘。
事务原理
事务是一组操作的集合,是不可分割的单位。这些操作要么同时成功,要么同时失败
四大特性:
原子性 隔离性 持久性 一致性
redo log:
重做日志。记录用户修改数据的记录,用来实现事务的持久性
该日志文件由两部分组成:
重做日志缓冲(red0 log buffer),重做日志文件(redo log file)。前者储存在内存中,后者储存在磁盘中
undo log:
回滚日志,用来记录数据修改之前的信息。作用包含两个:提供回滚和MVCC(多版本并发控制)