MYSQL学习
一、MySQL是什么
- MySQL 是一个数据管理系统,核心功能是管理数据库,一个 MySQL 环境下可管理多个数据库。
- 遵循 “关系模型”,数据之间可通过 “主键 - 外键” 建立关联,同时支持 ACID 事务特性。
- 其核心功能是存储、组织、管理数据:通过 “数据库 - 表 - 行 - 列” 的层级结构(关系模型),将数据结构化存储(例如用 “用户表” 存储用户信息,“订单表” 存储订单数据),并支持高效的查询、修改、删除等操作。
二、MySQL基本指令
(一)DDL(数据定义语言)
| 操作目的 | SQL语句 | 说明 |
|---|---|---|
| 查看所有数据库 | show databases; | 列出当前 MySQL 环境下的所有数据库 |
| 进入指定数据库 | use 数据库名; | 切换到目标数据库,后续操作基于该数据库 |
| 查看数据库表 | show tables; | 列出当前数据库中的所有表 |
| 新建数据库 | create database 数据库名; | 创建一个新的空数据库 |
| 删除数据表 | drop table 表名; | 注意:此操作会永久删除表,需谨慎执行 |
(二)DML(数据操作语言)
1. 插入数据
-- 1. 指定字段插入(推荐,灵活性高)
insert into 表名 (字段1,字段2,...) value (数据1,数据2,...);
-- 2. 插入所有列数据(需按表中列的顺序填写数据)
insert into 表名 value(数据1,数据2,数据3,...);
-- 3. 批量插入多条数据
insert into 表名
value (数据1-1,数据1-2,...),
(数据2-1,数据2-2,...),
(数据3-1,数据3-2,...);
2. 删除数据
-- 删除指定条件的数据(例:删除 student 表中姓名为“王刚”的记录)
delete from student where name='王刚';
3. 修改数据
-- 修改指定条件的数据(例:修改 student 表中姓名为“李白”的记录,将 age 改为 20)
update student set age=20 where name='李白';
(三)DQL(数据查询语言)
1. 基础查询
-- 1. 查询表中所有数据(不推荐在工作环境使用)
select * from student;
-- 2. 查询指定列数据(例:查询 student 表的 student_id 和 student_name 列)
select student_id, student_name from student;
注意事项:
工作环境中尽量避免使用 * 查询,原因如下:
- 数据安全:SELECT
*会无条件返回表中所有列。可能泄露敏感字段(如密码、手机号); - IO 消耗:会强制数据库读取表中所有列的数据,对于不必要的列会增加磁盘 IO 负担;
- 网络消耗:多余数据传输会占用更多网络带宽。
- 索引失效:无法利用 “覆盖索引” 优化,只能执行 “回表查询”,增加查询耗时。
- 慢查询:大字段拖慢查询。
2. 列别名(AS 用法)
-- 使用 AS 给列起别名(若别名是关键字,可用着重号 `` 包裹)
select student_id as '学生ID', student_name as `name` from student;
- SQL 关键字是数据库系统预定义的、具有特殊含义的单词,用于表示 SQL 语句的结构或操作。
- 着重号是SQL中用于标识标识符的符号,告诉数据库:“包裹的内容是一个自定义名称,而非 SQL 关键字”,避免解析歧义。
3. 条件查询
-- 单条件查询(例:查询 student 表中姓名为“李四”的学生信息)
select student_id, student_name, birthday, gender from student
where student_name='李四';
-- 多条件查询(AND 逻辑:同时满足)
select student_id, student_name, birthday, gender from student
where student_name='李四' and student_id = 9;
-- 多条件查询(OR 逻辑:满足任一)
select student_id, student_name, birthday, gender from student
where student_name='李四' or gender = '女';
4. IN 关键字(匹配多个值)
-- 1. 匹配指定集合中的值(例:查询 student_id 为 1、3、8、10、12 的学生)
SELECT * FROM student WHERE student_id IN (1,3,8,10,12);
-- 2. NOT IN 排除指定集合(例:查询 student_id 不在 1、3、8、10、12 中的学生)
SELECT * FROM student WHERE student_id NOT IN (1,3,8,10,12);
5. 模糊查询(LIKE 用法)
%:匹配任意长度的字符(包括 0 个);_:匹配 exactly 1 个字符。
-- 1. 姓名以“张”开头(例:张三、张伟、张小明等)
SELECT * FROM student WHERE student_name LIKE '张%';
-- 2. 姓名是 3 个字(例:张三丰、李四海等)
SELECT * FROM student where student_name LIKE '___';
-- 3. 姓名是 2 个字且以“张”开头(例:张三、张伟等)
SELECT * FROM student WHERE student_name LIKE '张_';
-- 4. 姓名中包含“张”(例:张三、李张三、张四等)
SELECT * FROM student WHERE student_name LIKE '%张%';
-- 5. 名字中没有“张”字的学生
SELECT * FROM student WHERE student_name NOT LIKE '%张%';
若需要匹配的字符串中本身包含 % 或 _(如 “折扣率 80%”“型号 A_123”),直接使用会被当作通配符解析,需用转义符 \ 声明“这是普通字符,不是通配符”。
-- 解析:`\%` 表示“普通字符 %”,而非通配符 → 匹配“折扣 80%”“优惠 80%”等
SELECT product_name FROM product WHERE product_desc LIKE '%80\%';
注意事项
- 区分大小写(部分数据库)
- MySQL 默认不区分大小写(如
LIKE 'Zhang%'和LIKE 'zhang%'效果一致); - 若需区分大小写,可使用
BINARY关键字:SELECT student_name FROM student WHERE BINARY student_name LIKE 'Zhang%'; -- 仅匹配“Zhang”开头(首字母大写)的姓名
- 性能影响
- 模糊查询(尤其
%开头,如'%三')会导致数据库无法使用索引,只能全表扫描,数据量大时查询速度慢; - 优化建议:尽量用“前缀匹配”(如
'三%'),避免“%开头”的匹配;若需频繁模糊查询,可考虑数据库的“全文索引”(如 MySQL 的FULLTEXT索引)。
- 中文匹配问题
- 确保数据库表的字符集为
utf8mb4或gbk(支持中文),否则可能出现匹配失效; - 示例:若字符集为
latin1(不支持中文),LIKE '王%'可能无法匹配中文“王磊”。
6. 排序(ORDER BY)
- 升序:
ASC(默认,可省略); - 降序:
DESC(需显式指定)。 - 排序字段:可以是表中的列名、表达式(如 SUM(score))或列的别名(如 AS 别名)。
-- 1. 单字段排序(例:按 student_id 降序查询)
SELECT * FROM student ORDER BY student_id DESC;
-- 2. 多字段排序(例:先按 student_name 升序,再按 student_id 降序)
-- 规则:先按第一个字段排序,第一个字段值相同时,再按第二个字段排序,以此类推。
SELECT * FROM student ORDER BY student_name ASC, student_id DESC;
-- 3. 排序字段可以是表达式或函数的计算结果,而非直接使用表中的列。(按 “字符串长度” 排序(使用 LENGTH() 函数))
-- 按姓名长度升序排列(短名字在前,长名字在后)
SELECT student_name FROM student ORDER BY LENGTH(student_name) ASC;
注意事项
- MySQL 中,NULL 被视为 “最小值”,
- 字符串排序默认按字符的 ASCII 码值排序(而非实际语义)
7. 部分查询(LIMIT)
| 语法格式 | 说明 | 适用场景 |
|---|---|---|
LIMIT N | 返回查询结果的前 N 条数据(从第 1 条开始,索引从 0 计算时对应 0~N-1) | 获取 “Top N” 数据(如前 10 条、前 5 条) |
LIMIT offset, N | offset 表示 “起始偏移量”(从 0 开始计数,即第 offset+1 条数据),N 表示 “返回的行数” | 分页查询(如第 2 页、第 3 页数据) |
-- 1. 查询前 3 条数据(起始索引默认 0,可省略)
select * FROM student LIMIT 3; -- 等价于 LIMIT 0, 3
-- 2. 查询第 4-7 条数据(起始索引 3,查询 4 条)
select * from student limit 3, 4;
-- 3. 明确查询前 3 条(显式写起始索引 0,可读性更高)
select * from student limit 0, 3;
注意事项:
- 若结果包含 NULL 值,LIMIT 会正常截取
- LIMIT offset, N 中,offset 是 “起始位置的偏移量”,而非 “起始页码”,且从 0 开始计数:
- offset=0 → 从第 1 条数据开始
- offset=5 → 从第 6 条数据开始
- 错误示例:若想查第 1 页(1~5 条),写 LIMIT 1, 5 会从第 2 条开始,导致第 1 条数据丢失,正确写法是 LIMIT 0, 5 或简化为 LIMIT 5。
- 必须配合 ORDER BY 保证顺序稳定
深度分页
通常表现为 LIMIT 子句中 offset 数值很大。
执行逻辑:
- 数据库需要先扫描并排序前 100000 + 10 = 100010 条数据;
- 丢弃前 100000 条数据,仅返回最后 10 条;
- 随着 offset 增大,扫描和排序的数据量线性增加,磁盘 IO 和内存消耗剧增,查询耗时会大幅上升(可能从毫秒级变为秒级)。
优化方案:
- 基于 “索引有序性” 的定位查询:若表中存在唯一且有序的字段(如自增主键 id、创建时间 create_time 等,需有索引),可通过 “条件过滤” 直接定位到分页起始位置,替代 offset。
- 借助 “覆盖索引” 减少数据扫描:若查询只需部分字段,可创建覆盖索引(包含查询所需的所有字段),让数据库无需访问表数据,直接通过索引返回结果,减少 IO 消耗。
8. 合并查询(UNION / UNION ALL)
| 关键字 | 功能 | 效率 | 适用场景 |
|---|---|---|---|
UNION | 合并多个查询结果,并去重(整体去重) | 较低 | 需避免重复结果时 |
UNION ALL | 合并多个查询结果,不做去重 | 较高 | 允许重复结果,追求效率 |
注意:合并的结果集列数必须相同(列类型可不同)。
-- 格式1:UNION(去重合并)
SELECT 字段1, 字段2 FROM 表1 [WHERE 条件]
UNION
SELECT 字段1, 字段2 FROM 表2 [WHERE 条件];
-- 格式2:UNION ALL(保留重复合并)
SELECT 字段1, 字段2 FROM 表1 [WHERE 条件]
UNION ALL
SELECT 字段1, 字段2 FROM 表2 [WHERE 条件];
注意事项:
- 多个 SELECT 语句返回的字段数量必须相同,否则会报错。
-- 错误:第一个查询返回2个字段,第二个返回1个字段
SELECT id, name FROM student
UNION
SELECT age FROM student;
- 对应位置的字段类型需兼容(如 int 与 float 兼容,varchar 与 text 兼容),否则可能导致数据转换错误或结果不符合预期。
-- 正确:id(int)与 num(int)兼容,name(varchar)与 title(varchar)兼容
SELECT id, name FROM student
UNION
SELECT num, title FROM course;
- 若需对合并后的结果排序或分页,需将合并查询作为子查询,在外层进行处理(ORDER BY 和 LIMIT 需写在最后)。
- UNION 会对整个结果集进行去重(即所有字段完全相同的行才会被视为重复),而非单字段去重。
适用场景
- 合并同一表中不同条件的结果
- 合并不同表的同类数据
- 当查询条件复杂且难以用 OR 合并时,可拆分为多个简单查询再合并。需求:查询 “(年龄 < 18 且成绩> 90)” 或 “(年龄 > 60 且成绩 < 60)” 的学生。
SELECT * FROM student WHERE age < 18 AND score > 90
UNION ALL -- 已知两个条件无重叠,用 UNION ALL 提升性能
SELECT * FROM student WHERE age > 60 AND score < 60;
9. 结果去重(DISTINCT)
-- 例:查询 student 表中不重复的姓名(对结果集去重,非对表中字段去重)
SELECT DISTINCT name FROM student;
(四)嵌套查询与关联查询
1. 嵌套查询(子查询)
定义:一个查询语句中嵌套另一个查询(子查询结果作为主查询的条件或数据源)。
-- 场景1:查询有不及格成绩的学生信息(成绩 < 60 为不及格)
-- 方式一:多表关联 + DISTINCT 去重
SELECT DISTINCT * FROM student,score WHERE score.score < 60 and student.student_id = score.student_id;
-- 方式二:IN 子查询(更简洁)
SELECT student_name
FROM student
WHERE student_id IN (
SELECT DISTINCT student_id
FROM score
WHERE score < 60
);
-- 场景2:查询总成绩最高的学生 ID(子查询作为临时表)
-- 注意:子查询作为表时,必须起别名(例:别名 a)
SELECT *
FROM (
SELECT student_id, SUM(score) as sumscore
FROM score
GROUP BY student_id
) as a
ORDER BY sumscore DESC
LIMIT 1;
2. 关联查询
定义:当查询结果需要从多张表获取时,通过“关联条件”将表连接(常见:左连接、内连接、全外连接)。
-- 1. 左连接(LEFT JOIN)
-- 规则:以左表(student)为主表,显示左表所有数据;右表(score)仅显示匹配关联条件的数据(不匹配则为 NULL)
SELECT s.*, sc.*
FROM student s
LEFT JOIN score sc
ON s.student_id = sc.student_id;
-- 执行逻辑:先将 student 表中的每一行都加入到结果集中,然后在 score 表中查找与 student 表当前行 student_id 匹配的行,
-- 如果找到,就将对应的成绩字段值填入结果集;如果没找到,成绩字段就填入 NULL。
-- 2. 内连接(INNER JOIN)
-- 规则:仅显示两张表中“满足关联条件”的数据(无匹配则不显示)
SELECT s.*, sc.*
FROM student s
INNER JOIN score sc
ON s.student_id = sc.student_id;
-- 执行逻辑:数据库会逐行检查 student 表和 score 表中的记录,
-- 只有当 student 表中的 student_id 与 score 表中的 student_id 相等时,才会将这两条记录对应的字段组合成一行加入到结果集中。
-- 3. 全外连接(FULL JOIN)
-- 说明:MySQL 不原生支持 FULL JOIN,需借助 UNION 合并左连接和右连接结果
SELECT s.*, sc.*
FROM student s
LEFT JOIN score sc
ON s.student_id = sc.student_id
UNION
SELECT s.*, sc.*
FROM student s
RIGHT JOIN score sc
ON s.student_id = sc.student_id;
-- 执行逻辑:先获取左连接的结果,再获取右连接的结果,最后通过 UNION 合并,去除重复行(如果使用 UNION ALL 则保留重复行)。
s.* 表示获取别名为 s 的表中的所有字段。
注意事项:
- SQL 中判断 NULL 值不能用
=,需用IS NULL(判断空)或IS NOT NULL(判断非空)。 - 关联条件应选择能准确反映表之间关系的字段,通常是主键 - 外键关系,否则可能导致结果错误或出现大量冗余数据。
- 在关联条件字段上创建索引可以大大提高关联查询的性能。
- 当涉及三个或更多表的关联查询时,需要合理安排表的连接顺序和关联条件。可以先确定主表,再逐步连接其他相关表,确保逻辑清晰,避免出现数据遗漏或错误。
三、数据库常用数据类型
数值类型
| 类型分类 | 具体类型 | 字节数 | 取值范围(有符号) | 取值范围(无符号 UNSIGNED) | 核心用途 |
|---|---|---|---|---|---|
| 整数类型 | TINYINT | 1 | -128 ~ 127 | 0 ~ 255 | 存储状态(如 0=禁用、1=启用)、性别(0=女、1=男)等微小型整数 |
SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 | 存储年龄(0-120)、少量数据的 ID(如分类 ID) | |
MEDIUMINT | 3 | -8388608 ~ 8388607 | 0 ~ 16777215 | 存储中等规模的 ID(如百万级数据的用户 ID) | |
INT | 4 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 | 最常用,存储常规 ID(如订单 ID、用户 ID)、数量(如商品库存) | |
BIGINT | 8 | -9223372036854775808 ~ 9223372036854775807 | 0 ~ 18446744073709551615 | 存储超大 ID(如分布式系统的全局 ID)、超大规模数据的计数 | |
| 浮点数/定点数 | FLOAT | 4 | 单精度,约 7 位小数精度 | - | 非精确计算场景(如科学数据、温度、湿度) |
DOUBLE | 8 | 双精度,约 15 位小数精度 | - | 较高精度的非精确计算(如身高、体重) | |
DECIMAL(M,D) | M+2 | M=总位数(默认 10),D=小数位(默认 0) | - | 精确计算场景(如金额、税率、价格,例 DECIMAL(10,2) 表示 0.00~9999999.99) |
字符串类型
| 类型分类 | 具体类型 | 长度限制 | 核心特点 | 核心用途 |
|---|---|---|---|---|
| 短字符串 | CHAR(M) | M=1~255(固定长度) | 存储时长度固定,不足补空格;查询效率高 | 固定长度的短文本(如手机号 CHAR(11)、身份证号 CHAR(18)、性别标识) |
VARCHAR(M) | M=1~65535(可变长度) | 存储时按实际内容长度分配空间,节省存储 | 长度不固定的短文本(如姓名 VARCHAR(50)、地址 VARCHAR(255)、邮箱) | |
| 长字符串 | TEXT | 0~65535 字节(约 64KB) | 普通长文本存储 | 短文、评论、商品简介 |
MEDIUMTEXT | 0~16777215 字节(约 16MB) | 中等长度长文本存储 | 文章内容、日志记录 | |
LONGTEXT | 0~4294967295 字节(约 4GB) | 超长文本存储 | 大型文档、视频字幕、海量日志 | |
| 二进制字符串 | BLOB | 0~65535 字节(约 64KB) | 存储二进制数据(如小图片、图标) | 小型二进制文件(实际开发中更推荐存储文件路径,而非二进制本身) |
MEDIUMBLOB | 0~16777215 字节(约 16MB) | 存储中等二进制数据 | 中等大小二进制文件(如压缩包、中等图片) | |
LONGBLOB | 0~4294967295 字节(约 4GB) | 存储超大二进制数据 | 大型二进制文件(如视频片段、高清图片) |
日期与时间类型
| 类型分类 | 具体类型 | 字节数 | 格式 | 取值范围 | 核心特点 | 核心用途 |
|---|---|---|---|---|---|---|
| 日期时间类型 | DATE | 3 | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 | 仅存储日期(年-月-日) | 存储生日、订单日期、活动开始日期 |
TIME | 3 | HH:MM:SS | -838:59:59 ~ 838:59:59 | 仅存储时间(时-分-秒),支持负数(时间差) | 存储时长、打卡时间(如 09:30:00)、时间间隔 | |
DATETIME | 8 | YYYY-MM-DD HH:MM:SS | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 存储日期+时间,不受时区影响 | 存储订单创建时间、用户注册时间、事件发生时间(需精确且固定) | |
TIMESTAMP | 4 | YYYY-MM-DD HH:MM:SS | 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07 | 存储日期+时间,受时区影响;支持自动更新 | 存储最后修改时间(如 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) | |
YEAR | 1 | YYYY | 1901 ~ 2155 | 仅存储年份,节省空间 | 存储出生年份、产品生产年份 |
特殊类型
| 类型分类 | 具体类型 | 长度限制 | 核心特点 | 核心用途 |
|---|---|---|---|---|
| 枚举/集合类型 | ENUM | 1~2 字节(取决于选项数) | 仅能从预定义选项中选择一个值;存储为数字索引 | 存储固定选项的单值(如性别 ENUM('男','女')、订单状态 ENUM('待支付','已完成')) |
SET | 1~8 字节(取决于选项数) | 可从预定义选项中选择多个值(最多 64 个) | 存储固定选项的多值(如兴趣爱好 SET('阅读','运动','音乐')、用户权限 SET('查询','编辑','删除')) |
注意事项:数据库优化之一是“选择合适的数据类型”,例如:
- 存储性别用
tinyint(0=女,1=男)而非varchar; - 存储金额用
decimal而非double,避免精度丢失。
四、事务管理
(一)事务基本操作
事务是“不可分割的工作单位”,操作要么全执行,要么全回滚。MySQL 中事务需依赖 InnoDB 存储引擎(MyISAM 不支持事务)。
| 操作指令 | 功能说明 |
|---|---|
start transaction | 开启事务(后续 DML 操作不会自动提交) |
savepoint 保存点名 | 设置事务保存点(用于回滚到指定步骤,而非全量回滚) |
rollback to 保存点名 | 回滚事务到指定保存点(保存点之后的操作会撤销) |
rollback | 全量回滚事务(撤销所有未提交的操作) |
commit | 提交事务(所有操作永久生效,无法回滚;提交后自动删除所有保存点) |
release savepoint 保存点名称 | 删除保存点(移除已创建的保存点,释放资源) |
savepoint(保存点):
SAVEPOINT 通过自定义的名称设置一个存储点,如果当前事务已经有了一个同名的 SAVEPOINT ,那么旧的将会被替代。
ROLLBACK TO SAVEPOINT 子句可以将当前事务回退到自定义的存储点位置,而不会结束事务, InnoDB 不会释放内存中的行锁(对于新插入的行,其锁信息通过行数据中自带的 “事务 ID” 标识,而不会单独存储在内存中,当回滚到保存点时,这些新插入的行会被直接删除,其携带的锁信息自然消失(相当于释放锁)。), 存储点之后语句都会撤销执行,包括新设置的存储点,也同样会被删除。
RELEASE SAVEPOINT 子句,会从当前的事务 SAVEPOINT 集合中,移除指定名称的 SAVEPOINT 。.
回退事务
当执行回退事务时,通过指定保存点可以回退到指定的点。
提交事务
使用comit语句可以提交事务,当执行了commit语句后,会确认事务的变化、结束事务、删除保存点、释放锁,数据生效。当使用commit语句结束事务之后, 其他会话将可以查看到事务变化后的新数据。
如果不开始事务,默认情况下,DML操作是自动提交的,不能回滚。mysql 的事务需要存储引擎为innodb ,而myisam 不支持。
示例:事务操作流程
-- 1. 创建测试表
create table TEXT(
id int,
name VARCHAR(32)
);
-- 2. 开启事务
start TRANSACTION;
-- 3. 设置保存点 a
SAVEPOINT a;
-- 4. 执行 DML 操作(插入第一条数据)
INSERT INTO TEXT values(100,'tom');
-- 5. 设置保存点 b
SAVEPOINT b;
-- 6. 执行 DML 操作(插入第二条数据)
INSERT INTO TEXT values(200,'jack');
-- 7. 查看当前数据(此时数据未提交,仅当前会话可见)
select * from TEXT; -- 结果:两条数据
-- 8. 回滚到保存点 b(撤销“插入 200,'jack'”的操作)
rollback to b;
select * from TEXT; -- 结果:仅 100,'tom'
-- 9. 回滚到保存点 a(撤销“插入 100,'tom'”的操作)
rollback to a;
select * from TEXT; -- 结果:空表
-- 10. (可选)提交事务(若执行 commit,后续无法回滚)
-- commit;
(二)事务隔离级别
1. 隔离级别作用
多个事务并发访问时,隔离级别用于控制“事务间数据可见性”,避免脏读、不可重复读、幻读等问题。
2. 三类常见问题
| 问题类型 | 定义 |
|---|---|
| 脏读(Dirty Read) | 事务 A 读取了事务 B 尚未提交的修改(若 B 回滚,A 读取的是“无效数据”) |
| 不可重复读(Non-repeatable Read) | 事务 A 同一查询多次执行,因事务 B 提交修改/删除,导致每次结果不同 |
| 幻读(Phantom Read) | 事务 A 同一查询多次执行,因事务 B 提交插入,导致每次结果行数不同 |
3. 隔离级别操作指令
-- 1. 查看当前会话隔离级别
select @@transaction_isolation;
-- 2. 查看系统全局隔离级别
select @@global.transaction_isolation;
-- 3. 设置当前会话隔离级别(例:设置为“读未提交”)
set session transaction isolation level read uncommitted;
-- 4. 设置系统全局隔离级别(例:设置为“读未提交”)
set global transaction isolation level read uncommitted;
4. 4 种隔离级别名称及含义
MySQL 支持 SQL 标准定义的 4 种隔离级别,按隔离程度从低到高排序:
| 隔离级别名称 | 含义 | 解决的问题 |
|---|---|---|
READ UNCOMMITTED(读未提交) | 事务可以读取其他事务未提交的修改(脏读) | 无 |
READ COMMITTED(读已提交) | 事务只能读取其他事务已提交的修改,避免脏读 | 脏读 |
REPEATABLE READ(可重复读) | 事务中多次读取同一数据的结果一致,避免不可重复读(MySQL 默认级别) | 脏读、不可重复读 |
SERIALIZABLE(可序列化) | 最高隔离级别,事务串行执行,避免幻读 | 脏读、不可重复读、幻读 |
(三)事务 ACID 特性
事务必须满足以下 4 个特性,确保数据一致性和可靠性:
- 原子性(Atomicity):事务是一个 “不可分割的最小操作单元”,操作“要么全成,要么全败”;undo log(回滚日志) 保证
- 一致性(Consistency):事务执行前后,数据库从“一个一致性状态”切换到“另一个一致性状态”(例:转账前 A 有 100 元、B 有 50 元,转账后 A 50 元、B 100 元,总金额不变);依赖其他三大特性保证
- 隔离性(Isolation):多个并发事务互不干扰,每个事务看到的数据是“独立的”;依赖 锁机制 和 MVCC(多版本并发控制) 实现
- 持久性(Durability):事务提交后,对数据的修改是永久性的(即使数据库崩溃,数据也不会丢失)。依赖 redo log(重做日志) 实现
五、数据库面试常见问题
1. mb3 和 mb4 有什么区别?
- 核心差异:
mb中m表示max(最大),b表示byte(字节),即mb3一个字符最多占 3 字节,mb4最多占 4 字节; - 功能差异:
mb4比mb3多 1 字节,可支持表情符号(如 😊),而mb3仅支持常规字符(英文 1 字节、阿拉伯语 2 字节、东亚语言 3 字节)。
2. 数据库连接命令是什么?
mysql -h 主机名 -P 端口号 -u 用户名 -p
- 参数说明:
-h:指定 MySQL 服务器的地址(IP 或域名)。-u:指定用户名(例:root是 MySQL 超级用户);-p:提示输入密码(输入命令后按回车,再输入密码即可登录)。
3. varchar 和 char 的区别?
| 对比维度 | VARCHAR | CHAR |
|---|---|---|
| 存储方式 | 可变长度,按实际数据长度存储 | 固定长度,不足时用空格填充至指定长度 |
| 长度限制 | 0-65535 字节(实际受行总长度限制) | 0-255 字节 |
| 存储空间 | 额外占用 1-2 字节存储长度信息(记录实际长度) | 不额外占用空间,直接使用指定长度的空间 |
| 查询效率 | 较低(需计算实际长度) | 较高(直接按固定长度读取) |
| 尾部空格处理 | 存储时会自动截断尾部空格 | 存储时保留尾部空格,查询时会自动截断 |
| 适用场景 | 长度不固定的字符串(如姓名、地址、描述) | 长度固定的字符串(如手机号、身份证号、性别) |
| 示例 | VARCHAR(50) 存储 "张三" 占用 3 字节(含长度标识) | CHAR(50) 存储 "张三" 占用 50 字节(补 48 个空格) |
- VARCHAR 更节省空间,适合存储长度差异较大的数据;CHAR 因长度固定,查询效率更高,但可能浪费空间。
- 尾部空格处理是重要区别:VARCHAR 会自动去除插入时的尾部空格,而 CHAR 会保留空格但查询时自动截断(可能导致匹配问题)。
- 选择时需平衡空间利用率和查询性能,根据数据长度是否固定来决定。
