MySQL的Order by与Group by优化详解!

news2025/5/11 14:02:39

在这里插入图片描述

目录

    • 前言
    • 核心思想:让索引帮你“排好序”或“分好组”
    • Part 1: ORDER BY 优化详解
      • 1.1 什么是 Filesort?为什么它慢?
      • 1.2 如何避免 Filesort?—— 利用索引的有序性
      • 1.3 EXPLAIN 示例 (ORDER BY)
    • Part 2: GROUP BY 优化详解
      • 2.1 什么是 Using Temporary 和 Using Filesort (for GROUP BY)?
      • 2.2 如何避免 Using Temporary 和 Filesort (for GROUP BY)?—— 利用索引的有序性
      • 2.3 EXPLAIN 示例 (GROUP BY)
    • Part 3: 联合索引,同时优化 WHERE, ORDER BY, GROUP BY
    • 总结 📝

🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!

其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等

如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning

前言

你好呀,数据库优化路上的同行们!🚀 当我们在数据库中查询数据时,除了根据 WHERE 条件筛选记录,经常还需要对结果进行排序 (ORDER BY) 或分组聚合 (GROUP BY)。这两个操作看似简单,但一旦数据量上来,它们就可能成为查询的性能瓶颈,导致查询变慢,甚至拖垮整个数据库系统。

为什么 ORDER BYGROUP BY 会慢呢?因为它们通常需要对大量数据进行排序或构建哈希表进行聚合,这个过程可能需要在内存甚至磁盘上进行,消耗大量的 CPU 和 I/O 资源。在 EXPLAIN 的输出中,如果看到 Extra 列出现了 Using filesortUsing temporary,那就要警惕了,这往往是性能问题的信号!🚨

今天,我们就来详细探讨如何通过合理的索引策略,帮助 MySQL 避免这些昂贵的操作,让 ORDER BYGROUP BY 飞起来!✨

核心思想:让索引帮你“排好序”或“分好组”

优化 ORDER BYGROUP BY 的核心思路是一样的:利用索引的有序性。B+tree 索引(MySQL InnoDB 存储引擎的默认索引类型)的一个关键特性就是存储的数据是按照索引列的值有序排列的。如果查询所需的排序或分组顺序恰好与某个索引的顺序一致,MySQL 就可以直接按照索引的顺序读取数据,而无需额外的排序或分组步骤。

Part 1: ORDER BY 优化详解

ORDER BY 子句用于指定结果集的排序方式。如果不能使用索引进行排序,MySQL 就需要执行一个额外的排序步骤,这个过程称为 Filesort

1.1 什么是 Filesort?为什么它慢?

当 MySQL 无法利用索引来满足 ORDER BY 的需求时,它会将查询结果(或者至少是需要排序的列以及用于回表的主键/行指针)读取出来,然后在内存中进行排序。如果内存不足,就会将数据分块,利用磁盘进行“归并排序”。这个过程就是 Filesort。

EXPLAINExtra 列显示 Using filesort,就表示发生了 Filesort。

为什么 Filesort 慢?

  • CPU 消耗: 排序本身是一个计算密集型操作。
  • 内存消耗: 需要分配内存缓冲区来存储待排序的数据。
  • 磁盘 I/O (如果内存不足): 当数据量大到内存装不下时,就会使用临时文件进行排序,产生大量的磁盘读写,这是最慢的情况。

1.2 如何避免 Filesort?—— 利用索引的有序性

避免 Filesort 的最佳方法是创建一个索引,使其列的顺序和排序方向与 ORDER BY 子句的要求一致。

条件:

要让索引能够用于 ORDER BY,通常需要满足以下条件:

  1. 索引列顺序: ORDER BY 子句中的所有列必须是索引中的连续的列,并且是索引的前缀
    • 例如,索引 (colA, colB, colC) 可以用于 ORDER BY colA, ORDER BY colA, colB, ORDER BY colA, colB, colC
    • 但不能用于 ORDER BY colB, ORDER BY colA, colC, ORDER BY colB, colA
  2. 排序方向: ORDER BY 子句中所有列的排序方向(ASC 或 DESC)必须一致,并且与索引的创建方向一致,或者全部与索引创建方向相反。MySQL 可以倒序扫描索引来满足相反方向的排序。
    • 例如,索引 (colA ASC, colB ASC) 可以用于 ORDER BY colA ASC, colB ASCORDER BY colA DESC, colB DESC
    • 但不能用于 ORDER BY colA ASC, colB DESC
  3. WHERE 子句与索引的关系: 如果查询有 WHERE 子句,并且 WHERE 子句使用了索引的前缀列进行等值查询,那么 ORDER BY 子句可以使用索引中紧随其后的列进行排序。
    • 例如,索引 (colA, colB, colC)
    • 查询 SELECT * FROM table WHERE colA = '...' ORDER BY colB, colC; 可以使用索引进行排序。
    • 查询 SELECT * FROM table WHERE colA > '...' ORDER BY colB, colC; 可能无法使用索引排序,因为 WHERE 子句在 colA 上是范围查询,中断了索引的连续性。
    • 查询 SELECT * FROM table WHERE colB = '...' ORDER BY colA; 无法使用索引排序,因为 WHERE 子句没有使用索引的前缀。
  4. 排序的列和 WHERE 子句的过滤列不能是相互冲突的范围: 例如 WHERE colA = 1 ORDER BY colA.
  5. 没有 LIMITORDER BY 列不在 WHERE 子句中,或 WHERE 是范围查询: 这种情况下,MySQL 可能为了避免全索引扫描而选择 Filesort。但如果有了 LIMIT,MySQL 可能会重新考虑使用索引排序。
  6. ORDER BY RAND() 这个是随机排序,索引是无法满足的,必定是 Filesort。避免在生产环境使用 ORDER BY RAND(),可以考虑其他随机获取数据的方法。

1.3 EXPLAIN 示例 (ORDER BY)

假设我们有表 products

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    category_id INT,
    price DECIMAL(10, 2),
    create_time DATETIME
);

-- 创建一个联合索引
CREATE INDEX idx_cat_price_time ON products (category_id, price, create_time);

-- 可以自己插入一些数据来进行下面的测试!

示例 1: Filesort (排序列不在索引前缀)

EXPLAIN SELECT * FROM products ORDER BY create_time DESC;

EXPLAIN 结果可能显示 type: ALL (全表扫描) 和 Extra: Using filesort。因为 create_time 不是索引 idx_cat_price_time 的前缀。

示例 2: 利用索引排序 (符合前缀规则)

EXPLAIN SELECT * FROM products ORDER BY category_id ASC, price ASC;

EXPLAIN 结果可能显示 type: index (全索引扫描) 或 type: ALL (如果优化器认为全表扫描更快),但 Extra没有 Using filesort。或者如果同时有 WHERE 子句限制了扫描范围,type 可能是 rangeref,且 Extra 中没有 Using filesort

EXPLAIN SELECT * FROM products WHERE category_id = 10 ORDER BY price ASC, create_time ASC;

EXPLAIN 结果可能显示 type: ref,并且 Extra没有 Using filesort。因为 WHERE 子句使用了索引前缀 category_id 的等值条件,ORDER BY 子句使用了索引中紧随其后的列 pricecreate_time

示例 3: Filesort (排序方向不一致)

EXPLAIN SELECT * FROM products WHERE category_id = 10 ORDER BY price ASC, create_time DESC;

EXPLAIN 结果很可能显示 type: ref,但 Extra 中有 Using filesort。因为 price 是 ASC 排序,而 create_time 是 DESC 排序,与索引定义 (..., price ASC, create_time ASC) 的方向不完全一致(或者完全相反)。

优化建议 (ORDER BY):

  • 分析慢查询中的 ORDER BY 子句。
  • 检查是否有合适的索引,其列的顺序和方向能匹配 ORDER BY 的需求。
  • 如果 WHEREORDER BY 都很频繁,考虑创建联合索引,将 WHERE 条件中用于等值过滤的列放在前面,将 ORDER BY 中的列按顺序放在后面。
  • 使用 EXPLAIN 验证 Filesort 是否被消除。

Part 2: GROUP BY 优化详解

GROUP BY 子句用于将结果集按照一个或多个列进行分组,通常与聚合函数(如 COUNT(), SUM(), AVG(), MAX(), MIN())一起使用。如果不能利用索引完成分组,MySQL 可能会创建临时表来存储中间结果,或者先排序再分组。

2.1 什么是 Using Temporary 和 Using Filesort (for GROUP BY)?

当 MySQL 无法直接通过索引的有序性来满足 GROUP BY 的需求时,它可能采取以下策略:

  1. 创建临时表 (Using temporary): MySQL 会创建一个内存或磁盘上的临时表,将需要分组的列和聚合所需的列存入其中。然后遍历所有符合 WHERE 条件的行,将数据插入临时表,并在插入时进行聚合(如果临时表上有主键或唯一索引)或插入后进行聚合。
  2. 排序后分组 (Using filesort): MySQL 会将结果集按照 GROUP BY 的列进行排序,然后遍历排序后的结果进行分组聚合。这个排序过程也可能导致 Filesort。

EXPLAINExtra 列显示 Using temporaryUsing filesort(有时两者都会出现),就表示 GROUP BY 过程不够优化。

为什么慢?

  • 临时表: 创建和维护临时表有开销,尤其是当临时表溢写到磁盘时,会产生大量磁盘 I/O。
  • Filesort:ORDER BY 中的 Filesort,消耗 CPU 和 I/O。

2.2 如何避免 Using Temporary 和 Filesort (for GROUP BY)?—— 利用索引的有序性

类似于 ORDER BY,利用索引的有序性可以帮助 MySQL 直接按分组所需的顺序扫描数据,从而避免临时表和额外的排序。

条件:

要让索引能够用于 GROUP BY,通常需要满足以下条件:

  1. 索引列顺序: GROUP BY 子句中的所有列必须是索引中的连续的列,并且是索引的前缀
    • 例如,索引 (colA, colB, colC) 可以用于 GROUP BY colA, GROUP BY colA, colB, GROUP BY colA, colB, colC
    • 但不能用于 GROUP BY colB, GROUP BY colA, colC, GROUP BY colB, colA
  2. WHERE 子句与索引的关系: 如果查询有 WHERE 子句,并且 WHERE 子句使用了索引的前缀列进行等值查询,那么 GROUP BY 子句可以使用索引中紧随其后的列进行分组。
    • 例如,索引 (colA, colB, colC)
    • 查询 SELECT colA, colB, COUNT(*) FROM table WHERE colA = '...' GROUP BY colA, colB; 可以使用索引进行分组。
    • 查询 SELECT colA, colB, COUNT(*) FROM table WHERE colA > '...' GROUP BY colA, colB; 可能无法使用索引分组,原因同 ORDER BY
    • 查询 SELECT colA, colB, COUNT(*) FROM table WHERE colB = '...' GROUP BY colA, colB; 无法使用索引分组,因为 WHERE 子句没有使用索引的前缀。
  3. GROUP BY 列的顺序很重要: 必须严格按照索引列的顺序进行分组。
  4. 没有 DISTINCTMIN/MAX 在非索引列上: 某些复杂的聚合函数组合可能阻止索引用于分组。COUNT(DISTINCT ...) 也经常导致无法使用索引进行分组。

2.3 EXPLAIN 示例 (GROUP BY)

还是使用上面的 products 表和 idx_cat_price_time (category_id, price, create_time) 索引。

示例 4: Using Temporary / Filesort (分组列不在索引前缀)

EXPLAIN SELECT price, COUNT(*) FROM products GROUP BY price;

EXPLAIN 结果可能显示 type: ALLExtra: Using temporary; Using filesort。因为 price 不是索引 idx_cat_price_time 的前缀。

示例 5: 利用索引分组 (符合前缀规则)

EXPLAIN SELECT category_id, COUNT(*) FROM products GROUP BY category_id;

EXPLAIN 结果可能显示 type: index (全索引扫描) 或 type: ALL,但 Extra没有 Using temporaryUsing filesort。或者如果同时有 WHERE 子句限制了扫描范围,type 可能是 rangeref,且 Extra 中没有 Using temporaryUsing filesort

EXPLAIN SELECT category_id, price, COUNT(*) FROM products WHERE category_id = 10 GROUP BY category_id, price;

EXPLAIN 结果可能显示 type: ref,并且 Extra没有 Using temporaryUsing filesort。因为 WHERE 子句使用了索引前缀 category_id 的等值条件,GROUP BY 子句使用了索引中紧随其后的列 category_idprice(尽管 category_idWHERE 里已经限制了,但在 GROUP BY 里再次出现并不影响索引的使用)。

优化建议 (GROUP BY):

  • 分析慢查询中的 GROUP BY 子句。
  • 检查是否有合适的索引,其列的顺序能匹配 GROUP BY 的需求。
  • 如果 WHEREGROUP BY 都很频繁,考虑创建联合索引,将 WHERE 条件中用于等值过滤的列放在前面,将 GROUP BY 中的列按顺序放在后面。
  • 注意 GROUP BY 列的顺序必须和索引前缀严格匹配。
  • 对于 COUNT(DISTINCT ...) 或复杂聚合,可能难以用索引优化分组,需要考虑其他方案(如子查询、汇总表等)。
  • 使用 EXPLAIN 验证 Using temporaryUsing filesort 是否被消除。

Part 3: 联合索引,同时优化 WHERE, ORDER BY, GROUP BY

最理想的情况是,一个联合索引能够同时支持 WHERE 子句过滤、GROUP BY 分组和 ORDER BY 排序。这需要精心设计索引列的顺序。

索引列顺序的考虑优先级(通常):

  1. WHERE 子句中的等值条件列: 放在索引最前面,能最有效地缩小扫描范围。
  2. WHERE 子句中的范围条件列: 放在等值条件列后面。范围条件会终止索引后续列用于进一步的索引查找优化,但可能可以用于 ICP。
  3. GROUP BY 子句中的列: 放在 WHERE 条件列后面,且顺序要和 GROUP BY 的顺序一致。
  4. ORDER BY 子句中的列: 放在 GROUP BY 列后面(如果 GROUP BYORDER BY 使用的列不同),且顺序和方向要一致。
  5. 查询中需要返回的列 (用于索引覆盖): 如果可能,将查询中 SELECT 的其他列也加入到索引中,形成覆盖索引,彻底避免回表。这部分列通常放在索引的最后。

示例 6: 一个尝试同时优化 WHERE, GROUP BY, ORDER BY 的联合索引

假设我们有一个查询:

SELECT category_id, price, COUNT(*) as total_count
FROM products
WHERE category_id = 10 AND create_time >= '2023-01-01'
GROUP BY category_id, price
ORDER BY price ASC, category_id ASC; -- 注意这里的ORDER BY顺序

根据上述优先级和规则,我们可以尝试创建索引:

-- category_id 是等值条件,放最前
-- create_time 是范围条件,放 category_id 后面
-- GROUP BY 是 category_id, price,所以 price 放 create_time 后面
-- ORDER BY 是 price ASC, category_id ASC,这与 GROUP BY 的列顺序一致,可以考虑合并
CREATE INDEX idx_optimal ON products (category_id, create_time, price);

执行 EXPLAIN 看看效果:

EXPLAIN SELECT category_id, price, COUNT(*) as total_count
FROM products
WHERE category_id = 10 AND create_time >= '2023-01-01'
GROUP BY category_id, price
ORDER BY price ASC, category_id ASC;

理想情况下,如果优化器认为这个索引合适:

  • WHERE category_id = 10 利用索引前缀进行等值查找。
  • WHERE create_time >= '2023-01-01' 利用索引的 create_time 部分进行范围扫描(可能伴随 ICP)。
  • GROUP BY category_id, price 由于 category_idWHERE 中已固定,且 price 紧随 create_time 之后,MySQL 可以利用索引的有序性进行分组。
  • ORDER BY price ASC, category_id ASC 由于 GROUP BY 通常会隐含排序,且这里的 ORDER BY 列和方向与 GROUP BY 以及索引的后续列顺序一致,MySQL 可以直接使用索引的顺序,避免 Filesort。

EXPLAIN 结果中可能显示 type: range,并且 Extra 中没有 Using temporaryUsing filesort。✨

重要的注意事项:

  • ORDER BYGROUP BY 的列和方向必须严格匹配索引的顺序和方向(或完全相反)才能利用索引避免 Filesort/Using temporary。
  • 在一个查询中,ORDER BYGROUP BY 有时会“争抢”索引的使用。如果一个索引能同时满足两者,MySQL 优化器会选择最有利的方式。
  • GROUP BY 如果能使用索引,通常也意味着结果是按照 GROUP BY 的列排序的,所以如果 ORDER BY 的列和方向与 GROUP BY 完全一致,ORDER BY 就可以被“优化掉”或者说融入到分组过程中。
  • EXPLAIN 是唯一的真理!任何索引优化猜想都需要通过 EXPLAIN 来验证。

总结 📝

优化 ORDER BYGROUP BY 的核心在于让 MySQL 能够利用索引的有序性来完成排序和分组,从而避免代价高昂的 Filesort 和 Using temporary 操作。

  • ORDER BY 优化: 关注索引列的顺序和排序方向是否与 ORDER BY 子句匹配,特别是与 WHERE 子句结合时的“最左前缀”规则。目标是消除 EXPLAIN 中的 Using filesort
  • GROUP BY 优化: 关注索引列的顺序是否与 GROUP BY 子句匹配,同样要考虑与 WHERE 子句的结合。目标是消除 EXPLAIN 中的 Using temporaryUsing filesort
  • 联合索引: 精心设计的联合索引可以同时优化 WHEREGROUP BYORDER BY。索引列的顺序通常按照等值过滤、范围过滤、分组、排序的优先级来考虑。
  • EXPLAIN 神器: 永远使用 EXPLAIN 来分析查询的执行计划,确认 Filesort 和 Using temporary 是否被避免,并评估索引的使用情况。

数据库优化是一个持续学习和实践的过程。掌握了索引对 ORDER BYGROUP BY 的优化原理,并结合 EXPLAIN 工具进行分析,你就能更有效地提升数据库查询性能!

希望这篇详细的讲解对你有所启发!祝你的数据库查询越来越快!🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2373176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试——用例篇(3)

目录 一、设计测试用例的具体方法 1.1等价类 1.1.1等价类概念介绍 1.1.2等价类分类 1.2边界值 1.2.1边界值分析法 1.2.2边界值分类 1.3正交法 1.3.1正交表 1.3.2正交法设计测试用例步骤 1.4判定表法 1.4.1判定表 1.4.2判定表方法设计测试用例 1.5 场景法 1.6错误…

在 Win11 下安装 Wireshark 的详细步骤

目录 一、了解 Wireshark1. 作用和功能2. 使用步骤 二、下载安装包三、运行安装包四、使用 Wireshark1. 抓包2. 窗口介绍3. 过滤器(显示 / 捕获过滤器)4. 保存过滤后的报文1)显示过滤器表达式(了解)2)过滤表…

AI编程: 使用Trae1小时做成的音视频工具,提取音频并识别文本

背景 在上个月,有网页咨询我怎么才能获取视频中的音频并识别成文本,我当时给他的回答是去问一下AI,让AI来给你答案。 他觉得我在敷衍他,大骂了我一顿,大家觉得我的回答对吗? 小编心里委屈,我…

RTC实时时钟DS1337S/PT7C4337WEX国产替代FRTC1337S

NYFEA徕飞公司的FRTC1337S串行实时时钟是一种低功耗时钟/日历,被设计成可以无缝替代市场上流行的DS1337S和PT7C4337WEX(SOP8)两种型号, 具有两个可编程的时钟闹钟和一个可编程方波输出。 地址和数据通过2线双向总线串行传输。时钟/日历提供秒、分钟、小时、天、日期…

Vue3.5 企业级管理系统实战(十七):角色管理

本篇主要探讨角色管理功能,其中菜单权限这里先不实现,后续在菜单管理中再进行实现。接口部分依然是使用 Apifox mock 的。 1 角色 api 在 src/api/role.ts 中添加角色相关 api,代码如下: //src/api/role.ts import service fro…

QTableWidget实现多级表头、表头冻结效果

最终效果: 实现思路:如果只用一个表格的话写起来比较麻烦,可以考虑使用两个QTableWidget组合,把复杂的表头一个用QTableWidget显示,其他内容用另一个QTableWidget。 #include "mainwindow.h" #include &qu…

A2A大模型协议及Java示例

A2A大模型协议概述 1. 协议作用 A2A协议旨在解决以下问题: 数据交换:不同应用程序之间的数据格式可能不一致,A2A协议通过定义统一的接口和数据格式解决这一问题。模型调用:提供标准化的接口,使得外部应用可以轻松调…

CMake 入门实践

CMake 入门实践 第一章 概念与基础项目1.1 CMake 基础认知1.2 最小 CMake 项目1.3 构建流程验证 第二章 多文件项目管理2.1 项目结构2.2 源码示例2.3 CMake 配置 第三章 库文件管理实战3.1 项目结构3.2 核心配置3.3 接口设计 第四章 构建类型与编译优化4.1 构建类型配置4.2 构建…

异地多活单元化架构下的微服务体系

治理服务间的跨IDC调用,而数据库层面还是要跨IDC 服务注册中心拆开、 金融要求,距离太远,异地备库,如果延迟没读到数据就可能有资损,IDC3平时不能用,IDC1挂了还是有数据同步问题,IDC3日常维护…

HarmonyOS NEXT——DevEco Studio的使用(还没写完)

一、IDE环境的搭建 Windows环境 运行环境要求 为保证DevEco Studio正常运行,建议电脑配置满足如下要求: 操作系统:Windows10 64位、Windows11 64位 内存:16GB及以上 硬盘:100GB及以上 分辨率:1280*8…

Windows系统Jenkins企业级实战

目标 在Windows操作系统上使用Jenkins完成代码的自动拉取、编译、打包、发布工作。 实施 1.安装Java开发工具包(JDK) Jenkins是基于Java的应用程序,因此需要先安装JDK。可以从Oracle官网或OpenJDK下载适合的JDK版本。推荐java17版本&#x…

C# 方法(ref局部变量和ref返回)

>本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 ref局部变量和ref返回 …

滑动窗口,438找出字符串中所有字母的异位词

1.题目 2.解析 这道题我们用滑动窗口来实现,加上哈希表和vector容器的使用来实现这道题目,每次滑动之后我们都对其和答案进行比较,如果全部相等我们返回left,不相等继续滑动即可。 本质就是我们把p中相同数量的字母框起来&#…

「国产嵌入式仿真平台:高精度虚实融合如何终结Proteus时代?」——从教学实验到低空经济,揭秘新一代AI赋能的产业级教学工具

引言:从Proteus到国产平台的范式革新 在高校嵌入式实验教学中,仿真工具的选择直接影响学生的工程能力培养与创新思维发展。长期以来,Proteus作为经典工具占据主导地位,但其设计理念已难以满足现代复杂系统教学与国产化技术需求。…

《Python星球日记》 第52天:反向传播与优化器

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、引言二、反向传播算法原理简述1. 什么是反向传播?2. 从数学角度…

Java常用类概述

Java常用类概述 一、字符串三剑客1. String(不可变字符串)2. StringBuilder(可变,线程不安全)3. StringBuffer(可变,线程安全) 二、日期时间类(重点掌握新版API&#xff…

C++STL——priority_queue

优先队列 前言优先队列仿函数头文件 前言 本篇主要讲解优先队列及其底层实现。 优先队列 优先队列的本质就是个堆,其与queue一样,都是容器适配器,不过优先队列是默认为vector实现的。priority_queue的接口优先队列默认为大根堆。 仿函数 …

深入解析WPF中的3D图形编程:材质与光照

引言 在Windows Presentation Foundation (WPF) 中创建三维(3D)图形是一项既有趣又具有挑战性的任务。为了帮助开发者更好地理解如何使用WPF进行3D图形的渲染,本文将深入探讨GeometryModel3D类及其相关的材质和光源设置。 1、GeometryModel3D类简介 GeometryMode…

SolidWork-2023 鼠標工程

地址 https://github.com/MartinxMax/SW2023-Project/tree/main/mouse 鼠標

vscode预览模式(点击文件时默认覆盖当前标签,标签名称显示为斜体,可通过双击该标签取消)覆盖标签、新窗打开

文章目录 VS Code 预览模式如何取消预览模式(即“固定”标签页)?预览模式有什么用? VS Code 预览模式 在 VS Code 中,当你单击文件浏览器(例如,资源管理器侧边栏)中的某个文件时&am…