Mysql_锁定读取

在MySQL中,为了在事务中确保数据安全,可以使用锁定读取来防止其他事务修改查询到的数据。锁定读取分为共享模式锁定和排他模式锁定,用于保护单个表或跨表的数据操作。在事务中,锁定读取的锁定会在提交或回滚时释放,但必须在禁用自动提交的情况下才能使用。这种技术在处理引用完整性、递增计数器等场景时非常关键,能有效避免并发操作导致的问题。
锁定读取

如果查询数据,然后在同一事务中插入或更新相关数据,则常规SELECT 语句不能提供足够的保护。其他事务可以更新或删除刚查询的相同行。 InnoDB支持两种类型的 锁定读取,这些读取提供了额外的安全性:

  • SELECT ... LOCK IN SHARE MODE

    在读取的任何行上设置共享模式锁定。其他会话可以读取行,但是在事务提交之前不能修改它们。如果这些行中的任何一个被尚未提交的另一个事务更改,则查询将等待直到该事务结束,然后使用最新值。

  • SELECT ... FOR UPDATE

    对于索引记录,搜索遇到的情况,锁定行和任何关联的索引条目,就像您UPDATE对这些行发出 语句一样。其他事务被阻止SELECT ... LOCK IN SHARE MODE在某些事务隔离级别更新这些行,执行操作或读取数据。一致的读取将忽略读取视图中存在的记录上设置的任何锁定。(记录的旧版本无法锁定;可以通过在记录的内存副本上应用撤消日志来重构它们 。)

这些子句在处理单个表或跨多个表的树结构或图结构数据时最有用。您从一处到另一处遍历边缘或树枝,同时保留返回的权利并更改这些“ 指针 ”值中的任何一个 。

提交或回滚事务时,将释放 由LOCK IN SHARE MODE和 设置的所有锁定FOR UPDATE

注意

仅当禁用自动提交时(START TRANSACTION通过autocommit以0 开始事务或设置 为0 才可以进行锁定读取) 。

除非在子查询中也指定了锁定读取子句,否则外部语句中的锁定读取子句不会锁定嵌套子查询中表的行。例如,以下语句不会锁定table中的行 t2

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2) FOR UPDATE;

要锁定table中的行,t2请向子查询添加锁定的read子句:

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2 FOR UPDATE) FOR UPDATE;
锁定阅读示例

假设您要在表中插入新行 child,并确保子行在表中具有父行parent。您的应用程序代码可以确保整个操作序列的引用完整性。

首先,使用一致的读取来查询表 PARENT并验证父行是否存在。您可以安全地将子行插入表格 CHILD吗?不可以,因为某些其他会话可能会在您SELECT和您之间的时刻删除父行 INSERT,而您却没有意识到。

为避免此潜在问题,请执行以下 SELECT使用LOCK IN SHARE MODE

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

在之后LOCK IN SHARE MODE的查询返回父'Jones',你可以放心地将孩子记录添加到CHILD表并提交事务。任何试图获取PARENT表中适用行中的排他锁的事务都将 等到您完成操作(即所有表中的数据处于一致状态)后再进行。

再举一个例子,考虑一个表中的整数计数器字段,该字段CHILD_CODES用于为添加到table的每个子代分配唯一标识符 CHILD。不要使用一致读取或共享模式读取来读取计数器的当前值,因为数据库的两个用户可能会看到该计数器的相同值,并且如果两个事务尝试使用以下方式添加行,则会发生重复键错误:与CHILD表相同的标识符。

在这里,LOCK IN SHARE MODE这不是一个好的解决方案,因为如果两个用户同时读取计数器,则其中至少有一个在尝试更新计数器时会陷入死锁状态。

要实现读取和递增计数器,请先使用进行锁定读取FOR UPDATE,然后再递增计数器。例如:

SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;

A SELECT ... FOR UPDATE读取最新的可用数据,并在读取的每一行上设置排他锁。因此,它设置了与搜索的SQL UPDATE 在行上设置的锁相同的锁。

前面的描述只是工作方式的一个示例 SELECT ... FOR UPDATE。在MySQL中,生成唯一标识符的特定任务实际上可以仅通过单次访问表来完成:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();

SELECT语句仅检索标识符信息(特定于当前连接)。它不访问任何表。

更多内容欢迎关注我的个人公众号“韩哥有话说”,100G人工智能学习资料,大量后端学习资料等你来拿。

韩哥有话说

<think>我们面对的是两个独立的问题: 1. 无法打开某些日志文件(被占用):ProgramData\MySQL\Data\WIN-K0DG8GA83V4-slow.log 和 #innodb_redo\#ib_redo16640 2. 创建压缩文件时提示“文件名、目录名或卷标语法不正确” 根据问题描述,结合引用资料,我们逐一解决: ### 问题1:无法打开日志文件(另一个程序正在使用) **原因分析**: - MySQL服务运行时,会持续写入日志文件(如慢查询日志slow.log)和重做日志(ib_redo*),导致文件被独占锁定[^1]。 - 引用[1]提到,MySQL的慢查询日志可以记录到文件(FILE)或表(TABLE)中。当记录到文件时,文件会被MySQL进程锁定[^1]。 - 重做日志(redo log)是InnoDB引擎的核心组件,在数据库运行期间持续被写入,因此不可能在MySQL服务运行时直接访问这些文件[^2]。 **解决方案**: 1. **停止MySQL服务再备份**(推荐): - 在备份前停止MySQL服务,释放所有文件锁。 - 备份完成后重新启动服务。 - *缺点*:会造成数据库短暂不可用。 2. **使用热备份工具**(如Percona XtraBackup): - 引用[2]指出,XtraBackup可以在不关闭服务器的情况下复制普通文件,它通过利用InnoDB的重做日志(REDO LOG)来保证备份的一致性[^2]。 - 适用于InnoDB引擎,对于MyISAM表则需要短暂锁定。 3. **调整日志配置**(仅适用于慢查询日志): - 将慢查询日志记录方式从文件改为表(修改`log_output='TABLE'`),避免生成文件锁[^1]。 - 但重做日志无法通过配置避免锁定。 针对您的具体文件: - `WIN-K0DG8GA83V4-slow.log`:可通过停止服务或改用表记录方式避免锁定。 - `#innodb_redo\#ib_redo16640`:必须使用热备份工具(如XtraBackup)或在服务停止后备份。 ### 问题2:压缩文件时提示“文件名、目录名或卷标语法不正确”(路径:E:\MySQL_Backup\20251020\mysql_backup_20251020.rar) **原因分析**: - RAR压缩命令中指定的路径包含非法字符或格式错误。最常见的原因是: a) 路径中包含特殊字符(如`<`, `>`, `|`, `:`等) b) 路径拼接错误导致出现无效字符(如多余的点号、斜杠等) c) 路径长度超过Windows限制(260字符) - 观察路径:`E:\MySQL_Backup\20251020\mysql_backup_20251020.rar` 表面看似正常,但注意您的错误提示中出现了两个路径: - `E:\MySQL_Backup\20251020\mysql_backup_20251020.rar` - `E:\MySQL_Backup\20251020.rar` 说明脚本中可能同时尝试创建了这两个文件,后者(20251020.rar)位于`E:\MySQL_Backup`目录下,而目录名`20251020`作为压缩包名没有扩展名,这可能是合法的,但前者在目录`20251020`下创建压缩包,这个目录是否存在? **解决方案**: 1. **检查目标目录是否存在**: - 确保`E:\MySQL_Backup\20251020\`目录已经存在,否则WinRAR无法创建压缩包。 - 在备份脚本中添加目录创建命令: ```batch if not exist "%BACKUP_DIR%\20251020" mkdir "%BACKUP_DIR%\20251020" ``` 2. **检查路径中的特殊字符**: - 日期格式`20251020`是纯数字,不会有问题。 - 但注意:在Windows中,文件名不能以空格开头或结尾,也不能包含`:*?"<>|`等字符。 - 确保`BACKUP_DIR`变量(即`E:\MySQL_Backup`)没有尾随空格(常见于字符串拼接错误)。 3. **正确转义路径**: - 在WinRAR命令行中,路径应使用双引号包裹: ```batch "C:\Program Files\WinRAR\Rar.exe" a -r "E:\MySQL_Backup\20251020\mysql_backup_20251020.rar" "源文件路径" ``` 4. **避免路径拼接错误**: - 脚本中可能错误地拼接了路径。例如: ```batch set ARCHIVE_FILE=%BACKUP_DIR%\%DATE_STR%\mysql_backup_%DATE_STR% ``` 压缩时使用: ```batch "%WINRAR_PATH%" a "%ARCHIVE_FILE%.rar" ... ``` 这样生成的完整路径是:`E:\MySQL_Backup\20251020\mysql_backup_20251020.rar`,正确。 - 如果出现`E:\MySQL_Backup\20251020.rar`,说明`ARCHIVE_FILE`变量被错误地设置为`E:\MySQL_Backup\20251020`(缺少子目录和文件名前缀),需检查变量定义。 ### 综合修复脚本建议 结合两个问题,修改备份脚本: ```batch @echo off setlocal enabledelayedexpansion :: 停止MySQL服务(释放文件锁) net stop MySQL80 >nul 2>&1 :: 设置日期格式(示例:20251020) for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "datetime=%%a" set DATE_STR=%datetime:~0,4%%datetime:~4,2%%datetime:~6,2% :: 备份目录设置(注意不带末尾反斜杠) set BACKUP_DIR=E:\MySQL_Backup set TEMP_DIR=%BACKUP_DIR%\temp_%DATE_STR% :: 创建备份子目录(以日期命名) set TARGET_DIR=%BACKUP_DIR%\%DATE_STR% if not exist "%TARGET_DIR%" mkdir "%TARGET_DIR%" :: 执行mysqldump(此时数据库已停止,可以安全访问文件) :: 注意:这里假设您还需要备份日志文件,请根据实际路径调整 mysqldump -u root -pYourPassword --all-databases > "%TEMP_DIR%\full_backup.sql" :: 复制日志文件(在服务停止状态下) copy "C:\ProgramData\MySQL\Data\WIN-K0DG8GA83V4-slow.log" "%TEMP_DIR%" :: 重做日志文件位于数据目录下的#innodb_redo子目录,但服务停止后可以访问 robocopy "C:\ProgramData\MySQL\Data\#innodb_redo" "%TEMP_DIR%\#innodb_redo" /E :: 重新启动MySQL服务 net start MySQL80 >nul 2>&1 :: 压缩备份文件 set WINRAR_PATH="C:\Program Files\WinRAR\Rar.exe" set ARCHIVE_FILE=%TARGET_DIR%\mysql_backup_%DATE_STR% %WINRAR_PATH% a -r "%ARCHIVE_FILE%.rar" "%TEMP_DIR%\*" :: 删除临时文件 if exist "%TEMP_DIR%" rmdir /S /Q "%TEMP_DIR%" ``` ### 注意事项 1. **服务重启风险**:确保脚本中的MySQL服务名称(如`MySQL80`)与实际一致。 2. **备份完整性**:停止服务会导致短暂中断,确保业务允许。 3. **重做日志备份必要性**:通常,备份重做日志没有意义,因为它们在崩溃恢复时使用,且备份后很快会被覆盖。建议只备份数据文件和二进制日志等。 如果必须在线备份,请使用XtraBackup替代脚本备份方案[^2]。 §§相关问题§§ 1. 如何在不停机的情况下备份MySQL数据库? 2. 哪些MySQL文件在运行时会被锁定? 3. 如何正确配置MySQL慢查询日志? 4. Windows路径名有哪些限制? 5. 使用WinRAR命令行时如何避免路径错误? </think>### 解决MySQL备份文件操作问题的完整方案 针对您遇到的MySQL备份文件操作问题,我将分步解决两个核心问题: #### 🔒 问题1:无法打开日志文件(另一个程序正在使用) **根本原因**: 这些文件(`WIN-K0DG8GA83V4-slow.log`和`#ib_redo16640`)是MySQL服务运行时持续写入的核心文件: 1. **慢查询日志**:由MySQL服务锁定写入[^1] 2. **重做日志**:InnoDB引擎的核心事务文件,运行时持续更新[^2] $$ \text{文件锁定状态} = \begin{cases} \text{slow.log: } & \texttt{MySQL服务独占写入} \\ \text{\#ib_redo*: } & \texttt{InnoDB事务日志持续更新} \end{cases} $$ **解决方案**: 1. **停止服务再备份(推荐简单方案)** ```batch :: 备份前停止MySQL服务 net stop MySQL80 timeout /t 5 >nul :: 执行文件复制操作 robocopy "C:\ProgramData\MySQL\Data" "E:\Backup\logs" *.log /S /R:3 /W:5 robocopy "C:\ProgramData\MySQL\Data\#innodb_redo" "E:\Backup\redo" /S /R:3 /W:5 :: 备份完成后重启服务 net start MySQL80 ``` 2. **使用热备份工具(专业生产方案)** - 采用[XtraBackup](https://www.percona.com/software/mysql-database/percona-xtrabackup)工具: ```bash xtrabackup --backup --target-dir=/path/to/backup \ --datadir="C:\ProgramData\MySQL\Data" ``` - 原理:通过读取重做日志保证备份一致性[^2] - 优势:无需停止服务,备份速度提升$3-5\times$ 3. **配置变更(慢查询日志专用)** 修改`my.ini`配置文件: ```ini [mysqld] slow_query_log = 1 log_output = TABLE # 将日志写入mysql.slow_log表而非文件[^1] ``` #### 📁 问题2:无法创建RAR文件(语法不正确) **根本原因分析**: ```mermaid graph LR A[语法错误] --> B[路径特殊字符] A --> C[非法文件名] A --> D[路径长度超限] B --> B1[20251020含中文符号?] C --> C1[#innodb_redo含#号] D --> D1[路径超260字符] ``` **解决方案**: 1. **路径规范处理** ```batch :: 原始错误路径 set BAD_PATH=E:\MySQL_Backup\20251020\mysql_backup_20251020.rar :: 修复方案:移除特殊字符并缩短路径 set SAFE_PATH=E:\MySQLBak\20251020\bak.rar ``` 2. **RAR命令优化** ```batch :: 错误示例(含特殊文件夹名) rar a "E:\MySQL_Backup\20251020\mysql_backup_20251020.rar" "#innodb_redo" :: 正确写法:路径引号+短名称 rar a "E:\MySQLBak\20251020\bak.rar" "C:\MySQL\Data\redo" ``` 3. **自动路径清洗函数** ```batch :clean_path set "str=%~1" set "str=%str::=_%" set "str=%str:\=_%" set "str=%str:#=_%" set "clean=%str%" goto :eof :: 使用示例 call :clean_path "#innodb_redo" echo 清洗后路径: %clean% >> backup.log ``` #### 🔧 完整备份脚本优化 ```batch @echo off setlocal enabledelayedexpansion :: 步骤1:停止MySQL服务 net stop MySQL80 || ( echo [错误] 无法停止MySQL服务 >> backup.log exit /b 1 ) :: 步骤2:创建安全备份路径 set "BACKUP_ROOT=E:\MySQLBak" set "DATE_STR=%date:~0,4%%date:~5,2%%date:~8,2%" md "%BACKUP_ROOT%\%DATE_STR%" 2>nul :: 步骤3:复制日志文件(服务停止后安全操作) robocopy "C:\ProgramData\MySQL\Data" "%BACKUP_ROOT%\%DATE_STR%\logs" *.log /S robocopy "C:\ProgramData\MySQL\Data\#innodb_redo" "%BACKUP_ROOT%\%DATE_STR%\redo" /S :: 步骤4:重启MySQL net start MySQL80 :: 步骤5:安全压缩(使用清洗后的路径) set "ARCHIVE=%BACKUP_ROOT%\%DATE_STR%\bak.rar" "c:\Program Files\WinRAR\rar.exe" a -m5 -ep1 "%ARCHIVE%" "%BACKUP_ROOT%\%DATE_STR%" :: 步骤6:验证备份 if exist "%ARCHIVE%" ( echo [成功] 备份创建于 %ARCHIVE% >> backup.log ) else ( echo [错误] 压缩文件创建失败 >> backup.log ) ``` #### 📊 关键预防措施 1. **路径长度控制** - Windows路径最大长度:$MAX_{PATH}=260$字符 - 解决方案:使用`\\?\`前缀扩展至32,767字符 ```batch rar a "\\?\E:\超长路径...\backup.rar" ... ``` 2. **文件占用检测** ```batch :: 使用Sysinternals工具检测 handle64.exe -accepteula -nobanner "C:\ProgramData\MySQL\Data" > locks.txt findstr /i "mysql" locks.txt && ( echo [警告] 文件被MySQL进程锁定 >> backup.log ) ``` 3. **备份验证机制** ```batch :: RAR完整性验证 "c:\Program Files\WinRAR\rar.exe" t "%ARCHIVE%" if errorlevel 1 ( echo [危急] 备份文件损坏 >> backup.log ) ``` > **推荐工具**:[Percona XtraBackup](https://www.percona.com/software/mysql-database/percona-xtrabackup) 或 [MySQL Enterprise Backup](https://www.mysql.com/products/enterprise/backup.html) 可彻底解决文件锁定问题[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值