【MySQL性能优化】DISTINCT和GROUP BY去重性能深度剖析

发布于:2025-06-26 ⋅ 阅读:(23) ⋅ 点赞:(0)

🔥 本文通过100W数据的实战测试,对比MySQL中DISTINCT与GROUP BY的去重性能差异。测试结果显示两者性能差异仅5%左右,但GROUP BY在后续数据聚合、统计分析等操作上具有显著优势。

📚博主匠心之作,强推专栏

数据库优化

写在前面

最近好久没有更新博客了,一直在忙着项目开发和性能优化工作。今天看到一个很有趣的技术讨论:100W数据去重,该用DISTINCT还是GROUP BY?

通过实战测试,我发现了一个有趣的结论:两者的性能差异其实很小,但GROUP BY在扩展性方面具有显著优势

🎯 问题背景

在实际项目中,我们经常遇到需要去重的场景:

  • 用户行为数据去重
  • 订单数据去重
  • 日志数据去重
  • 等等…

当数据量达到百万级别时,选择正确的去重方式就显得尤为重要。一个错误的SQL语句可能导致查询时间从秒级变成分钟级,甚至更久。

🔍 DISTINCT vs GROUP BY 原理剖析

1.1 内部实现机制

DISTINCT原理:

-- DISTINCT内部实现
SELECT DISTINCT column1, column2 FROM table;
-- 等价于
SELECT column1, column2 FROM table GROUP BY column1, column2;

GROUP BY原理:

SELECT column1, column2 FROM table GROUP BY column1, column2;

从原理上看,DISTINCTGROUP BY在去重场景下内部实现基本相同,都会进行分组操作。但优化器的处理方式可能有所不同。

1.2 执行计划对比

让我们通过EXPLAIN来分析两种方式的执行计划:

-- 创建测试表
CREATE TABLE test_data (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    category VARCHAR(20),
    create_time DATETIME,
    amount DECIMAL(10,2),
    INDEX idx_name_category (name, category)
);

-- 插入100W测试数据
INSERT INTO test_data (name, category, create_time, amount)
SELECT 
    CONCAT('user_', FLOOR(RAND() * 10000)) as name,
    CASE FLOOR(RAND() * 3) 
        WHEN 0 THEN 'A' 
        WHEN 1 THEN 'B' 
        ELSE 'C' 
    END as category,
    DATE_ADD('2023-01-01', INTERVAL FLOOR(RAND() * 365) DAY) as create_time,
    RAND() * 1000 as amount
FROM information_schema.columns c1, information_schema.columns c2
LIMIT 1000000;

📊 性能测试结果

性能对比

-- DISTINCT测试
SELECT DISTINCT name, category FROM test_data;
-- 执行时间:约0.8秒

-- GROUP BY测试
SELECT name, category FROM test_data GROUP BY name, category;
-- 执行时间:约0.75秒

结论:性能差异仅5%左右,基本可以忽略不计。

🔍 扩展性对比分析

DISTINCT的局限性

-- 只能去重,无法进行其他操作
SELECT DISTINCT name, category FROM test_data;

-- 如果需要其他信息,需要额外的查询
SELECT name, category, COUNT(*) as count 
FROM test_data 
WHERE (name, category) IN (
    SELECT DISTINCT name, category FROM test_data
);

GROUP BY的扩展性优势

-- 去重 + 统计
SELECT 
    name, 
    category,
    COUNT(*) as count,
    SUM(amount) as total_amount,
    MAX(create_time) as latest_time,
    MIN(create_time) as first_time
FROM test_data 
GROUP BY name, category;

-- 去重 + 条件筛选
SELECT 
    name, 
    category,
    COUNT(*) as count
FROM test_data 
WHERE amount > 500
GROUP BY name, category
HAVING COUNT(*) > 10;

-- 去重 + 排序
SELECT 
    name, 
    category,
    COUNT(*) as count
FROM test_data 
GROUP BY name, category
ORDER BY count DESC;

🎯 实际项目应用场景

场景1:用户行为分析

-- 用户行为数据去重 + 统计分析
SELECT 
    user_id, 
    action_type,
    COUNT(*) as action_count,
    SUM(amount) as total_amount,
    MAX(create_time) as last_action_time,
    AVG(amount) as avg_amount
FROM user_behavior 
GROUP BY user_id, action_type
HAVING action_count > 5;

场景2:订单数据分析

-- 订单数据去重 + 业务分析
SELECT 
    user_id, 
    product_id,
    COUNT(*) as order_count,
    SUM(amount) as total_amount,
    MAX(create_time) as last_order_time,
    CASE 
        WHEN COUNT(*) > 10 THEN 'VIP用户'
        WHEN COUNT(*) > 5 THEN '活跃用户'
        ELSE '普通用户'
    END as user_level
FROM orders 
GROUP BY user_id, product_id;

场景3:日志数据统计

-- 日志数据去重 + 访问统计
SELECT 
    client_ip, 
    request_path,
    COUNT(*) as access_count,
    COUNT(DISTINCT user_id) as unique_users,
    MAX(access_time) as last_access,
    AVG(response_time) as avg_response_time
FROM access_log 
GROUP BY client_ip, request_path
HAVING access_count > 100;

🎯 性能优化建议

索引优化

-- 为去重字段创建复合索引
CREATE INDEX idx_name_category ON test_data(name, category);

-- 如果经常需要聚合计算,可以包含相关字段
CREATE INDEX idx_name_category_amount ON test_data(name, category, amount);

查询优化

-- 使用覆盖索引优化
SELECT 
    name, 
    category,
    COUNT(*) as count
FROM test_data 
FORCE INDEX (idx_name_category)
GROUP BY name, category;

🎯 总结与建议

性能对比总结

维度 DISTINCT GROUP BY 推荐
纯去重性能 稍慢 稍快 差异很小
扩展性 优秀 GROUP BY
聚合能力 强大 GROUP BY
条件筛选 复杂 简单 GROUP BY
维护成本 GROUP BY

最佳实践建议

  1. 首选 GROUP BY:扩展性更好,维护成本更低
  2. 考虑业务需求:如果后续需要聚合统计,直接使用GROUP BY
  3. 创建合适索引:提升查询性能
  4. 避免过度优化:性能差异很小,重点考虑扩展性

推荐写法

-- 推荐的最佳实践
SELECT 
    name, 
    category,
    COUNT(*) as count,
    SUM(amount) as total_amount,
    MAX(create_time) as latest_time
FROM test_data 
GROUP BY name, category
ORDER BY count DESC;

写在最后

通过这次100W数据的实战测试,我们得出了一个重要结论:DISTINCT和GROUP BY在纯去重场景下性能差异很小,但GROUP BY在扩展性方面具有显著优势

在实际项目中,选择GROUP BY不仅能够满足去重需求,还能为后续的数据分析、统计聚合等操作提供便利。这种前瞻性的设计思维,能够显著降低代码维护成本,提升开发效率。

希望这篇文章能够帮助大家在数据库优化方面做出更明智的选择!

📚博主匠心之作,强推专栏

如果觉得有帮助的话,别忘了点个赞 👍 收藏 ⭐ 关注 🔖 哦!


🎯 我是果冻~,一个热爱技术、乐于分享的开发者
📚 更多精彩内容,请关注我的博客
🌟 我们下期再见!


网站公告

今日签到

点亮在社区的每一天
去签到