mysql 存储过程执行慢_MySQL存储过程因为字符集不同导致执行慢

本文分析了一个线上存储过程执行缓慢的问题,通过对比独立执行的DELETE语句,发现字符集不匹配导致索引失效是根本原因,并给出了两种解决方案。

业务反馈线上一个存储过程执行很慢,导致业务超时较多,而存储过程比较简单,就是一个简单的判断逻辑然后delete一条记录,而且delete语句是有索引的。

表结构如下:

CREATE TABLE `table1` (

`BaseName` varchar(255) NOT NULL COMMENT '相对路径名(相对于上层目录)',

`Bucket` mediumtext NOT NULL COMMENT '目录所属Bucket(业务名)',

`DirUuid` varchar(64) NOT NULL COMMENT '父目录Uuid',

`IsDir` tinyint(1) NOT NULL COMMENT '是否是文件夹',

xxx

PRIMARY KEY (`DirUuid`,`BaseName`),

KEY `origin` (`DirUuid`,`OriginBaseName`),

KEY `dir` (`DirUuid`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

存储过程如下:

CREATE DEFINER=`xxx`@`%` PROCEDURE `PDelete`(

IN `base_name` varchar(255),

IN `dir_uuid` varchar(36),

IN `IsDir` tinyint(1))

BEGIN

DECLARE t_error INTEGER DEFAULT 0;

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;

START TRANSACTION;

IF IsDir = 1 THEN

delete from table1 where `DirUuid` = dir_uuid and `BaseName` = base_name;

delete from table2 where `DirUuid` = dir_uuid and `Dir` = base_name;

ELSE

delete from table2 where (`DirUuid` = dir_uuid and `BaseName` = base_name) or (`DirUuid` = dir_uuid and `OriginBaseName` = base_name);

END IF;

IF t_error = 1 THEN

ROLLBACK;

ELSE

COMMIT;

END IF;

select t_error;

END

然后单独调用存储过程:

mysql> call PDelete('5', 'd3b18237-07ee-43bd-be15-aa7b7399f94b', 0);

+---------+

| t_error |

+---------+

|       0 |

+---------+

1 row in set (3.47 sec)

竟然需要3.47s,从存储过程的定义以及表结构来看,怎么也不用3.47s.

当单独拿出delete语句来执行看看:

mysql> delete from table1 where (`DirUuid` = 'd3b18237-07ee-43bd-be15-aa7b7399f94b' and `BaseName` = '5') or (`DirUuid` = 'd3b18237-07ee-43bd-be15-aa7b7399f94b' and `OriginBaseName` = '5');

Query OK, 0 rows affected (0.00 sec)

执行很快 ,和存储过程调用差别了很大。而且存储过程中也没有其他特别的逻辑,没有循序,没有等待,不应该这么慢才对,只有可能耗时的地方就是delete语句,

存储过程中delete和外部单独执行的delete语句,看上去完全一致,但是我们还需要判断,where条件的类型是否一致,会不会是类型不一致导致的执行慢?

然后对比存储存储过程和表结构,where条件给定的类型也是一致的。那还会有什么原因导致执行很慢呢,还有一种情况没考虑到:字符集,如何表的字符集和where条件中值字符集不一致,

也是有可能导致索引失效的,进行导致执行变慢。这里就需要理解一下调用存储过程是如何使用字符集的,在官方介绍中有如下说明:

For character data types, if there is a CHARACTER SET attribute in the declaration, the specified

character set and its default collation is used. If the COLLATE attribute is also present, that collation is

used rather than the default collation.

If CHARACTER SET and COLLATE attributes are not present, the database character set and collation in

effect at routine creation time are used. To avoid having the server use the database character set and

collation, provide explicit CHARACTER SET and COLLATE attributes for character data parameters.

If you change the database default character set or collation, stored routines that use the database

defaults must be dropped and recreated so that they use the new defaults.

The database character set and collation are given by the value of the character_set_database

and collation_database system variables. For more information, see Section 10.1.3.2, “Database

Character Set and Collation”.

如果存储过程中定义参数时,没有指定字符集,会默认读取创建存储过程时的全局变量character_set_server,如果后续变更了字符集,存储过程不会自动变更字符集,

需要删除重新创建存储过程,才能使得新的字符集生效。

由于创建存储过程时,没有指定字符集,因此采用的character_set_server指定的字符集 utf8mb4,那就意味着存储过程传参都是采用utf8mb4,那么存储过程中delete语句中value的字符集

就是utf8mb4,那再看看表的字符集,) ENGINE=InnoDB DEFAULT CHARSET=utf8; 是utf8的,是不兼容utfbmb4的,字符集不同从而导致索引失效,进而导致delete很慢,影响整个存储过程的执行效率。

解决办法很简单,两种方案均可:

1.调整character_set_server 为 utf8,然后重建存储过程即可,不重建的话,不会起作用。存储过程的字符集是以存储过程创建时character_set_server 为准。

2.将表的字符集更改为utf8mb4也可以。

两种方案选择代价较小的进行即可,我们这里由于是线上环境,更改字符集需要重启服务,因此选择转化表的字符集。

反过来,如果存储过程的字符集是utf8,而表的字符集是utf8mb4,那么是不会出现这个问题的,utf8mb4是兼容utf8的,这里需要注意一下。

<think>嗯,用户需要解决MySQL存储过程字符集相关的报错问题。从用户提供的引用内容来看,这个问题主要涉及存储过程中文字符集不匹配导致的数据插入错误或索引失效。 用户可能正在编写包含中文数据的存储过程,遇到了类似"Error 1366"或"无法插入中文"的报错。这类问题通常有四个核心原因需要排查:存储过程本身的字符集声明、连接会话的字符集设置、数据库/表的字符集一致性,以及字符串字面量的隐式转换规则。 参考引用[2]提到的"MySQL存储过程字符集不同索引不生效"和"charset-literal charset-collation-coercibility",需要特别注意字符串字面量的字符集继承规则。根据MySQL官方文档,未显式声明的字符串字面量会继承连接字符集,如果与表字段字符集不一致可能导致索引失效或比较错误。 解决方案应该分层次处理: 首先检查存储过程创建时的字符集设置,可以通过SHOW CREATE PROCEDURE确认。如果未指定,默认会使用数据库字符集,但用户可能需要在CREATE PROCEDURE时显式声明CHARACTER SET utf8mb4。 其次要确保连接会话的字符集一致性,引用[1]提供的SHOW VARIABLES命令可以验证character_set_connection等关键变量。建议在存储过程开头用SET NAMES 'utf8mb4'统一会话设置。 对于表字段字符集,需要确认与连接设置匹配,特别是主表和临时表。引用[3]提到default-character-set=utf8mb4的服务端配置很重要。 最容易被忽视的是字符串字面量的处理。根据用户引用[2]提到的"Character Set Repertoire"规则,在WHERE条件或变量赋值时,应该用CONVERT()函数显式转换字符串,或者使用_N'中文'的写法强制指定字符集。 用户引用[4]的案例很有参考价值,说明存储过程插入中文失败往往源于字符集链断裂。建议用户在存储过程中增加字符集检查语句,类似DECLARE cur_charset VARCHAR(50) DEFAULT @@session.character_set_connection;这样的调试代码。 最后生成的三个相关问题都紧扣用户的使用场景:性能影响对应索引失效问题,迁移方案涉及多环境适配,调试技巧则是实际排错需要的。特别是第二个关于存储过程迁移的问题,考虑到用户可能需要在开发/生产环境同步配置,这点很实用。</think>针对MySQL存储过程字符集相关的报错问题,以下是系统化的解决方案: --- ### **一、核心原因分析** 1. **字符集链断裂** MySQL处理数据需经过`连接层 → 数据库层 → 表层 → 字段层`的字符集转换,任一环节不匹配会导致乱码或报错。 2. **存储过程特殊机制** - 存储过程内字符串字面量**默认继承`character_set_connection`编码** - 若与表字段字符集不一致,可能引发`Error 1366`(非法字符)或索引失效 --- ### **二、解决方案步骤** #### **步骤1:统一字符集环境** ```sql -- 检查全局字符集(需管理员权限) SHOW GLOBAL VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%'; -- 修改存储过程使用的字符集(在创建时指定) DELIMITER $$ CREATE PROCEDURE your_procedure() BEGIN -- 显式设置会话字符集 SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; -- 业务逻辑代码 END $$ DELIMITER ; ``` #### **步骤2:修正字符串字面量** ```sql -- 方法1:强制转换字面量编码 SET @name = CONVERT('中文数据' USING utf8mb4); INSERT INTO your_table (text_column) VALUES (@name); -- 方法2:使用_N前缀(MySQL 8.0+) INSERT INTO your_table (text_column) VALUES (_utf8mb4 '中文数据'); ``` #### **步骤3:验证存储过程字符集** ```sql -- 查看已存在存储过程字符集 SHOW CREATE PROCEDURE your_procedure_name; -- 重建存储过程(指定字符集) DROP PROCEDURE IF EXISTS your_procedure; CREATE PROCEDURE your_procedure() CHARACTER SET 'utf8mb4' -- 关键声明 BEGIN ... END ``` #### **步骤4:检查表字段一致性** ```sql -- 确认表字段字符集 SHOW FULL COLUMNS FROM your_table; -- 修改字段字符集(示例) ALTER TABLE your_table MODIFY text_column VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` --- ### **三、关键配置项** | 配置项 | 推荐值 | 检查命令 | |---------------------------|---------------------|------------------------------------------| | `character_set_client` | utf8mb4 | `SHOW VARIABLES LIKE 'character_set_%';` | | `character_set_connection` | utf8mb4 | | | `character_set_database` | utf8mb4 | | | `collation_connection` | utf8mb4_unicode_ci | | > **注意**:若使用临时表,需显式指定字符集: > ```sql > CREATE TEMPORARY TABLE temp_table (...) > CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; > ``` --- ### **四、调试技巧** 1. **日志追踪** 在存储过程首部添加: ```sql DECLARE cur_charset VARCHAR(50); SET cur_charset = @@session.character_set_connection; SELECT CONCAT('当前字符集:', cur_charset) AS Debug; ``` 2. **逐段隔离** 注释代码块逐步定位报错位置 --- ### **五、典型错误案例** **错误:** `Incorrect string value: '\xE4\xB8\xAD' for column` **原因:** 客户端发送UTF-8数据,但`character_set_connection=latin1` **解决:** 在存储过程入口添加 `SET NAMES utf8mb4;` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值