news 2026/10/5 3:31:13

MySQL基本查询实战详解:从执行顺序到性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL基本查询实战详解:从执行顺序到性能优化

写SQL查数据这件事,入门容易,写好却没那么简单。很多人在MySQL表的基本查询上栽跟头,不是不会写SELECT,而是没搞清楚一条查询语句背后的执行逻辑、条件过滤的边界、分组聚合的语义,以及排序分页在真实数据量下的表现。这篇文章不聊安装、不扯集群,就聚焦在“把一张表查明白”这件事上,结合我实际写SQL、调慢查询的经验,从最简单的SELECT骨架讲到索引利用、跨表合并、大表分页这些躲不开的坑,适合正在补基本功的开发者和刚接触MySQL的同学。

1. 先读懂一条查询语句的执行顺序

1.1 SELECT语法的完整形态

很多新手写查询,脑子里只有SELECT 字段 FROM 表这一个形状,但MySQL的SELECT实际支持的子句比想象中多得多。完整的语法骨架大概是这样的:

SELECT [DISTINCT] 列名或表达式 FROM 表名 [INNER JOIN | LEFT JOIN 其他表 ON 连接条件] [WHERE 行级过滤条件] [GROUP BY 分组字段] [HAVING 分组后的过滤条件] [ORDER BY 排序字段 [ASC|DESC]] [LIMIT 偏移量, 行数]

这个骨架是日常业务里百分之九十九的查询都会用到的。难点不在于记住每个子句能干什么,而在于理解它们的执行顺序。举个非常典型的例子:如果一张订单表有几千万行,要统计每个客户今年的订单总金额且只保留总金额超过一万的客户,很多人会把客户过滤条件一股脑塞进WHERE,又把金额过滤塞进HAVING,结果执行计划一团糟。

正确理解顺序比死记硬背重要得多。MySQL拿到一条SELECT后,大致的执行过程是这样的:先根据FROM确定数据源,然后通过ON和JOIN把多张表的数据拼起来,接着用WHERE把行一级的条件过滤掉,之后再GROUP BY分组并执行聚合计算,接着用HAVING过滤分组结果,再对最终结果做ORDER BY排序,最后LIMIT从排序后的集合里取指定的行。很多人没意识到SELECT子句中写的列别名,在执行顺序里非常靠后,所以不能在WHERE或GROUP BY里直接引用别名,这就是一个高频报错点。

1.2 逻辑顺序和书写顺序的差异

我用一个真实发生过的场景来说明这个差异。假设有一张员工表employee,里面记录了员工所属部门、薪资和入职日期。我需要统计各部门的薪资总额,并且只统计入职日期在2023年之后的员工,最后只要总薪资超过50万的部门。

第一版SQL很容易写成这样:

SELECT department, SUM(salary) AS total_salary FROM employee WHERE hire_date >= '2023-01-01' GROUP BY department HAVING total_salary > 500000 ORDER BY total_salary DESC;

这个SQL是正确的。但如果换一种写法,把hire_date的过滤条件放到HAVING里:

SELECT department, SUM(salary) AS total_salary FROM employee GROUP BY department HAVING hire_date >= '2023-01-01' AND SUM(salary) > 500000;

表面区别不大,实际逻辑完全不同。HAVING是在分组之后才执行的,它在过滤时面对的是分组里的所有员工,而不是每一行。MySQL会用整个分组里员工的hire_date来作为条件判断,这既不符合需求,还会把不需要的行先做聚合,白白消耗CPU和内存。正确理解执行顺序之后,写SQL犯这类错误的概率会大幅下降。

这里还顺带说一个经验:能用WHERE过滤掉的数据,绝对不要留到HAVING阶段。WHERE在聚合前过滤,意味着分组的数据量更小,聚合效率更高。这点在千万行大表上尤其明显,过滤越早,扫描和计算量越小,整个查询的响应时间差距能到十倍以上。

2. WHERE条件过滤:决定查询快慢的第一道闸门

2.1 条件优先级与括号的必要性

WHERE子句支持AND、OR、NOT、IN、BETWEEN、LIKE、比较运算符、子查询等一大堆操作符。优先级的规则其实不复杂:NOT优先级最高,其次是AND,最后是OR。没有括号的时候,条件会按照这个优先级来结合,而不是从左到右依次计算。

我见过一个报表SQL,本来想查两个部门的员工,或者入职满十年的老员工,结果因为没有括号写成了:

SELECT * FROM employee WHERE department = '研发部' OR department = '市场部' AND hire_date <= '2015-01-01';

这个条件的真实含义是:研发部的所有员工,加上市场部里入职满十年的员工。如果业务期望是“两个部门的员工,同时入职满十年”,那么这个查询结果就是错的。解决方式非常简单且没有任何歧义,就是加括号:

SELECT * FROM employee WHERE (department = '研发部' OR department = '市场部') AND hire_date <= '2015-01-01';

只要条件里同时存在AND和OR,我强烈建议一律加括号。别去赌自己和同事的记忆力,代码的可读性与正确性比少敲两个字符重要得多。

2.2 NULL判断是新手最难看透的细节

在MySQL里,NULL不是一个值,而是一个“不确定”的标记。它不等于空字符串,也不等于0,更不等于NULL。如果你试图用column = NULL来筛选空值,结果永远是空的。

正确判断NULL的方式必须用IS NULL或IS NOT NULL。这一点几乎每一本SQL书都会提,但没有书会提醒你,在业务场景里,很多数据表字段的默认值不是NULL,而是空字符串,两者混在一起的时候,查询会频繁出现数据对不上的情况。

我处理过一个客户表,电话号码字段一部分是真正的NULL,一部分是空字符串,还有一部分是填充了默认值的'0'。写查询时如果不把这些情况分开讨论,统计出来的缺号率就是错的。后来我总结出一个稳妥的写法:先通过COALESCE函数把字段统一成可判断的形态,再做过滤:

SELECT * FROM customer WHERE COALESCE(phone, '') = '';

这样不管底层存的是NULL还是空字符串,都能一次性捞出来。反过来,如果要查有电话的客户,就写COALESCE(phone, '') <> ''。这类看似不起眼的细节,恰恰是查询结果是否可信的关键。

2.3 过滤条件怎么写才不容易让索引失效

WHERE条件不光影响结果正确性,还直接影响查询速度。如果一张表有上千万行,过滤条件走不到索引,MySQL就得全表扫描,查询时间会从毫秒级变成秒级甚至分钟级。

最容易让索引失效的几个习惯,我挨个说。第一,对索引列使用函数,比如WHERE YEAR(hire_date) = 2024,这时候即使hire_date上有索引,MySQL也没法直接定位数据,只能把每一行的年份算出来再比较。更好的写法是直接写成范围条件WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01'。

第二,索引列参与了数值运算,比如WHERE salary * 12 > 600000,同样的道理,可以把运算换到等号另一边,写成WHERE salary > 50000。

第三,使用LIKE模糊查询时,如果把通配符放在最前面,比如LIKE '%研发%',索引基本失效。如果业务允许,尽量用模糊后缀,比如LIKE '研发%',这样还能利用索引的前缀匹配特性。

顺带说一个大家都关心的话题,那就是“辅助索引如何避免回表”。什么是回表?简单理解就是,一个辅助索引上只存了索引字段和主键值,查询时如果还需要其他字段,MySQL要拿着主键值回到聚簇索引的叶子节点再去读取整行数据,这个过程叫回表。只要查询需要的所有字段都能在辅助索引里找到,MySQL就不用回表,这种状态叫“覆盖索引”。比如我建了一个辅助索引idx_department(department),但查询是SELECT department, COUNT(*) FROM employee GROUP BY department,那这个查询只用到department字段,走索引就够用了,不需要回表,速度自然快。反过来,如果select里还带了salary,索引里没有这个字段,MySQL就必须回表取数据。理解回表机制后,写查询时会有意识地去减少不必要的字段查询,不是为了少打几个字符,而是为了减少回表次数。

3. 分组与聚合:GROUP BY、聚合函数和HAVING的配合

3.1 COUNT(*)和COUNT(列名)不能盲目替换

分组聚合是基本查询里最容易出逻辑偏差的部分,偏差往往出在细节上。拿最常见的COUNT为例,COUNT(*)统计的是行数,不管这行里的字段是不是NULL;COUNT(column)统计的则是该列非NULL的数量。两者看起来差不多,结果可能差得远。

有一张业务表,字段remark允许为空。我想知道总共有多少条业务记录,同时想知道有多少条记录填写了备注。这时候:

SELECT COUNT(*) AS total_rows, COUNT(remark) AS remark_filled FROM business_log;

这种写法之所以可靠,是因为COUNT(remark)天然跳过NULL,正好可以用来统计非空数量。如果反过来,我在写代码时随意把COUNT(*)替换成COUNT(某字段),很可能因为该字段存在NULL值而得到偏小的统计结果。聚合函数还有SUM、AVG、MAX、MIN,需要记住的是AVG和SUM都会忽略NULL,但不会忽略0。如果字段存在NULL,求平均的时候会有一种微妙的偏差,因为NULL被跳过了而不是被当成0处理。理解这个原理后,遇到类似需求时,可以用COALESCE把NULL先转成0再做聚合,保证结果符合业务预期。

3.2 HAVING的定位:它是分组的过滤器

很多人分不清WHERE和HAVING,这里再强调一次,WHERE过滤的是行,HAVING过滤的是分组。WHERE在数据分组之前执行,所以不能使用聚合函数;HAVING在分组之后执行,可以引用聚合函数的结果。

举个例子,统计各部门人数,并且只要人数大于100的部门:

SELECT department, COUNT(*) AS emp_count FROM employee GROUP BY department HAVING COUNT(*) > 100;

把COUNT(*) > 100放进WHERE里就会直接报错,因为MySQL在执行WHERE时还不知道分组的存在。更值得注意的是,HAVING虽然能做,但能不用就尽量不用。如果这个分组后的过滤条件实际上可以用WHERE先做行级过滤,查询性能会好很多。比如要统计2024年以后入职的员工按部门的分布,就先把入职时间在WHERE阶段过滤掉,再做分组和计数,这样参与分组的数据量会小很多,占用的临时表和内存也少。

3.3 几千万行大表上的聚合经验

聊到几千万行的大表,聚合查询的优化就不是一句“加索引”能解决的了。MySQL在做GROUP BY时,如果无法利用索引直接完成分组,就需要创建临时表来存放分组数据,数据量一大,临时表可能被写到磁盘上,性能断崖式下跌。

实战中如果必须对超大表做分组统计,我的经验是尽量缩小扫描范围。业务上几乎不存在需要每天全量统计的场景,通常都有时间维度作为天然边界,比如“近半年”“上个月”等。先把范围通过WHERE锁死,再分组,命中索引的概率也会高很多。

另一个思路是利用覆盖索引来做分组,这是比较实在的优化方式。比如要统计员工表各部门的人数,如果有一个包含department字段的辅助索引,那么SELECT department, COUNT(*) FROM employee GROUP BY department可以通过索引顺序扫描直接完成,不需要临时表,速度非常快。

再一个被很多人忽略的点:如果聚合任务实在太大,可以把结果明细层先落成汇总表,查询时直接查汇总表,而不是每次都跑原始大表。这不是投机取巧,而是数据仓库领域最常见的“以空间换时间”策略。业务系统里的统计报表,不可能每一次都实时扫几千万行,按天或按小时预聚合是合理且可靠的方法。

4. 排序和分页:ORDER BY与LIMIT里藏的坑

4.1 ORDER BY到底按什么排

排序看起来最简单,实际最容易翻车。ORDER BY默认按升序ASC排列,但字符串的排序规则取决于字段的排序规则,也就是collation。在MySQL 8.0默认的utf8mb4字符集下,中文字段的排序并不一定按照拼音或笔画,而是按照Unicode编码顺序,这一点会让很多人惊讶。

对中文排序有严格要求的场景,比如按部门名称的拼音排序,不能直接依赖ORDER BY department,可以考虑ORDER BY CONVERT(department USING gbk)这样的方式,这是利用GBK编码的顺序近似拼音顺序的做法。但说实话,我更建议在应用层处理这种排序需求,数据库管数据正确性,应用层管展示逻辑,MySQL的编码转换排序在小数据量下好使,大数据量下会影响索引使用。

另一个常见问题多字段排序的写法。需要明确的是,ORDER BY后面可以跟多个字段,每个字段独立指定方向:

SELECT * FROM employee ORDER BY department ASC, salary DESC;

这个查询先按部门升序排,同一部门内再按薪资降序排。很多业务逻辑里“跨组对比”的效果就是这么实现的,但要注意多个排序字段的先后顺序直接影响结果形态,写之前想清楚到底哪个是主排序,哪个是次排序。

4.2 LIMIT分页的偏移量陷阱

分页查询是基本查询的一个重要应用,几乎每个后台管理系统都离不开。常规写法是这样的:

SELECT * FROM employee ORDER BY emp_no LIMIT 0, 20;

第一页、第二页都没什么问题,但页码大了之后,比如要查第10000页的数据,偏移量就变成了(10000-1) * 20 = 199980,MySQL得先把前面199980行都读出来扔到结果集里,再往后跳过20行返回数据。这个过程极其浪费,数据量一大就是灾难。

有经验的开发者会用“延迟关联”或者“基于主键范围”的方式来优化。延迟关联的意思是,先从索引上取出目标主键,再关联回原表取完整数据:

SELECT e.* FROM employee e INNER JOIN ( SELECT emp_no FROM employee ORDER BY emp_no LIMIT 199980, 20 ) t ON e.emp_no = t.emp_no;

这个SQL里,子查询走的索引是主键或覆盖索引,不需要回表读取完整行,所以取20个主键的速度快得多,然后外层只对20行做回表,整体开销小很多。如果业务表有连续的自增主键,还可以直接用WHERE emp_no > 上一页最大主键 ORDER BY emp_no LIMIT 20,这是性能最好的分页方式,缺点是不能随意跳页,只能通过下一页按钮逐页访问。

4.3 排序列和索引的匹配关系

ORDER BY能不能用到索引,取决于排序字段是否和索引的字段顺序完全匹配。如果一个复合索引是(department, hire_date),那么ORDER BY department, hire_date可以走索引,但ORDER BY hire_date, department就未必了,因为索引的排列顺序是department在前,hire_date在后。

排序方向也要注意,MySQL的索引大部分场景下是按升序存储的,8.0开始支持倒序索引,但使用起来依然有限制。我自己的经验是:如果业务上经常要按某个时间字段倒序查看最新的记录,就在这个时间字段上建单列索引,查询时ORDER BY create_time DESC LIMIT 10通常能利用索引直接倒序扫描,效率很高。

还有一点容易被忽略:ORDER BY的字段如果同时出现在WHERE条件里,并且WHERE条件本身也走索引,MySQL有机会通过索引本身的有序性直接避免额外排序,这叫“Using index condition”配合“filesort优化”。具体的执行计划可以通过EXPLAIN查看,看到Using filesort时就要警醒,因为这意味着MySQL真的把数据拿出来排了一遍序,数据量大时很费时间。

5. 多表查询基础:JOIN、UNION与跨表合并的区别

5.1 INNER JOIN和LEFT JOIN的语义要抓准

基本查询一旦涉及多张表,就绕不开JOIN。很多初学者会机械地认为只要表之间有关联就使用LEFT JOIN,这种思维定式经常导致数据翻倍。理解多表连接的关键在于,JOIN是根据关联条件把一张表的行和另一张表的行“拼起来”,如果右边有多行匹配,左边的行就会重复出现多次。

假设员工表employee里有员工所属部门编号,部门表department里有部门信息。我想查出每个员工及其部门名称,使用INNER JOIN:

SELECT e.name, d.dept_name FROM employee e INNER JOIN department d ON e.dept_id = d.id;

这样能查到有部门信息的员工,没有匹配部门的员工会被过滤掉。如果业务上需要把所有员工都列出来,部门为空就显示空,那就必须用LEFT JOIN:

SELECT e.name, d.dept_name FROM employee e LEFT JOIN department d ON e.dept_id = d.id;

但LEFT JOIN的坑主要在于关联条件的唯一性。如果部门表里存在两个相同id的记录,员工表里每个员工就会拼接出两条结果,导致数据翻倍。所以在写连接查询之前,要确认关联键在右边这张表上是唯一的,如果业务上不唯一,就要先做去重,再做关联。忽略了这一点,查询结果就会悄悄多出一堆重复行,而且还很难察觉。

5.2 UNION和UNION ALL,跨表合并的正确打开方式

很多人看到“跨表合并”这个词就想到JOIN,但它们在语义上完全不同。JOIN是横向拼接,把多张表的列合并到一起;跨表合并指的是纵向合并,把多张表的行拼到一个结果集里,这时候要用的是UNION或UNION ALL。

比如有三张月度销售报表,结构完全一样,都需要合并到一张结果里做年度分析:

SELECT order_no, amount FROM sales_january UNION ALL SELECT order_no, amount FROM sales_february UNION ALL SELECT order_no, amount FROM sales_march;

UNION会对结果集自动去重,所以比较慢;UNION ALL则完全不去重,直接合并所有行。如果业务上知道这些表里没有重复数据,或者重复数据不影响统计,一定要用UNION ALL,因为它避免了排序去重,执行速度快得多。我在实际使用中,凡是做数据合并,第一选择永远是UNION ALL,只有明确要求结果集不能有重复时才用UNION。

使用UNION还有一个细节:每个SELECT的列数和顺序必须完全一致,否则会报错。字段类型可以不同,MySQL会做隐式转换,但转换结果可能不符合预期,所以最好在查询时手动通过CAST统一类型,避免出现把字符串拼成数字这种低级问题。

5.3 多表查询中的字段歧义与分组口径

多表查询里的字段歧义是另一个高频问题。如果两张表都有name字段,直接在SELECT里写name会让MySQL报错,必须用表前缀或表别名限定,比如e.name和d.name。在SQL语句里给表起简短别名,不只是为了少打字,更是为了清晰地区分每个字段的来源。

分组口径在多表连接后特别容易出问题。还是拿员工和部门来举例,如果要统计每个部门的薪资总和,直接GROUP BY d.dept_name很直观,但要注意如果部门表某一行在员工表中有很多行匹配,分组时MySQL会先把所有拼接结果都放进临时表再聚合,数据量大了性能很差。更合理的做法是先分别聚合员工表得到部门汇总,再关联部门表补充部门名称,也就是“先聚合再连接”而不是“先连接再聚合”。

先聚合再连接的写法示意如下:

SELECT d.dept_name, t.total_salary FROM department d LEFT JOIN ( SELECT dept_id, SUM(salary) AS total_salary FROM employee GROUP BY dept_id ) t ON d.id = t.dept_id;

这样每个员工只会被聚合一次,不会因为部门表的数据重复而错误放大统计结果。很多报表数据对不上账,排查到最后都是这样的连接顺序问题。

6. 查询性能自查与常见问题实录

6.1 用EXPLAIN看穿一条查询的执行路径

写基本查询不是写完就结束了,一定要会看执行计划。在SELECT前面加EXPLAIN,MySQL会展示这条查询的执行路径,包括用了哪个索引、扫描了多少行、是否需要临时表、是否要排序等。

EXPLAIN输出里的几个关键列,我实际工作中看得最多的是type、key、rows和Extra。type从好到差大致是system>const>eq_ref>ref>range>index>ALL,其中ALL就是全表扫描,是性能最差的一种。看到ALL时,基本可以断定这条查询该加索引了,或者条件写法有问题。key列显示实际上用到的索引名,如果为NULL,说明没有用到索引。rows是MySQL估算要扫描的行数,这个数字越大越危险。Extra里如果出现Using filesort,说明排序没有用上索引,出现Using temporary则说明用到了临时表,这两种情况在大数据量下都是性能隐患。

查看执行计划的意义在于,你不需要靠猜来判断SQL写得好不好,执行计划会把真相直接摆在你面前。每次写完一条稍复杂的查询,随手敲一遍EXPLAIN,看两个核心指标,形成习惯之后,写出来的SQL质量会明显提升。

6.2 常见查询问题速查表与思考路径

我在答疑过程中整理过一张基本查询问题对照表,很多重复出现的问题都能在里面找到影子:

现象可能原因排查思路
查询结果行数比预期多JOIN时关联键不唯一,或条件缺少去重检查关联字段是否唯一,考虑先分组再去重
查询结果比预期少使用了INNER JOIN导致未匹配行被过滤改为LEFT JOIN,同时确认过滤条件位置
查询很慢但数据量不大索引失效,或LIKE前置通配符EXPLAIN查看type与key,改写条件格式
分页越翻越慢LIMIT偏移量过大,MySQL扫描过多行改为基于主键范围或延迟关联分页
统计值对不上NULL被聚合函数跳过,或WHERE/HAVING用错用COALESCE统一NULL,检查聚合口径
字段排序混乱中文字段排序依赖编码规则应用层排序或显式指定排序规则

这张表里的每一个问题,我在实际项目中都遇到过。它们看起来不算难,但一旦出现在生产环境,排查起来往往要花不少时间。与其事后Debug,不如写SQL的时候就在脑子里过一遍这几个维度:过滤是否最早生效、关联是否唯一、索引是否可用、排序分页是否会全表扫。

6.3 三个我最想提醒的实操经验

第一,查询语句的每个字段,尽量显式写清楚。SELECT *虽然打字少,但在生产环境里害处很大:一是回表次数更多,二是当表结构增加字段时,应用代码可能收到意料之外的数据。很多人觉得多查几个字段无所谓,实际上在宽表里,多出来的字段会白白占用数据库和网络IO。

第二,时间范围查询别用BETWEEN和LIKE解决一切。如果字段是datetime类型,BETWEEN '2024-01-01' AND '2024-12-31'会漏掉2024-12-31 23:59:59之后的数据,所以更严谨的写法是create_time >= '2024-01-01' AND create_time < '2025-01-01',左闭右开区间永远不会漏边界数据。

第三,对基本查询做任何改动之前,先备份原始SQL结果。这个建议听起来很基础,但实际开发中,我见过太多人因为改了一个过滤条件,导致线上报表数字变动,却找不到是谁改的。把验证前后的结果集行数、关键字段汇总值都记录一下,能避免绝大多数低级翻车事故。

写查询这件事,本质上是把业务逻辑翻译成一套有序的、可预测的计算流程。理解SELECT的执行顺序,把WHERE、GROUP BY、JOIN、ORDER BY这些基础子句的语义吃透,再配合执行计划验证性能,基本查询就不会成为业务系统的短板。如果非要给一点个人体会,那就是:不要小看任何一条“简单查询”,它就是整个数据体系的根基,根基扎实了,后面做再复杂的功能都不怕。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 3:30:53

为什么有的网站有 www,有的没有?-起源篇

封面图是世界上第一台网页服务器的照片&#xff0c;即蒂姆伯纳斯-李&#xff08;&#xff08;Tim Berners-Lee&#xff09;在 CERN 使用的 NeXT 计算机。 文章首发在我的个人博客&#xff0c;欢迎前去参观&#xff01; https://markxu.icu/blog/why-does-some-website-has-www…

作者头像 李华
网站建设 2026/10/5 3:30:43

VMware Workstation 17 Pro安装银河麒麟Kylin V10 SP3保姆级教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 3:29:28

政务云上云流程拆解:云化部署与迁移的规范落地指南

简介&#xff1a;这份PDF是贵州省地方标准DB52/T 1539.4—2020《政务云 第4部分&#xff1a;政务信息系统云化部署和迁移规范》&#xff0c;供政务部门、云服务商、部署与迁移实施方参考&#xff0c;用于统一新建系统上云和存量系统迁移的流程。文档明确了云化部署与云化迁移的…

作者头像 李华
网站建设 2026/10/5 3:29:23

风光柴储微网多目标优化调度:NSGA-II与MATLAB实现详解

做电力系统优化调度的人应该都有共识&#xff1a;风光柴储微网的多目标优化调度&#xff0c;最难的不是模型本身&#xff0c;而是“怎么把模型写进代码、跑出结果、再让结果经得起推敲”。我之前被这个题目折腾了挺久&#xff0c;一开始拿网上的通用框架跑&#xff0c;总是要么…

作者头像 李华
网站建设 2026/10/5 3:29:03

用分立MOS管从零搭建五种逻辑门:从开关原理到工程选型全解析

开篇先聊一个很多人的共同经历&#xff1a;第一次翻开数字电路教材&#xff0c;看到“逻辑门”那一章&#xff0c;脑子里冒出的第一个问题往往是——芯片里到底是怎么把电路做成“与”和“或”的&#xff1f;教材只会画一堆三角形符号和真值表&#xff0c;告诉你“这就是与非门…

作者头像 李华
网站建设 2026/10/5 3:29:03

Docker容器化部署避坑指南:从安装到GPU直通的排查与定位

先说个小事&#xff1a;标题里的“Doker”是Docker的笔误&#xff0c;不过这不影响今天的内容。我上个月帮人部署一套服务&#xff0c;从Windows上装Docker Desktop开始&#xff0c;一路把典型的坑踩了个遍&#xff1a;装完起不来、镜像拉不下来、Redis容器起来了但客户端连不上…

作者头像 李华