文章851
标签121
分类10

Mysql 中的 utf8mb4、utf8mb4_unicode_ci、utf8mb4_general_ci

1.utf8与utf8mb4(utf8 most bytes 4)

MySQL 5.5.3之后增加了utfmb4字符编码
支持BMP(Basic Multilingual Plane,基本多文种平面)和补充字符
最多使用四个字节存储字符
utf8mb4utf8的超集并完全兼容utf8,能够用四个字节存储更多的字符。

标准的UTF-8字符集编码是可以使用1-4个字节去编码21位字符,这几乎包含了世界上所有能看见的语言。
MySQL里面实现的utf8最长使用3个字符,包含了大多数字符但并不是所有。例如emoji和一些不常用的汉字,如“墅”,这些需要四个字节才能编码的就不支持。

2.字符集、连接字符集、排序字符集

utf8mb4对应的排序字符集有utf8mb4_unicode_ciutf8mb4_general_ci.

utf8mb4_unicode_ciutf8mb4_general_ci的对比:

准确性:

utf8mb4_unicode_ci是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序
utf8mb4_general_ci没有实现Unicode排序规则,在遇到某些特殊语言或者字符集,排序结果可能不一致。
但是,在绝大多数情况下,这些特殊字符的顺序并不需要那么精确。

性能 :
utf8mb4_general_ci在比较和排序的时候更快
utf8mb4_unicode_ci在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。
但是在绝大多数情况下发,不会发生此类复杂比较。相比选择哪一种collation,使用者更应该关心字符集与排序规则在db里需要统一。

记一次MySQL迁移并从MySQL5.6升级到5.7后查询慢了几十倍的问题

起因

因为生产环境数据量越来越大,客户越来越多,项目功能也越来越多,项目本身也越来越多,导致之前的服务器内存、硬盘都已经渐渐的不够用了,当时出现了2种解决方案,增加服务器配置和新购服务器,但是就算是新增硬盘,也需要对数据库进行迁移,所以就采用了新购服务器的方案,并且因为之前是高效云盘,出现过IO占满的情况,所以对于新购的服务器采用了SSD硬盘,理论上速度会飞起来了,实际上我在新服务器上安装MySQL5.7,因为听说MySQL5.7性能提升了N倍,还支持json(虽然对我们没什么用),但是毕竟MySQL8已经出来了,说明MySQL5.7也肯定稳定好了。so,就找了个夜深人静的晚上偷偷的吧数据库迁移过来了,然后开启慢查询日志,限制为5秒,于是开始各种测试,然后查看了一下慢查询日志,一堆慢查询日志,所以有了这篇文章,为什么会出现这么多慢查询,以及如何解决

开始排查

将慢查询SQL拿出来,发现主要的慢查询SQL都是链表查询的语句,也就是说查询语句本身非常复杂,所以就把SQL语句放回之前的数据库执行,发现之前的数据库都是不到1秒就查询出来了,而在新的数据库上最慢能达到140多秒,这明显就不正常了,毕竟新的MySQL服务器无论是CPU、内存、还是硬盘相对于以前的MySQL服务器来说,都是好了不止一星半点,如果说性能差不多还能接受,但是一下子慢了这么多,就明显是有异常了,于是开始挨个排查

排查第一步:配置问题

首先2边服务器的配置文件是一模一样的,因为就是从旧服务器吧配置文件复制过来的,但是因为MySQL的版本不一致,所以怀疑是因为新版有些配置不一样,所以导致的,于是百度了MySQL5.7的优化配置,同时根据机器的内存CPU等硬件情况调整了部分配置,重启MySQL,执行语句,效果不明显,平均下来能快1秒左右,但是这1秒本身也可以认为是查询波动,所以不是配置的问题

排查第二步:硬件问题

首先CPU和内存应该不会有什么问题,唯一可能性就是SSD硬盘的问题,因为之前看到过因为SSD硬盘导致K,V键值存储性能低下,跟机械硬盘完全不在同一个等级上,所以怀疑SSD是否需要开启什么特别的配置才行,于是百度,发现虽然有针对SSD的优化配置,但是也没有因为用了SSD导致速度非常慢的情况,于是针对SSD进行了优化配置,重启MySQL,执行语句,效果微乎其微

排查第三步:语句问题

也是实在没办法了,才想到这个问题,但是我自己都觉得不大可能,而且语句本身也优化的差不多了,小结果集驱动大结果集,索引根据where条件创建等。毕竟就算MySQL升级也不会说改变SQL语法之类的,最多就是在优化SQL的进行了一些特殊处理,所以先查看一下SQL语句的索引执行情况于是desc sql语句查看,跟旧库上面差别

旧库(MySQL5.6)

Xnip2024-03-29_20-52-01.png

新库(MySQL5.7)

Xnip2024-03-29_20-52-39.png

在2个库数据,索引相同的情况下,居然会出现索引引用和命中不一样的情况,所以怀疑是否是迁移数据库的时候导致索引数据被破坏,于是百度去找,还真的发现了一个例子,也是迁移数据库后查询非常慢,后面重建索引之后恢复了,于是准备重建索引,由于表非常多,所以写了一个工具类来重建索引(唯一索引和普通索引,不包含主键索引),核心代码如下:

List<HashMap> list = mapper.select1();  
        HashMap<String,HashMap<String,Object>> temp = new HashMap<>();
        for(HashMap map : list){
            String tableName = map.get("TABLE_NAME").toString();
            String indexName = map.get("INDEX_NAME").toString();
            String nonUnique = map.get("NON_UNIQUE").toString();
            String columnName = map.get("COLUMN_NAME").toString();
            if(temp.containsKey(tableName+"|"+indexName)){
                HashMap<String,Object> value = temp.get(tableName+"|"+indexName);
                List<String> columns = (List<String>) value.get("columns");
                columns.add(columnName);
            }else{
                HashMap<String,Object> value = new HashMap<>();
                value.put("nonUnique",nonUnique);
                List<String> columns = new ArrayList<>();
                columns.add(columnName);
                value.put("columns",columns);
                value.put("indexName",indexName);
                value.put("tableName",tableName);
                temp.put(tableName+"|"+indexName,value);
            }
        }
        List<String> creates = new ArrayList<>();
        List<String> drops = new ArrayList<>();
        for(Map.Entry<String,HashMap<String,Object>> entry:temp.entrySet()){
            String create = null;
            String tableName = entry.getValue().get("tableName").toString();
            String indexName = entry.getValue().get("indexName").toString();
            String nonUnique = entry.getValue().get("nonUnique").toString();
            List<String> columns = (List<String>) entry.getValue().get("columns");
            drops.add("DROP INDEX "+indexName+" ON "+tableName+";");
            if("0".equals(nonUnique)){
                //唯一键索引
                create = "CREATE UNIQUE INDEX "+indexName+" ON "+tableName+" (";
            }else{
                //创建普通索引
                create = "CREATE INDEX "+indexName+" ON "+tableName+" (";
            }
            for(int i = 0;i < columns.size();i++){
                if(i == columns.size() - 1){
                    create += columns.get(i)+");";
                }else{
                    create += columns.get(i)+",";
                }
            }
            creates.add(create);
        }
        for(String str : drops){
            System.out.println(str);
        }
        for(String str : creates){
            System.out.println(str);
        }

查询所有索引SQL代码如下:

select * from information_schema.statistics WHERE INDEX_SCHEMA='xxxx' AND INDEX_NAME<>'PRIMARY'  

其中xxxx是数据库实例名,代码执行完成后将打印出来的SQL语句放进SQL里面执行即可,当然也可以在使用Java调用SQL执行,不过我为了随时观察状况,所以把SQL复制出来执行

重建索引完成后执行SQL语句,发现速度还是没有明显变化,说明不是因为索引数据异常的问题。

检查MySQL5.7新特性

百度查看MySQL5.7有没有更新什么新特性,看到了derived_merge特性,因为derived_merge是MySQL5.7的新的SQL优化方式,所以试着将derived_merge关闭,执行SQL

set GLOBAL optimizer_switch='derived_merge=off'  

执行SQL,发现速度比旧服务器还快,然后用desc查看SQL索引使用情况,跟旧服务器也一样,于是问题解决

关闭derived_merge后的新问题

本来以为关闭derived_merge后就万事大吉了,但是服务器的CPU占满却说明事情没有那么简单,top命令查看服务器CPU占满的原因发现是因为MySQL(肯定是MySQL,毕竟服务器就这一个软件),执行命令:

show full processlist;  

查看卡住的链接信息,发现有大量的视图查询卡住,于是把SQL语句复制出来,发现只是查询单条数据,理论上不会这样慢,为了找出原因,停止测试,重启MySQL,执行视图SQL语句,发现完全卡住几分钟都不能执行完成,强行停止,检查视图的SQL是否有异常,发现视图的SQL也是普通的SQL(4个表的关联查询),理论上来说不会耗费这么久的时间,把创建视图的SQL语句拿出来跟执行的视图的SQL条件拼接起来,用desc查看,发现索引正常命中,于是试着执行一次SQL,结果非常意外,速度非常快,所以以为是服务器发疯,但是为了测试好,就又执行一下视图的SQL,结果为卡死。也就是说视图本身的SQL执行没有任何问题,但是使用视图查询,就会进入卡死状态。于是使用desc

查看视图SQL索引命中情况,发现结果跟直接的SQL不同,下面是对比图:
Xnip2024-03-29_20-55-27.png

视图的索引命中情况明显比视图SQL索引命中多了一个索引,但是为什么会造成卡死呢,原因就在多的那个索引身上,仔细看可以看到,索引命中的行有83141272975行,11位数,上百亿,难怪会卡死,索引命中了上百亿的数据,那跟没有命中索引也没有区别了,而且最为关键的是,我们整个库所有表加起来应该也没有上百亿的数据啊,毕竟目前最大的表数据量也才近千万,所以这个索引肯定是有问题,数据也有问题,但是具体什么问题,就不是我了解的了,毕竟不是专业搞数据库的,所以也希望了解的能帮我解答一下。

知道问题后,感觉解决就简单了,百度搜索了一下MySQL5.7对视图是否进行了优化,但是不管是百度还是谷歌都没有找到合适的答案,毕竟视图本身也只是存储了一个SQL语句而已,并没有保存实际数据,也就是说就算优化也是针对SQL语句本身进行优化,但是SQL语句本身执行又没有任何问题,而且心想MySQL不可能将这么大个bug放出来吧,于是回想之前调过的参数,是否是因为修改了配置导致的,因为之前主要修改特性的配置就derived_merge,所以怀疑是因为derived_merge导致的,于是又打开derived_merge

set GLOBAL optimizer_switch='derived_merge=on'  

执行视图,一切正常

排查第四步:索引命中问题

由于关闭了derived_merge会导致视图查询问题,而系统中用到了很多视图,所以如果不用视图的话需要对系统进行大的改动导致关闭derived_merge不现实,也就只能另想他法了,查询之所以慢的原因主要还是因为索引没有命中导致的,也就是说解决了索引命中的问题,就能解决查询慢的问题,先对比2个库命中的索引,发现主要是链表查询的时候ON后面跟的条件在新库上面没有命中索引,ON后面的条件在主表是跟其他列有组成联合索引的,而被链接的表有部分表是跟其他列组合成联合索引,有些表的列则没有任何索引,于是尝试着在被链接的表创建ON后面的字段单独的索引,创建之后,速度明显快了一倍,但是还是有部分索引没有命中,所以又在主表对ON后面的字段单独创建索引(如果ON后面有几个条件,就创建联合索引),创建完成后,执行语句,秒查询出来,问题解决

尝试在旧库上优化索引

由于新库创建了索引后速度上明细比旧库快了很多,当然跟配置本身也有关系,于是相到再旧库上也创建同样的索引会不会更快了,于是在旧库上创建了跟新库相同的索引,执行SQL语句,比未创建索引之前慢了一倍,查看索引命中情况,虽然命中了更多的索引,但是也导致了命中的索引的行数增加

感悟

MySQL不同的版本有不同的SQL优化器,而且不同的版本可能会出现索引命中规则不同,另外索引并不是越多查询就真的能更快,不合理的索引创建不仅会导致插入慢,还会导致查询变慢,所以了解MySQL索引命中规则和了解所用的MySQL的SQL优化器是有必要的,以及不要轻易更新版本,天知道会出现什么莫名其妙的问题。。。。。

宽表为什么横行? SPL+DQL

宽表为什么横行?SPL+DQL的应用

在大数据时代,数据量和数据复杂性都呈现爆发式增长。为了满足业务需求和分析要求,传统的关系型数据库模型已经难以胜任。在这种背景下,宽表(Wide Table)成为了一种流行的数据建模方式。本文将探讨宽表为什么横行,并介绍一种用于处理宽表的技术——SPL+DQL。

1. 宽表的概念

宽表是指具有大量列的表结构,相对于传统的关系型数据库模型中的纵向表(Tall Table),宽表更加扁平化,将多个属性以列的形式存储在同一张表中。宽表的特点包括:

  • 冗余数据:宽表中可能存在大量冗余数据,因为不同的行可能会拥有相同的属性列。
  • 灵活查询:宽表的扁平结构使得查询更加方便。可以通过单个查询获取多个属性值,而不需要进行多表连接。
  • 高度冗余:宽表的冗余数据可能导致存储空间的浪费,但在某些特定场景下,这种冗余可以提高查询性能。

2. 宽表的优势

为什么宽表横行?宽表的设计有以下几个优势:

  • 简化查询:宽表的扁平结构使得查询更加简单和高效。不需要进行多表连接,可以通过单个查询获取所需的属性值。
  • 提高查询性能:宽表中的冗余数据可以减少查询时的数据访问量,从而提高查询性能。
  • 支持快速分析:宽表中的数据已经按照属性进行了扁平化处理,可以更快速地进行数据分析和挖掘。

3. SPL+DQL的应用

SPL+DQL(Schema Pattern Language + Data Query Language)是一种用于处理宽表的技术。SPL用于定义宽表的模式,包括表的结构、属性和关系。DQL用于查询和操作宽表中的数据。

SPL+DQL的优势在于:

  • 灵活性:SPL+DQL可以根据实际需求定义不同的宽表模式,适应不同的业务场景和数据需求。
  • 高效性:宽表的扁平结构和冗余数据可以提高查询性能,而SPL+DQL可以更好地支持宽表的查询和操作。
  • 易于开发和维护:SPL+DQL提供了一种简单而强大的方式来定义和操作宽表,降低了开发和维护的复杂性。

参考文献

以下是一些相关的参考文献,供进一步学习和了解:

  1. C. Zhang, X. Li, J. Zhang, and P. Zhou, "An optimized query processing method for wide table," 2015 IEEE International Conference on Big Data (Big Data), Santa Clara, CA, 2015, pp. 1850-1857, doi: 10.1109/BigData.2015.7364016.
  2. L. Hu, "Design and Application of Data Storage Scheme for Wide Table," 2019 18th IEEE/ACIS International Conference on Computer and Information Science (ICIS), Okayama, Japan, 2019, pp. 1072-1077, doi: 10.1109/ICIS.2019.8856529.

结论

宽表以其扁平化的设计和冗余数据的存储方式,在大数据时代得到了广泛应用。宽表的横行主要体现在其灵活的查询、高效的性能和简化的数据分析过程中。SPL+DQL作为一种用于处理宽表的技术,提供了灵活性、高效性和易于开发和维护的优势。

">