Carry の Blog Carry の Blog
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL8-SOP

    • 第一章 概述
    • 第二章 环境准备
    • 第三章 安装部署规范
    • 第四章 ReplicaSet高可用配置
    • 第五章 监控与日常维护
    • 第六章 故障处理手册
    • 第七章 安全与权限管理
    • 第八章 扩展与升级方案
    • 附录
  • 课程笔记

  • 专题系列
  • 课程笔记
  • MySQL实战45讲
Carry の Blog
2024-07-27
目录

SQL性能差异函数与转换

# SQL性能差异函数与转换

在MySQL数据库中,SQL语句的性能差异往往源于函数使用、数据类型转换等因素。本文将深入分析这些因素对查询性能的影响,并提供相应的优化策略。

# 1. 函数对SQL性能的影响

# 1.1 函数导致的索引失效

-- 问题示例:函数导致索引失效
SELECT * FROM users WHERE YEAR(created_date) = 2024;
-- 等价于
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

-- 优化方案:避免在索引字段上使用函数
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
1
2
3
4
5
6
7

# 1.2 函数计算开销

-- 函数计算开销示例
-- 不好的做法
SELECT UPPER(name), LOWER(email) FROM users WHERE age > 25;

-- 更好的做法(如果需要)
SELECT name, email FROM users WHERE age > 25;
-- 在应用层进行大小写转换

-- 或者使用索引优化
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
-- 优化为
SELECT * FROM users WHERE name = 'John';
1
2
3
4
5
6
7
8
9
10
11
12

# 1.3 复杂函数的性能问题

-- 复杂函数示例
-- 性能较差
SELECT *, 
       CONCAT(first_name, ' ', last_name) as full_name,
       DATE_FORMAT(birth_date, '%Y-%m-%d') as formatted_birth_date
FROM users 
WHERE YEAR(birth_date) = 2000;

-- 优化后
SELECT *,
       CONCAT(first_name, ' ', last_name) as full_name,
       birth_date as formatted_birth_date
FROM users 
WHERE birth_date >= '2000-01-01' AND birth_date < '2001-01-01';
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2. 数据类型转换对性能的影响

# 2.1 字符串与数字转换

-- 问题示例:数据类型转换
SELECT * FROM orders WHERE order_id = '12345';
-- 如果order_id是INT类型,MySQL需要进行类型转换

-- 优化方案:保持数据类型一致
SELECT * FROM orders WHERE order_id = 12345;
1
2
3
4
5
6

# 2.2 字符集转换

-- 字符集转换问题
SELECT * FROM products WHERE product_name = 'iPhone 12';

-- 如果product_name是latin1字符集,而查询是utf8字符集
-- 会产生字符集转换开销

-- 优化方案:保持字符集一致
-- 在查询中明确指定字符集
SELECT * FROM products WHERE CONVERT(product_name USING utf8) = 'iPhone 12';
1
2
3
4
5
6
7
8
9

# 2.3 时间类型转换

-- 时间类型转换示例
-- 不好的做法
SELECT * FROM logs WHERE DATE(log_time) = '2024-01-01';

-- 优化方案:避免函数使用
SELECT * FROM logs WHERE log_time >= '2024-01-01' AND log_time < '2024-01-02';

-- 更好的做法:使用范围查询
SELECT * FROM logs WHERE log_time BETWEEN '2024-01-01' AND '2024-01-01 23:59:59';
1
2
3
4
5
6
7
8
9

# 3. 性能差异分析工具

# 3.1 EXPLAIN分析

-- 使用EXPLAIN分析函数对性能的影响
EXPLAIN SELECT * FROM users WHERE YEAR(created_date) = 2024;
-- 查看type字段是否为ALL(全表扫描)

EXPLAIN SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
-- 查看type字段是否为range(范围扫描)
1
2
3
4
5
6

# 3.2 查询执行时间分析

-- 分析查询执行时间
SET profiling = 1;

SELECT * FROM users WHERE YEAR(created_date) = 2024;
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;  -- 分析第一个查询
SHOW PROFILE FOR QUERY 2;  -- 分析第二个查询
1
2
3
4
5
6
7
8
9

# 3.3 性能监控

-- 监控慢查询
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';
1
2
3
4
5
6

# 4. 函数优化策略

# 4.1 避免在WHERE子句中使用函数

-- 问题示例
SELECT * FROM orders WHERE DAY(order_date) = 15;

-- 优化方案
SELECT * FROM orders WHERE order_date >= '2024-01-15' AND order_date < '2024-01-16';

-- 更好的范围查询
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-15' AND '2024-01-15 23:59:59';
1
2
3
4
5
6
7
8

# 4.2 使用索引优化函数表达式

-- 创建函数索引(MySQL 8.0+)
CREATE INDEX idx_year_created ON users (YEAR(created_date));

-- 或者使用虚拟列
ALTER TABLE users ADD COLUMN year_created INT AS (YEAR(created_date)) STORED;
CREATE INDEX idx_year_created ON users (year_created);
1
2
3
4
5
6

# 4.3 预计算和缓存

-- 预计算优化
-- 不好的做法:每次查询都计算
SELECT *, 
       (price * quantity) as total,
       (price * quantity * tax_rate) as total_with_tax
FROM order_items;

-- 好的做法:预先计算并存储
ALTER TABLE order_items ADD COLUMN total DECIMAL(10,2);
ALTER TABLE order_items ADD COLUMN total_with_tax DECIMAL(10,2);

UPDATE order_items 
SET total = price * quantity,
    total_with_tax = price * quantity * tax_rate;

-- 查询时直接使用
SELECT total, total_with_tax FROM order_items;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5. 数据类型转换优化

# 5.1 字段类型匹配

-- 检查字段类型
DESCRIBE users;
-- 确保查询时使用正确的数据类型

-- 不好的做法
SELECT * FROM users WHERE user_id = '12345';

-- 好的做法
SELECT * FROM users WHERE user_id = 12345;

-- 检查字段类型
SHOW CREATE TABLE users;
1
2
3
4
5
6
7
8
9
10
11
12

# 5.2 转换函数的使用

-- 合理使用转换函数
-- 日期转换
SELECT * FROM orders WHERE order_date = STR_TO_DATE('2024-01-01', '%Y-%m-%d');

-- 数值转换
SELECT * FROM products WHERE price = CAST('99.99' AS DECIMAL(10,2));

-- 字符串转换
SELECT * FROM users WHERE user_name = CONVERT('john', CHAR CHARACTER SET utf8mb4);
1
2
3
4
5
6
7
8
9

# 5.3 字符集优化

-- 检查字符集设置
SHOW CREATE TABLE products;
SHOW VARIABLES LIKE 'character_set%';

-- 优化字符集使用
-- 在连接时指定字符集
SET NAMES utf8mb4;

-- 或者在连接字符串中指定
-- mysql://user:password@host:port/db?charset=utf8mb4
1
2
3
4
5
6
7
8
9
10

# 6. 复杂场景优化

# 6.1 多字段组合查询

-- 问题示例:多字段函数操作
SELECT * FROM users 
WHERE CONCAT(first_name, ' ', last_name) = 'John Smith';

-- 优化方案:使用LIKE或范围查询
SELECT * FROM users 
WHERE first_name = 'John' AND last_name = 'Smith';

-- 或者使用全文索引
ALTER TABLE users ADD FULLTEXT(first_name, last_name);
SELECT * FROM users 
WHERE MATCH(first_name, last_name) AGAINST('John Smith');
1
2
3
4
5
6
7
8
9
10
11
12

# 6.2 子查询中的函数

-- 问题示例:子查询中的函数
SELECT * FROM orders 
WHERE customer_id IN (
    SELECT customer_id FROM customers 
    WHERE YEAR(registration_date) = 2024
);

-- 优化方案:避免函数使用
SELECT o.* FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.registration_date >= '2024-01-01' AND c.registration_date < '2025-01-01';
1
2
3
4
5
6
7
8
9
10
11

# 6.3 CASE语句优化

-- 问题示例:CASE语句中的复杂计算
SELECT 
    CASE 
        WHEN age < 18 THEN 'Minor'
        WHEN age BETWEEN 18 AND 65 THEN 'Adult'
        ELSE 'Senior'
    END as age_group,
    name
FROM users;

-- 优化方案:预计算
ALTER TABLE users ADD COLUMN age_group VARCHAR(20);
UPDATE users SET age_group = 
    CASE 
        WHEN age < 18 THEN 'Minor'
        WHEN age BETWEEN 18 AND 65 THEN 'Adult'
        ELSE 'Senior'
    END;

-- 查询时直接使用
SELECT age_group, name FROM users;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 7. 性能测试与验证

# 7.1 基准测试

-- 创建测试数据
CREATE TABLE test_performance (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_date DATE,
    status VARCHAR(20)
);

-- 插入测试数据
INSERT INTO test_performance (name, created_date, status) 
VALUES 
('User1', '2024-01-01', 'active'),
('User2', '2024-01-02', 'inactive'),
-- ... 更多数据

-- 性能测试
SET @start_time = NOW();
SELECT * FROM test_performance WHERE YEAR(created_date) = 2024;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as function_time;

SET @start_time = NOW();
SELECT * FROM test_performance WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as direct_time;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 7.2 执行计划对比

-- 对比执行计划
EXPLAIN SELECT * FROM users WHERE YEAR(created_date) = 2024;
EXPLAIN SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

-- 查看详细信息
EXPLAIN FORMAT=JSON 
SELECT * FROM users WHERE YEAR(created_date) = 2024;
1
2
3
4
5
6
7

# 8. 最佳实践建议

# 8.1 查询设计原则

-- 1. 避免在WHERE子句中使用函数
-- 2. 保持数据类型一致
-- 3. 合理使用索引
-- 4. 预计算复杂表达式

-- 示例:优化的查询设计
-- 原始查询
SELECT * FROM orders 
WHERE DATE_FORMAT(order_date, '%Y-%m') = '2024-01';

-- 优化后
SELECT * FROM orders 
WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01';
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.2 索引策略

-- 为避免函数索引失效,创建合适的索引
-- 日期范围查询
CREATE INDEX idx_orders_date ON orders (order_date);

-- 多字段查询
CREATE INDEX idx_orders_customer_date ON orders (customer_id, order_date);

-- 复合索引优化
CREATE INDEX idx_users_status_age ON users (status, age);
1
2
3
4
5
6
7
8
9

# 8.3 监控和优化

-- 创建性能监控脚本
DELIMITER //
CREATE PROCEDURE monitor_function_performance()
BEGIN
    DECLARE slow_query_count INT DEFAULT 0;
    
    -- 检查使用函数的慢查询
    SELECT COUNT(*) INTO slow_query_count
    FROM performance_schema.events_statements_history_long 
    WHERE DIGEST_TEXT LIKE '%YEAR(%' 
    OR DIGEST_TEXT LIKE '%MONTH(%' 
    OR DIGEST_TEXT LIKE '%DAY(%';
    
    IF slow_query_count > 0 THEN
        INSERT INTO performance_alert (message, timestamp) 
        VALUES (CONCAT('Found ', slow_query_count, ' queries with date functions'), NOW());
    END IF;
END //
DELIMITER ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 8.4 应用层优化

# Python示例:应用层优化
import datetime

# 不好的做法:在数据库中计算
# query = "SELECT * FROM users WHERE YEAR(created_date) = 2024"

# 好的做法:在应用层计算
current_year = datetime.datetime.now().year
query = "SELECT * FROM users WHERE created_date >= %s AND created_date < %s"
params = (f"{current_year}-01-01", f"{current_year+1}-01-01")
1
2
3
4
5
6
7
8
9
10

# 9. 常见陷阱和避免方法

# 9.1 隐式类型转换

-- 隐式类型转换问题
SELECT * FROM orders WHERE order_id = '12345';
-- 如果order_id是INT类型,会产生隐式转换

-- 明确类型转换
SELECT * FROM orders WHERE order_id = 12345;
1
2
3
4
5
6

# 9.2 字符串比较优化

-- 问题示例
SELECT * FROM users WHERE UPPER(name) = 'JOHN';

-- 优化方案
SELECT * FROM users WHERE name = 'John';
-- 或者创建索引
CREATE INDEX idx_users_name_upper ON users (UPPER(name));
1
2
3
4
5
6
7

# 9.3 空值处理

-- 空值处理的性能考虑
-- 不好的做法
SELECT * FROM users WHERE UPPER(name) IS NOT NULL;

-- 好的做法
SELECT * FROM users WHERE name IS NOT NULL;
1
2
3
4
5
6

# 10. 总结

SQL性能差异主要来源于函数使用和数据类型转换。通过理解这些机制,可以有效优化查询性能。

关键优化策略包括:

  1. 避免在WHERE子句中使用函数
  2. 保持数据类型一致性
  3. 合理使用索引
  4. 预计算复杂表达式
  5. 使用EXPLAIN分析执行计划
  6. 监控慢查询并及时优化

通过遵循这些最佳实践,可以显著提升MySQL查询性能,减少系统资源消耗,提高用户体验。

#学习笔记#MySQL
上次更新: 8/29/2026
最近更新
01
当监控说没事而 DMV 说有事——N9E 与 SQL Server 指标交叉验证实战 原创
08-28
02
TiKV 节点 CPU 周期性打满,进程却只占 4%:一次热点 Region 的逆向排查 原创
08-28
03
托管 SQL Server 的运维边界:哪些 DBA 手段会失效,以及用什么替代 原创
08-28
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式