MySQL SQL 分析 - 参数化查询 vs query cache 功能

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: query cache,  mysql 5 开始附带的一个功能, 与引擎无关, 只与数据查询语法相关。   测试描述: 当前使用中是 MySQL-5.6.14 Linux RHEL6  64 位系统产生环境,  使用 INNODB 引擎, 分配 innodb 2g 内存空间   [root@TiYanPlat ~]# uname -a Linux TiYanPlat 2.6.32

query cache,  mysql 5 开始附带的一个功能, 与引擎无关, 只与数据查询语法相关。

 

测试描述: 当前使用中是 MySQL-5.6.14 Linux RHEL6  64 位系统产生环境,  使用 INNODB 引擎, 分配 innodb 2g 内存空间

 

[root@TiYanPlat ~]# uname -a
Linux TiYanPlat 2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64x86_64 GNU/Linux

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.14    |
+-----------+
1 row in set (0.00 sec)

mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 2147483648 |
+-------------------------+------------+
1 row in set (0.01 sec)

 

Query cache 功能:

利用 qeury_cache_size 定义内存大小, 内存用于把用户 SQL 放入内存中, 包括 SQL 语句, 包括SQL 语句执行的结果

假如下一次查询时使用相同的 SQL 语句, 则直接从内存中获得结果, 不再进行 SQL 分析, 不在进行磁盘 I/O 读数据。加速数据查询返回结果。

 

实现目标,开启 QCACHE 功能, 如 my.cnf 定义

query-cache-size=16777216
query-cache-type=ON

查询数据库中是否使用当前功能

mysql> show status like '%qcache%';
+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 440      |
| Qcache_free_memory      | 12306960 |
| Qcache_hits             | 13176    |
| Qcache_inserts          | 29777    |
| Qcache_lowmem_prunes    | 0        |
| Qcache_not_cached       | 45862    |
| Qcache_queries_in_cache | 2098     |
| Qcache_total_blocks     | 4701     |
+-------------------------+----------+
8 rows in set (0.02 sec)

参数返回结果不再一一详细描述, 自行参考官方文档, 从上返回结果可以看到,使用中的数据库 SQL 命中率 (Qcache_hits) 并不理想,原因与业务有关。

 

SQL 分析一, 使用了 QUERY CACHE 的好处

原理, 利用 EXPLAIN 分析当前 SQL 执行计划, 利用 PROFILE 功能分析当前 SQL 执行计划,过程

执行下面语句进行分析

mysql> explain select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=11024;
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table        | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | tbcrbtnumb0_ | ALL  | NULL          | NULL | NULL    | NULL |180182 | Using where |
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+

1 row in set (0.00 sec)

当前语法执行的是全表扫描,另外,需要从 180182 行中扫描相关结果

 

SQL 分析二, 判断第一次执行该 SQL 时候的执行过程

mysql> set profiling=1;

mysql> select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=11024;

+-------+---------------+---------------+-------------+
| id41_ | business2_41_ | applicat3_41_ | mobile41_   |
+-------+---------------+---------------+-------------+
| 30838 |         11024 | TH20121127229 | 02038688592 |
+-------+---------------+---------------+-------------+
1 row in set (0.30 sec)

mysql> show profile;                                                                                                          +--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000191 |
| Waiting for query cache lock   | 0.000023 |
| init                           | 0.000090 |
| checking query cache for query | 0.000499 |
| checking permissions           | 0.000030 |
| Opening tables                 | 0.000131 |
| init                           | 0.000277 |
| System lock                    | 0.000042 |
| Waiting for query cache lock   | 0.000005 |
| System lock                    | 0.000443 |
| optimizing                     | 0.000364 |
| statistics                     | 0.000107 |
| preparing                      | 0.000059 |
| executing                      | 0.000019 |
| Sending data                   | 0.290067 |
| end                            | 0.000483 |
| query end                      | 0.000169 |
| closing tables                 | 0.000158 |
| freeing items                  | 0.000252 |
| Waiting for query cache lock   | 0.000063 |
| freeing items                  | 0.000305 |
| Waiting for query cache lock   | 0.000015 |
| freeing items                  | 0.000095 |
| storing result in query cache  | 0.000145 |
| cleaning up                    | 0.000330 |
+--------------------------------+----------+
25 rows in set, 1 warning (0.01 sec)

从上面看出, 第一次执行该 SQL, MySQL 需要对 SQL 进行锁缓存,初始化,从缓存中查询是否具备之前缓存过的 SQL,检查用户权限, 表权限,打开表,锁定内存,定制执行计划,执行语句,把数据从磁盘中放入内存中操作,关闭表,锁定数据, 缓存数据等操作, 工作原理与 ORACLE 类似

 

按照 QUERY CACHE 原则, 假如 SQL 语句改变 (tbcrbtnumb0_.business_ring_id=11024) 替换该变量值, 那么该 SQL 会被看作为一个新的 SQL, 这个时候, MySQL 将会对整个 SQL 做一次全新的操作, 如上(黄线标注描述)

 

分析 SQL 三

mysql> select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=11021;
+-------+---------------+---------------+-------------+
| id41_ | business2_41_ | applicat3_41_ | mobile41_   |
+-------+---------------+---------------+-------------+
| 30835 |         11021 | TH20121127259 | 02038688592 |
+-------+---------------+---------------+-------------+
1 row in set (0.34 sec)

mysql> show profile;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000795 |
| Waiting for query cache lock   | 0.000077 |
| init                           | 0.000045 |
| checking query cache for query | 0.000337 |
| checking permissions           | 0.000040 |
| Opening tables                 | 0.000113 |
| init                           | 0.000488 |
| System lock                    | 0.000050 |
| Waiting for query cache lock   | 0.000030 |
| System lock                    | 0.000289 |
| optimizing                     | 0.000512 |
| statistics                     | 0.000278 |
| preparing                      | 0.000078 |
| executing                      | 0.000028 |
| Sending data                   | 0.322662 |
| end                            | 0.004777 |
| query end                      | 0.001703 |
| closing tables                 | 0.000526 |
| freeing items                  | 0.000874 |
| Waiting for query cache lock   | 0.000311 |
| freeing items                  | 0.001809 |
| Waiting for query cache lock   | 0.000105 |
| freeing items                  | 0.000184 |
| storing result in query cache  | 0.000966 |
| cleaning up                    | 0.000678 |
+--------------------------------+----------+
25 rows in set, 1 warning (0.00 sec)

上 SQL二,三结果可以看到, 当 WHERE 条件改变, MySQL 会把这两个 SQL 识别为一个新的 SQL, 需要重新操作。

 

SQL 分析四, 假如我们重新执行 SQL 三操作,看看结果如何?(注意,这个时候 QUERY CACHE 真正发挥作用)

mysql> select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=11021;
+-------+---------------+---------------+-------------+
| id41_ | business2_41_ | applicat3_41_ | mobile41_   |
+-------+---------------+---------------+-------------+
| 30835 |         11021 | TH20121127259 | 02038688592 |
+-------+---------------+---------------+-------------+
1 row in set (0.00 sec)

mysql> show profile;                                                                                                          +--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.001367 |
| Waiting for query cache lock   | 0.000071 |
| init                           | 0.000027 |
| checking query cache for query | 0.000163 |
| checking privileges on cached  | 0.000129 |
| checking permissions           | 0.000386 |
| sending cached result to clien | 0.000164|
| cleaning up                    | 0.000079 |
+--------------------------------+----------+
8 rows in set, 1 warning (0.01 sec)

参考 SQL 分析三,四,数据查询需要使用的时间(绿色标注部分)很明显, SQL 分析四返回速度块了很多,另外,从系统返回的 SQL 分析看出来,系统直接从缓存中返回数据给客户, 没有重复进行 SQL 分析及磁盘 I/O 操作。(蓝色标注部分) 因此, QEURY CACHE 明显加速了 SQL 返回结果。

但必须注意,只有两个 SQL 相同的情况下,才能够获得 QUERY CACHE 的优点。

 

 

参数化查询

参数化查询能够在一定情况下避免了 SQL 注入, 而 ORACLE 也比较推荐使用参数化查询, ORACLE 每次执行 SQL (无论 SQL 是否语法一致)都存在 SQL 分析,

假如SQL语法不一样,则进行硬解析,需要重新定制执行计划

假如SQL语法不一致则进行软解析,避免重复定制执行计划,减少 CPU 消耗,增加 SQL 语句返回时间。

MySQL 官方文档中并没有提出到这点。

 

对 MySQL 进行参数化查询分析

SQL 参数化分析一


mysql> set @num=11204;
mysql> select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=@num;
+-------+---------------+---------------+-----------+
| id41_ | business2_41_ | applicat3_41_ | mobile41_ |
+-------+---------------+---------------+-----------+
| 31051 |         11204 | 1222570       | 85237810  |
| 31052 |         11204 | 1222570       | 82685386  |
| 31053 |         11204 | 1222570       | 82783689  |
| 31054 |         11204 | 1222570       | 82685106  |
| 31055 |         11204 | 1222570       | 38880051  |
+-------+---------------+---------------+-----------+
5 rows in set (0.37 sec)

mysql> show profile;                                                                                                          +--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.001858 |
| Waiting for query cache lock   | 0.000089 |
| init                           | 0.000150 |
| checking query cache for query | 0.001143 |
| checking permissions           | 0.000970 |
| Opening tables                 | 0.000544 |
| init                           | 0.000743 |
| System lock                    | 0.000170 |
| optimizing                     | 0.000332 |
| statistics                     | 0.000293 |
| preparing                      | 0.000134 |
| executing                      | 0.000057 |
| Sending data                   | 0.438626 |
| end                            | 0.000694 |
| query end                      | 0.000221 |
| closing tables                 | 0.000300 |
| freeing items                  | 0.000521 |
| cleaning up                    | 0.000360 |
+--------------------------------+----------+
18 rows in set, 1 warning (0.01 sec)


变量值不变情况下,重复执行该 SQL 语句

mysql> select tbcrbtnumb0_.id as id41_, tbcrbtnumb0_.business_ring_id as business2_41_, tbcrbtnumb0_.application_no as applicat3_41_, tbcrbtnumb0_.mobile as mobile41_ from tb_crbt_numbers_load tbcrbtnumb0_ where 1=1 and tbcrbtnumb0_.business_ring_id=@num;
+-------+---------------+---------------+-----------+
| id41_ | business2_41_ | applicat3_41_ | mobile41_ |
+-------+---------------+---------------+-----------+
| 31051 |         11204 | 1222570       | 85237810  |
| 31052 |         11204 | 1222570       | 82685386  |
| 31053 |         11204 | 1222570       | 82783689  |
| 31054 |         11204 | 1222570       | 82685106  |
| 31055 |         11204 | 1222570       | 38880051  |
+-------+---------------+---------------+-----------+
5 rows in set (0.34 sec)

mysql> show profile;                                                                                                          +--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.001188 |
| Waiting for query cache lock   | 0.000052 |
| init                           | 0.000030 |
| checking query cache for query | 0.001791 |
| checking permissions           | 0.000172 |
| Opening tables                 | 0.000614 |
| init                           | 0.001346 |
| System lock                    | 0.000268 |
| optimizing                     | 0.000626 |
| statistics                     | 0.000674 |
| preparing                      | 0.000439 |
| executing                      | 0.000028 |
| Sending data                   | 0.327278 |

| end                            | 0.000649 |
| query end                      | 0.000217 |
| closing tables                 | 0.000408 |
| freeing items                  | 0.000692 |
| cleaning up                    | 0.000570 |
+--------------------------------+----------+
18 rows in set, 1 warning (0.01 sec)

分析结果

从数据返回时间上看来(蓝色标注), 使用参数化查询,并没有在时间返回上获得优势。

从SQL执行计划上看来(紫色标注), 相同 SQL 语句使用参数化查询,系统同样会重新定制执行计划,产生磁盘I/O, 并没有想 ORACLE 一样获得性能上的优化。

另外,参数化查询是不会在 query cache 内存块中取结果的。

 

可以看做每次使用参数化查询都会被认为是一个全新的 SQL 进行分析, (没有学习到 ORACLE 的精髓,比较失望)

 

注意,使用参数化查询时, 即时使用 SQL_cache  语法, 也无法使用 query cache 功能。

 

常见开发下有几种选择

1. 直接把 SQL 变量值提交至 MySQL API 执行,(可利用 QUERY CACHE 功能,有部分 SQL 能够进行数据加速)但可能会遇到 SQL 注入, 升级程序麻烦。

2. 利用  procudure, function 等功能先吧 SQL 进行打包然后再调用执行, 类似参数化查询, 无法获得 QUERY CACHE 功能, 令程序清晰, 程序升级,修改比较方便, 并且有效防止了 SQL 注入。

 

 

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
12天前
|
SQL 索引
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
|
12天前
|
SQL 大数据 数据挖掘
玩转大数据:从零开始掌握SQL查询基础
玩转大数据:从零开始掌握SQL查询基础
90 35
|
5天前
|
SQL Oracle 关系型数据库
【YashanDB知识库】如何将mysql含有group by的SQL转换成崖山支持的SQL
本文探讨了在YashanDB(崖山数据库)中执行某些SQL语句时出现的报错问题,对比了MySQL的成功执行结果。问题源于SQL-92标准对非聚合列的严格限制,要求这些列必须出现在GROUP BY子句中,而SQL:1999及更高版本允许非聚合列直接出现在选择列中。YashanDB和Oracle遵循SQL-92标准,因此会报错。文章提供了两种解决方法:使用聚合函数处理非聚合列,或将GROUP BY与ORDER BY拆分为两层查询。最后总结指出,SQL-92标准更为严谨合理,建议开发者遵循此规范以避免潜在问题。
|
9天前
|
SQL 缓存 关系型数据库
SQL为什么不建议执行多表关联查询
本文探讨了SQL中不建议执行多表关联查询的原因,特别是MySQL与PG在多表关联上的区别。MySQL仅支持嵌套循环连接,而不支持排序-合并连接和散列连接,因此在多表(超过3张)关联查询时效率较低。文章还分析了多表关联查询与多次单表查询的效率对比,指出将关联操作放在Service层处理的优势,包括减少数据库计算资源消耗、提高缓存效率、降低锁竞争以及更易于分布式扩展等。最后,通过实例展示了如何分解关联查询以优化性能。
|
3天前
|
关系型数据库 MySQL 数据库连接
docker拉取MySQL后数据库连接失败解决方案
通过以上方法,可以解决Docker中拉取MySQL镜像后数据库连接失败的常见问题。关键步骤包括确保容器正确启动、配置正确的环境变量、合理设置网络和权限,以及检查主机防火墙设置等。通过逐步排查,可以快速定位并解决连接问题,确保MySQL服务的正常使用。
102 82
|
2月前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
6天前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
1月前
|
关系型数据库 MySQL 数据库
Docker Compose V2 安装常用数据库MySQL+Mongo
以上内容涵盖了使用 Docker Compose 安装和管理 MySQL 和 MongoDB 的详细步骤,希望对您有所帮助。
193 42
|
11天前
|
SQL 关系型数据库 MySQL
MySQL生产环境迁移至YashanDB数据库深度体验
这篇文章是作者将 MySQL 生产环境迁移至 YashanDB 数据库的深度体验。介绍了 YashanDB 迁移平台 YMP 的产品相关信息、安装步骤、迁移中遇到的各种兼容问题及解决方案,最后总结了迁移体验,包括工具部署和操作特点,也指出功能有优化空间及暂不支持的部分,期待其不断优化。
|
1月前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
150 25