MySQL补充知识点学习

MySQL基本概念及表设计等知识点学习

书接上文:MySQL关系型数据库学习,继续看书补充MySQL知识点学习。

1. 基本概念学习

1.1 游标(Cursor)

MySQL 游标是一种数据库对象,它允许应用程序逐行处理查询结果集,而不是一次性获取所有结果。游标在需要逐行处理数据或执行复杂业务逻辑时非常有用。

游标的主要作用和功能

  1. ​​逐行处理查询结果​​
  • 游标允许应用程序按需获取查询结果的每一行,而不是一次性加载所有数据
  • 特别适合处理大量数据时避免内存溢出
  1. ​​支持复杂的业务逻辑处理​​
  • 允许在结果集上执行复杂的业务逻辑,如条件判断、计算、更新等
  • 可以在处理每一行时执行不同的操作
  1. ​​实现逐行更新或删除​​
  • 可以结合游标对查询结果中的每一行执行更新或删除操作
  • 这在需要基于当前行内容决定如何处理下一行时非常有用
  1. ​​支持存储过程中的流程控制​​
  • 在存储过程中使用游标可以实现更复杂的流程控制
  • 可以结合条件判断、循环等语句实现复杂的业务逻辑
  1. ​​提供灵活的数据访问方式​​
  • 允许应用程序以编程方式控制数据的访问和处理
  • 可以暂停、继续或重新开始数据处理

游标的基本使用步骤

在MySQL中,游标通常与存储过程一起使用,基本使用步骤如下:
1.​​ 声明游标​​:定义要处理的查询
2.​​ 打开游标​​:执行查询并准备结果集
3.​​ 获取数据​​:逐行获取结果集中的数据
4.​​ 处理数据​​:对每一行执行所需的操作
5.​​ 关闭游标​​:释放游标资源

游标使用的具体示例

示例1:基本游标使用

DELIMITER //

CREATE PROCEDURE process_employees()
BEGIN
    -- 1. 声明游标
    DECLARE emp_cursor CURSOR FOR 
        SELECT id, name, salary FROM employees;
    
    -- 2. 声明异常处理变量
    DECLARE done INT DEFAULT FALSE;
    DECLARE emp_id INT;
    DECLARE emp_name VARCHAR(100);
    DECLARE emp_salary DECIMAL(10,2);
    
    -- 3. 声明异常处理程序
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- 4. 打开游标
    OPEN emp_cursor;
    
    -- 5. 循环获取数据
    read_loop: LOOP
        FETCH emp_cursor INTO emp_id, emp_name, emp_salary;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- 6. 处理数据(这里只是打印,实际可以执行其他操作)
        -- 注意:MySQL存储过程中不能直接打印,这里只是示意
        -- 实际应用中可以执行更新、插入等操作
        -- SELECT CONCAT('Processing employee: ', emp_name) AS message;
    END LOOP;
    
    -- 7. 关闭游标
    CLOSE emp_cursor;
END //

DELIMITER ;

-- 调用存储过程
CALL process_employees();

示例2:结合条件判断和更新

DELIMITER //

CREATE PROCEDURE update_salaries()
BEGIN
    -- 1. 声明游标
    DECLARE emp_cursor CURSOR FOR 
        SELECT id, name, salary FROM employees WHERE status = 'active';
    
    -- 2. 声明异常处理变量
    DECLARE done INT DEFAULT FALSE;
    DECLARE emp_id INT;
    DECLARE emp_name VARCHAR(100);
    DECLARE emp_salary DECIMAL(10,2);
    
    -- 3. 声明异常处理程序
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- 4. 打开游标
    OPEN emp_cursor;
    
    -- 5. 循环获取数据
    read_loop: LOOP
        FETCH emp_cursor INTO emp_id, emp_name, emp_salary;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- 6. 处理数据:如果工资低于5000,则增加10%
        IF emp_salary < 5000 THEN
            UPDATE employees SET salary = salary * 1.1 
            WHERE id = emp_id;
        END IF;
    END LOOP;
    
    -- 7. 关闭游标
    CLOSE emp_cursor;
END //

DELIMITER ;

-- 调用存储过程
CALL update_salaries();

游标的特点和限制

特点:
1.​​逐行处理​​:可以逐行访问查询结果
2.​​灵活控制​​:可以控制数据处理的流程和逻辑
3.​​支持复杂逻辑​​:可以在处理每一行时执行复杂的业务逻辑
4.​​与存储过程结合​​:通常与存储过程一起使用

限制:
1.​​性能开销​​:游标会带来额外的性能开销,特别是在处理大量数据时
2.​​内存使用​​:虽然比一次性加载所有数据更节省内存,但仍会占用资源
3.​​只能用于存储过程​​:MySQL中的游标只能在存储过程中使用
4.​​不能直接用于应用程序​​:应用程序不能直接使用MySQL游标,必须通过存储过程间接使用
5.​​锁定问题​​:游标可能会锁定查询结果集中的行,影响并发性能

游标的适用场景

1.​​需要逐行处理大量数据​​:当数据量很大,一次性加载所有数据会导致内存问题时
2.​​需要基于当前行内容决定如何处理下一行​​:如复杂的业务逻辑处理
3.​​需要执行逐行更新或删除​​:根据当前行的内容决定如何处理其他行
4.​​需要实现复杂的流程控制​​:在存储过程中需要复杂的条件判断和循环
5.​​需要与外部系统交互​​:如逐行读取数据并发送到外部系统进行处理

游标与批量处理的比较

在这里插入图片描述

最佳实践

1.​​仅在必要时使用游标​​:优先考虑批量处理,只有在确实需要逐行处理时才使用游标
2.​​优化游标查询​​:确保游标使用的查询是高效的
3.​​限制游标处理的数据量​​:只处理必要的数据,避免不必要的数据处理
4.​​考虑替代方案​​:对于简单的批量操作,考虑使用批量更新或删除语句
5.​​测试性能​​:在生产环境使用前测试游标的性能影响
6.​​及时关闭游标​​:确保在不再需要时关闭游标,释放资源

MySQL游标是一个强大的工具,但应该谨慎使用,因为它会带来性能开销。在大多数情况下,批量处理是更好的选择,只有在确实需要逐行处理复杂逻辑时才使用游标。

1.2 事务(transaction)

将多个操作作为一个整体来处理的功能称为“事务”(transaction)。将开启事务之后的处理结果反馈到数据库的操作称为“提交”(commit),不反映到数据库中而恢复成原来的状态的操作称为“回滚”。

自动提交

默认情况下,也就是不手动开启事务时,MySQL的处理都是直接被提交的。也就是说,所有的操作都会自动执行commit;语句。这种功能被称为“自动提交”(auto commit)。

使用范围

下面这些操作是无法还原的,小伙伴们一定要记住。

  1. drop database
  2. drop table
  3. drop view
  4. alter table

事务的属性

事务有很严格的定义,必须同时满足4个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这4个属性通常又被简称为“ACID”特性。

  1. 原子性:事务作为一个整体来执行,所有操作要么都执行,要么都不执行;
  2. 一致性:事务应确保数据库从一个一致状态转变为另一个一致状态;
  3. 隔离性:当多个事务并发执行时,一个事务的执行不影响其他事务的执行;
  4. 持久性:事务一旦提交,它对数据库的修改应该永久保存在数据库中;

1.3 表的设计原则

介绍一些常用的小技巧,以设计出更好的表,主要包括以下5个方面。

  1. 对于一个表的主键,我们一般是使用自动递增的值,而不是手动插入值;
  2. 如果一个字段只有两种取值,比如“男”或“女”、“是”或“否”,比较好的做法是使用tinyint(1)类型,而不是使用varchar等类型。当然,使用varchar等类型也是没有问题的。
  3. 如果想要保存图片,我们一般不会将图片保存到数据库中,这样会占用大量的空间。一般是将图片上传到服务器,数据库中保存的则是图片的地址(URL)。
  4. 对于一篇文章,数据库一般保存的是包含该文章的HTML代码,也叫作“富文本”。一般我们会使用富文本编辑器编辑内容,然后获取对应的HTML代码,而将该HTML代码保存到数据库中。
  5. 设计表时,应该给所有的表和字段添加对应的注释。这个好习惯一定要养成,这样可以使后期的维护工作更加轻松、简单。

1.4 mysql软删除是什么

MySQL 软删除(Soft Delete)详解

软删除是一种数据管理策略,它不是真正从数据库中物理删除记录,而是通过标记记录为"已删除"状态来保留数据。这与硬删除(直接从数据库中移除记录)形成对比。

为什么使用软删除?
数据恢复:可以轻松恢复误删的数据

审计追踪:保留完整的历史记录用于审计或分析

外键约束:避免因删除记录而破坏外键关系

数据分析:可以分析被删除的数据模式

如何实现软删除

添加"删除标记"列

最常见的方法是添加一个布尔类型的列(如is_deleted)或时间戳列(如deleted_at):

ALTER TABLE your_table ADD COLUMN is_deleted TINYINT(1) DEFAULT 0;

– 或者

ALTER TABLE your_table ADD COLUMN deleted_at DATETIME NULL;

更新查询逻辑

在所有查询中需要排除已删除的记录:

– 使用 is_deleted 列

SELECT * FROM your_table WHERE is_deleted = 0;

– 使用 deleted_at 列

SELECT * FROM your_table WHERE deleted_at IS NULL;

实现删除操作

更新记录而不是删除:

– 使用 is_deleted 列

UPDATE your_table SET is_deleted = 1 WHERE id = 123;

– 使用 deleted_at 列

UPDATE your_table SET deleted_at = NOW() WHERE id = 123;

恢复已删除的记录

– 使用 is_deleted 列

UPDATE your_table SET is_deleted = 0 WHERE id = 123;

– 使用 deleted_at 列

UPDATE your_table SET deleted_at = NULL WHERE id = 123;

高级实现方式

使用触发器自动处理

可以创建触发器在删除操作时自动更新标记:

CREATE TRIGGER before_delete_trigger
BEFORE DELETE ON your_table
FOR EACH ROW
BEGIN
    UPDATE your_table SET is_deleted = 1, deleted_at = NOW() WHERE id = OLD.id;
    -- 取消实际的删除操作
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Use soft delete instead';
END;

不过这种方法不太推荐,因为它会干扰正常的DELETE操作流程。
使用视图过滤已删除记录

创建一个只显示未删除记录的视图:

CREATE VIEW active_records AS
SELECT * FROM your_table WHERE is_deleted = 0;
-- 或者
CREATE VIEW active_records AS
SELECT * FROM your_table WHERE deleted_at IS NULL;

然后应用程序可以查询这个视图而不是原始表。

使用数据库扩展或ORM功能

许多ORM框架(如Laravel的Eloquent、Django ORM等)内置了软删除支持:
Laravel Eloquent:

  // 模型中添加
  use SoftDeletes;
  
  // 查询时会自动排除已删除记录
  $records = Model::all();
  
  // 恢复记录
  $record->restore();

Django:

    from django.db import models
  
  class MyModel(models.Model):
      # ...
      is_deleted = models.BooleanField(default=False)
      
      class Meta:
          abstract = True
      
      def delete(self, using=None, keep_parents=False):
          self.is_deleted = True
          self.save()

注意事项

索引优化:为is_deleted或deleted_at列添加索引以提高查询性能

备份策略:虽然数据未被物理删除,但仍需定期备份

存储空间:长期积累的"已删除"数据会占用存储空间,可能需要定期归档

查询习惯:开发人员需要养成总是排除已删除记录的习惯

软删除是一种简单有效的数据管理策略,特别适合需要保留历史记录或允许数据恢复的场景。

<think>好的,我现在需要帮用户总结MySQL事务的学习内容和关键知识点。用户之前已经问过类似的问题,我之前给出了一个结构化的回答,现在用户再次提问,可能希望得到更详细的信息或者补充。首先,我需要回顾之前的回答和用户提供的引用内容,确保信息准确且全面。 首先,检查用户提供的引用内容。引用1提到了存储过程和函数中的事务处理,引用2提到了表的10个关键知识点,包括事务。引用3详细解释了事务的基本概念,引用4讨论了隔离级别和脏读等问题,引用5涉及锁机制和封锁协议。这些引用内容应该整合到回答中,确保覆盖所有相关知识点。 用户的上一个问题中,我的回答已经涵盖了ACID特性、隔离级别、锁机制等内容。现在需要结合引用材料,补充可能遗漏的部分。例如,引用3提到事务是作为单元的一组数据库操作,成功则提交,失败则回滚,这需要强调在ACID的原子性部分。引用4详细说明了脏读和隔离级别的关系,特别是InnoDB默认的隔离级别是可重复读,这点需要补充到隔离级别的部分。引用5提到了锁机制和封锁协议,特别是三级封锁协议,这可以添加到锁机制的章节中,说明如何通过锁协议解决不同的问题。 接下来,用户可能希望了解更深入的内容,比如事务的实现原理,如MVCC、redo/undo日志等,这些在之前的回答中没有提到,但引用材料中没有直接涉及。不过根据我的知识,这些是事务的重要组成部分,应该包含在内,以提供更全面的总结。 另外,用户提供的引用5提到更新丢失和封锁协议,这部分需要整合到锁机制中,说明不同锁协议的应用场景。例如,一级封锁协议解决写操作,二级解决读脏数据,三级解决不可重复读。这些内容可以补充到锁机制部分,增强深度。 还要注意用户可能对事务的实际应用和常见问题感兴趣,比如死锁处理、性能优化等,这些可以作为实际应用中的关键点加入。同时,引用3提到事务用于管理insert、update、delete语句,这可以放在事务基础概念里。 需要确保回答结构清晰,分点明确,每个部分都有足够的细节,同时引用相关材料中的内容。例如,在隔离级别部分,引用4提到脏读发生的条件和InnoDB的默认隔离级别,应该明确写出。在锁机制中,引用5的三级封锁协议需要详细说明。 最后,生成相关问题时要覆盖用户可能进一步提问的方向,比如隔离级别的选择、死锁处理、事务与日志的关系等,确保问题具有引导性,帮助用户深入学习。 总结步骤: 1. 整合之前回答的结构,确保涵盖ACID、隔离级别、锁机制、事务控制语句等。 2. 补充引用中的细节,如事务定义、隔离级别默认值、锁协议。 3. 添加事务的实现原理,如MVCC、日志系统。 4. 结合实际应用场景,如死锁处理、性能优化。 5. 生成相关问题,引导用户深入探索。</think>### MySQL事务学习内容及关键知识点总结 --- #### 一、事务基础概念 1. **定义** 事务是一组原子性数据库操作的逻辑单元,保证所有操作要么全部成功(提交),要么全部失败(回滚)。例如: ```sql BEGIN; UPDATE account SET balance = balance - 100 WHERE user_id = 1; UPDATE account SET balance = balance + 100 WHERE user_id = 2; COMMIT; -- 若中间出错则执行ROLLBACK ``` 事务常用于管理`INSERT`、`UPDATE`、`DELETE`语句,确保数据一致性[^3]。 2. **适用场景** - 银行转账(需同时完成扣款和收款) - 订单支付(库存减少与订单状态更新需同步) - 批量数据处理(部分失败需整体回滚) --- #### 二、ACID特性 | 特性 | 说明 | 实现机制 | |--------------|----------------------------------------------------------------------|---------------------------------------| | **原子性** | 事务不可分割,所有操作要么全部提交,要么全部回滚 | `Undo Log`记录操作前的数据状态[^1] | | **一致性** | 事务执行前后,数据库必须满足业务规则(如唯一约束、外键约束) | 由应用逻辑和数据库约束共同保证[^3] | | **隔离性** | 并发事务之间互不干扰 | 通过锁机制和`MVCC`(多版本并发控制)[^4] | | **持久性** | 事务提交后,修改永久保存 | `Redo Log`确保数据写入磁盘[^1] | --- #### 三、事务隔离级别与并发问题 MySQL InnoDB默认隔离级别为**可重复读(Repeatable Read)**,通过`MVCC`实现[^4]。不同隔离级别解决的问题如下: | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能代价 | |-------------------|------|------------|------|----------| | 读未提交 | ❌ | ❌ | ❌ | 最低 | | 读已提交 | ✅ | ❌ | ❌ | 低 | | **可重复读** | ✅ | ✅ | ❌ | 中 | | 串行化 | ✅ | ✅ | ✅ | 最高 | - **脏读**:读到其他事务未提交的数据(通过`Read Committed`解决)[^4] - **不可重复读**:同一事务内多次读取同一数据结果不同(通过`Repeatable Read`解决) - **幻读**:同一事务内查询结果集的行数变化(通过`间隙锁`或`Serializable`解决)[^5] --- #### 四、锁机制与封锁协议 1. **锁类型** - **共享锁(S锁)**:允许读,阻止写(`SELECT ... LOCK IN SHARE MODE`) - **排他锁(X锁)**:禁止其他事务读写(`SELECT ... FOR UPDATE`)[^5] - **意向锁**:表级锁,快速判断表中是否存在行锁(如`IS锁`、`IX锁`) 2. **三级封锁协议** - **一级**:写数据前加X锁,事务结束释放(解决丢失更新) - **二级**:一级基础上,读数据前加S锁,读完即释放(解决脏读) - **三级**:一级基础上,读数据前加S锁,事务结束释放(解决不可重复读)[^5] 3. **死锁处理** - 超时机制:`innodb_lock_wait_timeout`(默认50秒) - 死锁检测:`innodb_deadlock_detect=ON`(自动回滚代价小的事务) --- #### 五、事务控制与优化 1. **事务控制语句** ```sql START TRANSACTION; -- 或 BEGIN SAVEPOINT savepoint_name; -- 设置保存点 ROLLBACK TO savepoint_name; -- 回滚到保存点 COMMIT; -- 提交事务 ``` 2. **性能优化策略** - **短事务**:减少锁持有时间 - **合理索引**:避免行锁升级为表锁(如非索引字段条件查询)[^4] - **批量操作**:使用`LIMIT`分批次提交,降低锁冲突风险 --- #### 六、高级实现原理 1. **MVCC(多版本并发控制)** - 通过`Read View`和`Undo Log`实现非锁定读 - 每个事务看到的数据版本由事务ID(`trx_id`)决定 2. **日志系统** - **Redo Log**:记录物理修改,用于崩溃恢复(保证持久性) - **Undo Log**:记录逻辑修改,用于回滚和MVCC(保证原子性) --- ### 相关问题 1. **如何选择事务隔离级别?** - 读已提交 vs 可重复读的实际场景区别是什么? 2. **死锁产生的条件及排查方法?** - 如何通过`SHOW ENGINE INNODB STATUS`分析死锁日志? 3. **MVCC如何避免幻读?** - 可重复读级别下,间隙锁与MVCC的协作机制? 4. **Redo Log与Binlog的区别与联系?** - 为什么需要两阶段提交(2PC)保证日志一致性?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草莓仙生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值