MySQL字符集不一致导致索引失效的解决办法

 更新时间:2024年04月16日 10:32:05   作者:不剪发的Tony老师  
本文分析了一个由于字符集不一致,导致增加了索引但是无法使用的案例,通过索引进行查找时需要进行数据的比较,字符集不一致时需要使用 convert 函数进行转换,从而导致索引失效,文中有详细的解决办法,需要的朋友可以参考下

问题描述

有个朋友给我发来一个问题,说是他们的系统有几十万用户,某个查询需要 5 秒以上的时间才能返回,同时服务器 CPU 资源占用率将近 100%。这个对于用户的线上操作影响非常大,那么我们就来看看如何分析和解决这个慢查询问题。

为了便于说明问题,我们对表结构进行了简化:

create table customer(
  cid int auto_increment primary key,
  cname varchar(50) not null,
  register_time datetime not null,
  recommender varchar(50) character set utf8
) engine=innodb default charset=utf8mb4;

create unique index uk_customer_cname on customer(cname);

insert into customer(cname, register_time, recommender) values('张三', now(), '');
insert into customer(cname, register_time, recommender) values('李四', now(), '张三'),('王五', now(), '李四');

customer 是用户表,其中 cid 是主键;cname 上有一个唯一索引;recommender 是用户的推荐人。

实际查询涉及了很多表,经过简化之后存在性能问题的语句如下:

select c.*
from customer c
join customer r on (c.recommender = r.cname )
where r.cid = 1
and c.register_time between now() - interval 1 day and now();

大意是查找通过某人推荐,在指定时间段内注册的用户。

问题分析

了解问题之后,首先我让他给我发来了 explain 执行计划:

explain
select c.*
from customer c
join customer r on (c.recommender = r.cname )
where r.cname = '张三'
and c.register_time between now() - interval 1 day and now();

id|select_type|table|partitions|type |possible_keys    |key              |key_len|ref  |rows|filtered|Extra      |
--|-----------|-----|----------|-----|-----------------|-----------------|-------|-----|----|--------|-----------|
 1|SIMPLE     |r    |          |const|uk_customer_cname|uk_customer_cname|202    |const|   1|   100.0|Using index|
 1|SIMPLE     |c    |          |ALL  |                 |                 |       |     |   3|   33.33|Using where|

从结果可以看出,有一个全表扫描(type = ALL)的操作,显然这是因为 recommender 字段上缺少索引。

所以,我们首先为 recommender 字段创建了一个索引:

create index idx_customer_cname on customer(recommender);

之后再次查看了执行计划,结果没有任何变化,创建的索引没有生效。然后我们使用了 show warnings 命令看看有没有更多的信息:

show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select `hrdb`.`c`.`cid` AS `cid`,`hrdb`.`c`.`cname` AS `cname`,`hrdb`.`c`.`register_time` AS `register_time`,`hrdb`.`c`.`recommender` AS `recommender` from `hrdb`.`customer` `c` join `hrdb`.`customer` `r` where ((`hrdb`.`c`.`register_time` between <cache>((now() - interval 1 day)) and <cache>(now())) and (convert(`hrdb`.`c`.`recommender` using utf8mb4) = '张三'))
1 row in set (0.00 sec)

这里有一个问题,就是存在字符集转换:

convert(`hrdb`.`c`.`recommender` using utf8mb4) = '张三')

recommender 需要转换为 utf8mb4 字符集,查看表结构之后发现它的字符集是 utf8,和表中的其他字段字符集不一样。原来他们是从之前的版本迁移过来的表结构,不知怎么会导致遗留一个字段的字符集忘记了调整。

MySQL 支持数据库、表以及字段级别的字符集(Character Set)和排序规则(Collation)。不同字符集支持的字符种类和数量不同,例如 ASCII 字符集只能存储字母、数字和常见的符号,GB2312 和 GB18030 可以支持中文,Unicode 字符集能够支持多国语言;排序规则定义了字符的排序顺序,例如是否区分大小写、是否区分重音、中文按照拼音还是偏旁进行排序等。

接下来就是修改字段的字符集了:

alter table customer modify column recommender varchar(50) character set utf8mb4;

然后,再次查看执行计划的结果如下:

id|select_type|table|partitions|type |possible_keys     |key               |key_len|ref  |rows|filtered|Extra      |
--|-----------|-----|----------|-----|------------------|------------------|-------|-----|----|--------|-----------|
 1|SIMPLE     |r    |          |const|uk_customer_cname |uk_customer_cname |202    |const|   1|   100.0|Using index|
 1|SIMPLE     |c    |          |ref  |idx_customer_cname|idx_customer_cname|203    |const|   1|   33.33|Using where|

在实际环境中优化之后的查询需要 0.1 秒左右,已经完全可以满足业务的需求了。

总结

本文分析了一个由于字符集不一致,导致增加了索引但是无法使用的案例。通过索引进行查找时需要进行数据的比较,字符集不一致时需要使用 convert 函数进行转换,从而导致索引失效。通常在迁移遗留系统时需要特别小心,对于 Unicode 推荐使用最新的 utf8mb4 字符集。

以上就是MySQL字符集不一致导致索引失效的解决办法的详细内容,更多关于MySQL字符集不一致的资料请关注脚本之家其它相关文章!

相关文章

  • FROM_UNIXTIME 格式化MYSQL时间戳函数

    FROM_UNIXTIME 格式化MYSQL时间戳函数

    对MYSQL没有进行过深入的研究,基础知识匮乏,一遇到问题只能手册,看来要把MYSQL的学习安排进时间表了。
    2011-04-04
  • MySQL命令提示符出现输入错误时如何修改前面的命令

    MySQL命令提示符出现输入错误时如何修改前面的命令

    本文主要介绍了MySQL命令提示符出现输入错误时如何修改前面的命令,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • 线上MYSQL同步报错故障处理方法总结(必看篇)

    线上MYSQL同步报错故障处理方法总结(必看篇)

    下面小编就为大家带来一篇线上MYSQL同步报错故障处理方法总结(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Win中安装mysql的详细步骤

    Win中安装mysql的详细步骤

    这篇文章主要为大家详细介绍了Win中安装mysql的详细步骤,文中安装步骤介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • MySQL快速插入大量数据的解决方案和代码示例

    MySQL快速插入大量数据的解决方案和代码示例

    在这篇博客中,我们将深入探讨如何高效插入大量数据到MySQL数据库,无论你是数据库新手还是经验丰富的开发者,这篇文章都将为你提供实用的解决方案和代码示例,帮助你解决插入3万条数据需要20多秒的问题,需要的朋友可以参考下
    2024-08-08
  • mysql给id设置默认值为UUID的实现方法

    mysql给id设置默认值为UUID的实现方法

    由于mysql并不支持默认值为函数类型,给id设值有两种方式,本文主要介绍了mysql给id设置默认值为UUID的实现方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • Mysql中的索引精讲

    Mysql中的索引精讲

    这篇文章主要给大家介绍了关于Mysql中索引的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Mysql具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • MySql中的常用参数查询

    MySql中的常用参数查询

    这篇文章主要介绍了MySql中的常用参数查询,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 使用JDBC在MySQL数据库中如何快速批量插入数据

    使用JDBC在MySQL数据库中如何快速批量插入数据

    这篇文章主要介绍了使用JDBC在MySQL数据库中如何快速批量插入数据,可以有效的解决一次插入大数据的方法,
    2016-11-11
  • 深入讲解数据库中Decimal类型的使用以及实现方法

    深入讲解数据库中Decimal类型的使用以及实现方法

    MySQL DECIMAL数据类型用于在数据库中存储精确的数值,我们经常将DECIMAL数据类型用于保留准确精确度的列,例如会计系统中的货币数据,下面这篇文章主要给大家介绍了关于数据库中Decimal类型的使用以及实现方法的相关资料,需要的朋友可以参考下
    2022-02-02

最新评论