作为一个天天跟数据库打交道的人我太清楚“增删查改”这四个字的分量了。很多人觉得MySQL的增删查改就是四条SQL语句背下来就完事了。但真正上手做项目的时候才发现同样的INSERT、SELECT、UPDATE、DELETE有人写出来的语句稳如老狗有人写出来的语句三天两头把生产环境搞挂。差别在哪差别就在于对基础操作背后那些细节的理解深度。这篇文章我想认真聊聊MySQL里的增删查改。不是给你抄一遍官方文档而是把这些年实际踩过的坑、总结出来的经验、还有那些常规教程里不会明说的细节一次性梳理清楚。不管你刚入门、还是写过一段时间SQL但总觉得基础不牢这篇文章都值得你静下心来看完。1. 先把环境跑通安装、建库、建表这一条链路要认真对待增删查改的前提是得有一个能用的MySQL环境。很多新手挂在第一步所以我先把环境准备这条链路讲透。1.1 安装MySQL时最容易卡住的三个问题先说安装。我用过的安装方式挺多的Windows下直接下载安装包、Linux下用包管理器、还有Docker方式都试过。这里不推荐死记某一种方式关键是理解你当前系统最适合哪种。如果你在Linux服务器上操作Ubuntu系执行sudo apt install mysql-serverCentOS系执行sudo yum install mysql-server装完执行sudo systemctl status mysql有的发行版服务名是mysqld看下状态是running就对了。这里有个坑CentOS 7 默认的MySQL是mariadb想装官方MySQL要先添加官方yum源我当年在这上面浪费过不少时间。如果你用Windows去MySQL官网下载安装包。版本选择上我建议直接选8.0系列不要因为“稳定”去装5.7了。8.0在性能、安全性、窗口函数上都比5.7好不少而且现在大多数云数据库也都用8.x了没必要逆时代潮流。如果你图省事Docker方式最干净docker run --name mysql8 -e MYSQL_ROOT_PASSWORDyourpass -d mysql:8.0。这种方式最大的好处是环境隔离测试完直接删容器不会把系统弄脏。安装完以后有个高频问题MySQL的初始密码到底是什么不同版本、不同安装方式答案都不一样。windows安装包方式一般会让你自己设置Linux的apt方式安装完默认账号是root但密码不会告诉你需要看日志文件里的临时密码路径通常在/var/log/mysql/error.log里搜关键字“temporary password”。很多教程没提这个导致新手安装完成后死活登不进去。1.2 数据库连不上的时候先从这四条排查路径走一遍装好之后第一件事是验证能不能连上。命令行敲mysql -u root -p输密码进去这就说明服务端没问题。但实际工作中90%的“数据库连不上”都不是密码错而是网络、权限、配置文件这几个层面。我最常遇到的杀手动不了的问题就是这台报错热搜里同样能看到这条报错“ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock”。这个报错看到别慌它基本就三个原因MySQL服务根本没启动。在Linux上执行systemctl status mysqld或者service mysql status看下没启动就启动它。socket文件路径不对。本地用命令行连接默认走socket如果运行mysqld时指定了别的socket路径客户端也要用--socket参数指定。有些发行版的socket路径是/var/run/mysqld/mysqld.sock跟默认的/tmp/mysql.sock对不上就会报这个错。my.cnf里配置了skip-networking导致只允许本机unix socket连外部TCP连不进来。我自己的习惯是装好MySQL后第一件事就是确认监听端口netstat -tlnp | grep 3306。如果只有127.0.0.1:3306说明只监听本地远程连接肯定是不行的。想远程访问除了配置文件里bind-address改成0.0.0.0还得记得授权远程用户然后确认服务器安全组端口放行了。这几步做完99%的连接问题都能解决。1.3 用Workbench还是命令行工具选择上新手我建议两个都用命令行用来学语法Workbench用来做可视化查看。MySQL自带的MySQL Workbench建表、执行查询、看执行计划都很直观。尤其是看表的ER图、导出数据、管理用户权限图形化确实高效。但有一条我必须强调你写SQL的能力必须脱离图形工具。因为生产环境服务器上一般没有GUI界面出了问题你得用命令行排查。而且你面试写SQL、在线OJ刷题用的也都是命令行环境。Workbench和命令行之间的切换要当成基本功来练。2. 建表不是随便建的字段类型、约束与存储引擎的选择增删查改的第一环是“增”但INSERT真正落地之前你得先有一张设计合理的表。很多人忽略建表这一步后面写查询的时候就开始遭罪了。2.1 字段类型选不对查询速度天差地别字段类型是一个看起来基础、实则容易埋雷的点。我说几个最常见的选型问题。整数类型有TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT。很多人不管什么数字都默认INT这是不对的。比如用一个字段存用户的年龄TINYINT UNSIGNED就够了范围0-255存订单金额的“分”数用INT也能凑合但如果字段存雪花ID或者自增主键到了几十亿的规模就得用BIGINT。这里顺便辟个谣热搜里提到的“mysql中int5”可能很多人误以为INT(5)就是只能存5位数字。其实INT(M)里的M跟取值范围一点关系都没有它影响的是ZEROFILL零填充时的“显示宽度”。比如INT(5) UNSIGNED ZEROFILL存个12查询结果会显示00012。这玩意儿实际工作中用得很少知道它是什么原理就行别把这个当成限制长度的功能用。字符串类型的主流选择是CHAR、VARCHAR和TEXT。CHAR定长、VARCHAR变长、TEXT专门存大文本。一个很实用的选择原则你拿这个字段当主键或者做频繁等值查询而且长度固定如手机号、身份证号选CHAR长度不固定但是有上限如用户名、邮箱选VARCHAR超过一定长度的大段文本再考虑TEXT。别一上来就把所有字符串都定义成VARCHAR(255)存个文章正文也用VARCHAR(255)这会让行变长、索引变大、查询变慢。时间类型同样有讲究。DATETIME和TIMESTAMP是两大主力。老生常谈的是TIMESTAMP有2038年问题但这个真的太远了不用现在纠结。真正该注意的是时区问题——TIMESTAMP存的是UTC时间显示的时候会按照会话时区转换DATETIME是存什么显示什么。如果你没有特殊需要我建议直接DATETIME省得时区问题弄昏头。小数在数据库里也是重灾区。凡是涉及金额千万别用FLOAT和DOUBLE。浮点数在二进制里没法精确表示0.10.2算出来可能是0.30000000000000004。正确做法是用DECIMAL它是字符串存储的定点数不会出现精度丢失。我在银行系统里看到的惯例就是金额一律DECIMAL(10,2)起步单位统一用元除非有特殊精度要求。2.2 约束条件数据库数据质量的最后一道防线约束是很多新手直接跳过的东西。他们觉得反正程序里已经校验过了数据库里加不加约束无所谓。这是大错特错的想法。程序校验是前置控制数据库约束是兜底控制万一程序里有漏洞数据库约束就是最后一道防线。常用的约束就几个PRIMARY KEY 主键每张表必须有这是记录的唯一标识。InnoDB是索引组织表主键决定了数据的物理排列顺序。NOT NULL 非空字段不允许为空。比如用户表的用户名你觉得可以为null吗不行。查询里WHERE username NULL是永远查不到数据的空值会给逻辑判断带来无穷无尽的坑。DEFAULT 默认值热搜里有个词是“mysql设置默认值为0”实际应用场景很多。比如订单状态字段默认0待支付、积分字段默认0、逻辑删除标记默认0。有了默认值INSERT的时候不传这个字段也不会报错自动填0。UNIQUE 唯一约束业务上需要唯一性的字段比如手机号、邮箱除了主键之外需要加唯一索引。FOREIGN KEY 外键约束这里我要多嘴一句。互联网行业大型项目里我很少建议用物理外键因为高并发写入时外键检查会带来额外开销加上分库分表以后物理外键根本没法用。但这不是说外键概念不重要——你去面试不问物理外键用不用一定会问逻辑外键、表关联你懂不懂。所以学习阶段你尽可以建外键理解关联关系生产环境中则要谨慎使用。2.3 存储引擎InnoDB和MyISAM怎么选存储引擎这个话题一直有人问而且网络上众说纷纭。我直接给结论现在无脑选InnoDB。MyISAM在MySQL 5.5之前是默认引擎现在早就是明日黄花。我见过很多老项目还在用MyISAM因为被当年“查询快”的印象禁锢了。但MyISAM有个致命缺陷——它不支持事务、不支持行级锁表锁到底只要有一条写操作整张表都被锁住一旦有并发场景就完蛋。InnoDB支持事务、行级锁、崩溃恢复还能处理外键性能一点都不比MyISAM差就是磁盘占用稍微大一点这在现在的硬件条件下几乎不算问题。3. INSERT别只会写最基础的INSERT INTO建好表之后增删查改的第一步就是插入数据。INSERT看起来是四条语句里最简单的但细节还真不少。3.1 指定列插入和不指定列插入的本质区别先看最基础的两种写法-- 写法一不指定列名强烈不推荐 INSERT INTO user VALUES (1, 张三, 25, 12345678901qq.com); -- 写法二指定列名推荐 INSERT INTO user (id, name, age, email) VALUES (1, 张三, 25, 12345678901qq.com);不指定列名意味着VALUES里的数据必须严格按照建表时的字段顺序来一个都不能错。一旦表结构调整加了一列、调整了顺序这条SQL就废了而且错的还很隐蔽——数据插进去之后才发现张冠李戴。指定列名就灵活多了字段顺序随便打乱只要VALUES跟列名对应上就行。工作里的代码规范基本都强制要求INSERT必须显式写列名。这不是矫情是血泪教训换来的。3.2 批量插入别一条一条INSERT了我经常看到有人用循环去一条条执行INSERT例如在Java里for循环里跑1000次单条插入。这种做法最大的问题是性能每一条INSERT都包含一次网络往返、一次SQL解析、一次事务提交默认autocommit模式。1000条数据可能就要几百毫秒甚至几秒。正确的做法是批量插入INSERT INTO user (name, age, email) VALUES (张三, 25, aqq.com), (李四, 24, bqq.com), (王五, 26, cqq.com);一次INSERT语句里带上多条VALUES效率能提升一个数量级。我自己做过简单测试本地数据库插入1万条数据单条循环大约需要3-5秒而批量插入每次500条总耗时也就几百毫秒。差别就在这里。3.3 实用进阶重复记录怎么办实际业务里经常遇到一个问题插入的数据主键或者唯一键已经有了怎么办传统的做法是先查一下有就UPDATE没有就INSERT。但这样会导致两次SQL执行不但慢还容易出并发问题。MySQL提供了更优雅的解决方案INSERT INTO user (id, name, age, email) VALUES (1, 张三, 25, aqq.com) ON DUPLICATE KEY UPDATE name VALUES(name), age VALUES(age), email VALUES(email);这句的含义是如果id1这条记录不存在就正常插入如果已经存在就执行后面UPDATE的部分更新name、age、email这几个字段。一条SQL就把“有则更新无则插入”的逻辑搞定了这在处理爬虫数据、同步业务数据时极其好用。注意8.0.20之后的版本推荐用别名语法AS new ON DUPLICATE KEY UPDATE name new.nameVALUES()函数在新版本里标记为废弃但在8.0仍可用了解这个演进趋势就行。4. SELECT查询是增删查改里最值得抠细节的一块增删查改四类操作里日常开发使用频率最高的绝对是SELECT。但是很多人写SELECT还是“查询嘛不就是SELECT * FROM 表名 WHERE 条件”这种认知会限制你的成长。查询写得好不好直接决定你处理数据的效率上限。4.1 WHERE和ORDER BY条件筛选与排序的正确打开方式先说WHERE。WHERE后面跟查询条件等值比较、范围比较、模糊匹配、多条件组合都在这写。SELECT id, name, age FROM user WHERE age 18 AND age 30 AND status 1 ORDER BY age DESC, id ASC;这里有几个新手常见的操作误区SELECT *能不用就不用。显式列出你需要的字段既能减少数据传输量也能避免表结构变更时程序报错。我见过多少人图省事用SELECT *结果表里加了个超大text字段查出来直接拖垮网络传输。模糊查询LIKE %关键字%是查不到索引的因为索引最左前缀失效了。要优化的话尽量写成LIKE 关键字%或者上全文索引、搜索引擎。ORDER BY排序默认是ASC升序DESC降序。多字段排序时优先级是从左到右的先按第一个字段排相同再按后面的排。这个顺序别搞反。4.2 分页查询的经典写法与性能陷阱分页是后台管理系统里最常见的功能。最经典的写法SELECT id, name, age FROM user ORDER BY id LIMIT 100, 20;LIMIT后跟两个参数第一个是偏移量跳过多少条第二个是返回条数。上面这句的意思就是跳过100条取20条也就是第5页的数据。这个写法在小数据量时没毛病但数据量一大就会出问题。比如你要查第100万条往后的数据LIMIT 1000000, 20MySQL依然要从头扫描到1000020条然后丢掉前100万条再返回最后20条性能极差。优化的通用办法是“延迟关联”SELECT t.id, t.name, t.age FROM user t INNER JOIN ( SELECT id FROM user ORDER BY id LIMIT 1000000, 20 ) tmp ON t.id tmp.id;先通过覆盖索引找到目标行的主键再回表查询完整数据。因为子查询只查id字段可以直接走索引性能提升非常明显。我在一个百万级数据量的表上试过普通分页需要2-3秒优化之后几十毫秒完成。4.3 聚合查询GROUP BY和HAVING的分工不同统计报表是查询的重头戏。聚合查询的几个关键点是五个聚合函数COUNT、SUM、AVG、MAX、MIN、GROUP BY分组、HAVING分组后过滤。SELECT dept_id, COUNT(*) AS cnt, AVG(salary) AS avg_salary, MAX(salary) AS max_salary FROM employee WHERE status 1 GROUP BY dept_id HAVING COUNT(*) 10 ORDER BY cnt DESC;这里特别强调WHERE和HAVING的区别WHERE是分组前的行级过滤HAVING是分组后的组级过滤。WHERE salary 3000是先筛选出工资大于3000的员工再分组HAVING AVG(salary) 5000是分组之后过滤掉平均工资不达标的分组。不能把聚合条件写在WHERE里因为WHERE执行的时候聚合函数还没算出来。4.4 COUNT(*)到底是什么意思COUNT在面试里经常被问热搜里也有“mysql查询优化”相关的词。这里把COUNT讲透COUNT(*)统计所有行的数量包括NULL值。在InnoDB里它会对主键做记录统计。COUNT(1)跟COUNT(*)逻辑上等价就是给每一行加个常量1统计1的数量。很多人以为COUNT(1)更快其实在InnoDB 8.0中两者性能基本没有差别。COUNT(column)统计某列“非NULL”的行数。如果你统计的那一列有空值这个数字会小于总行数。很多人写统计时踩这个坑查出来的数字莫名其妙变少然后怀疑数据丢了其实就是NULL值被排除了。4.5 JOIN的背后是结果集的组装逻辑JOIN联表查询也是查询里的高频考点。热搜里有“mysql join含义”这里展开说一下。JOIN的本质把两张表按条件组合成一个结果集。常见的几种JOIN类型我用一句话分别总结INNER JOIN内连接只返回两张表中匹配成功的记录。LEFT JOIN左连接返回左表的全部记录右表中没有匹配的显示NULL。RIGHT JOIN右连接返回右表的全部记录左表没有匹配的显示NULL。CROSS JOIN交叉连接笛卡尔积即两张表所有行的组合。SELECT * FROM a CROSS JOIN b返回的行数是两表行数的乘积这个操作数据量大时极其危险。写JOIN的时候我强烈建议新手在脑子里过一遍这样的逻辑哪张表是驱动表先查哪张、关联条件写对没有、关联字段有没有索引。开发中最常见的问题就是ON后面的关联字段没建索引结果两张大表JOIN直接全表扫描查询慢到怀疑人生。5. UPDATE写更新语句之前先想三件事UPDATE操作风险等级比DELETE稍低但同样不能掉以轻心。执行UPDATE时我最怕看到的就是不带WHERE条件的语句。5.1 UPDATE的标准语法与常见用法UPDATE的基本语法UPDATE user SET age 26, status 1 WHERE id 1;要点SET后面跟要更新的字段和值多个字段用逗号分隔WHERE指定哪些行要更新。如果不写WHERE那结果就是全表更新这个操作在生产环境等于事故。凡是不带WHERE的UPDATE我已经形成了肌肉记忆一定要停下来确认自己是不是真想全表更新。除了基础用法还有一种场景经常用到把表中的某个字段统一更新成另一个字段计算后的结果。-- 把员工的工资统一上浮10% UPDATE employee SET salary salary * 1.1 WHERE dept_id 2;这里就是直接在SET里引用原字段。一条语句完成原本需要在程序里循环做的事情高效又安全。5.2 生产环境防误更新的三层保险我在生产环境执行UPDATE有三条铁律分享出来第一执行UPDATE之前先写一条SELECT看看条件命中了哪些行。比如你要UPDATE user SET status 0 WHERE expired_at 2024-01-01那就先执行SELECT id, status FROM user WHERE expired_at 2024-01-01确认影响范围是不是你预想的那批用户数量对不对。这一步30秒的时间能避免后面一晚上的火葬场。第二有条件的话把UPDATE包在事务里。先开事务执行UPDATE查一遍确认没问题再COMMIT不对就直接ROLLBACK。虽然InnoDB默认autocommit但你在一个显式事务里执行更新给自己留了后悔的余地。第三养成带LIMIT的习惯。如果你的UPDATE只打算处理一小部分数据可以加LIMIT限制行数避免一次锁太多行。但要注意UPDATE ... LIMIT只能限制影响行数不能缩小WHERE条件的范围千万别理解错了。5.3 UPDATE子查询一条语句更新多张表热搜里有“mysql中更新子查询”这个确实是个实用技巧。就是UPDATE的时候被更新的字段值来自另一张表的查询结果。UPDATE employee e SET e.dept_name ( SELECT name FROM department d WHERE d.id e.dept_id ) WHERE EXISTS ( SELECT 1 FROM department d WHERE d.id e.dept_id );这种写法在数据同步、冗余字段刷新场景下很常见。注意子查询返回的结果必须是单行单列否则会报“Subquery returns more than 1 row”的错误。6. DELETE删除数据是高风险操作先记好这几条DELETE是四条基本语句里风险等级最高的操作。原因很简单INSERT插错了还能再删SELECT查错了顶多慢点UPDATE更新错了还能再改回来DELETE删掉的数据如果不小心用了物理删除那是真正的“覆水难收”。6.1 DELETE的语法与TRUNCATE、DROP的区别DELETE语法本身很简单DELETE FROM user WHERE id 1;不带WHERE就是清空整张表这是新手最容易犯的灾难级错误。我把DELETE、TRUNCATE、DROP三者放一起对比区别十分清晰操作作用范围能否带WHERE速度能否恢复DELETE删除指定行可以慢逐行删、记录日志事务内可回滚TRUNCATE清空整张表数据不可以快不可回滚DROP删除整张表连结构一起删不可以极快不可回滚数据量大的时候DELETE性能很差因为每删一行都会记录事务日志。如果你想清空一张大表TRUNCATE比DELETE快了不止一个数量级。但TRUNCATE的代价是连日志都不写误操作之后没有任何后悔药。所以我的建议是日常业务删除用DELETE清空临时表、需要释放空间的场景才用TRUNCATEDROP不到万不得已别碰。6.2 软删除和物理删除为什么推荐软删除我强烈建议如果业务允许优先考虑软删除方案。简单说就是不是真的把记录从表里删掉而是给这行数据打上一个“已删除”的标记。具体操作是建表时加一个is_deleted TINYINT DEFAULT 0字段0表示正常1表示已删除。删除操作变成UPDATEUPDATE user SET is_deleted 1 WHERE id 1;查询的时候默认带一个条件SELECT id, name FROM user WHERE is_deleted 0;这样做的最大好处是数据永远都在误删了改回来就行甚至可以多一个字段记录删除时间和删除操作人后续做数据审计、恢复、历史追溯都方便。很多大型互联网公司对企业核心数据就是这套玩法。当然软删除也有代价每一条查询都要多带一个is_deleted条件而且要在is_deleted字段上考虑联合索引索引设计上会多费点心思。但跟数据误删带来的损失相比这点代价根本不值一提。6.3 DELETE之后自增主键的坑还有一个很容易被忽视的细节InnoDB的自增主键在你删除数据之后不会自动回退。也就是说你删了id100, 101, 102这几条记录再插入新记录id会从103开始而不是从99开始。这一点和MyISAM有区别也和TRUNCATE有区别TRUNCATE会把自增计数重置。这个不是bug是设计如此但很多半路转MySQL的人在这上面栽过跟头尤其是做数据导入导出的时候发现id号对不上整个人都是懵的。7. 索引增删查改变慢时最常见的解药热搜词里有“mysql创建索引”和“mysql性能调优”。这两件事是绑在一起的因为90%的查询性能问题都跟索引有关。7.1 常用索引类型和创建方式索引的本质是数据的“目录”。没有索引时查数据是全表扫描一行一行比对有索引后通过BTree跳着找效率指数级提升。最常用的是普通索引和唯一索引-- 普通索引 CREATE INDEX idx_user_name ON user(name); -- 唯一索引 CREATE UNIQUE INDEX uk_user_email ON user(email); -- 联合索引多列组合 CREATE INDEX idx_user_age_status ON user(age, status); -- 建表时直接加索引 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), age INT, email VARCHAR(100), INDEX idx_age (age), UNIQUE KEY uk_email (email) ) ENGINEInnoDB;7.2 索引不是越多越好索引有成本这里我必须泼盆冷水索引是把双刃剑。它确实能让SELECT变快但会让INSERT、UPDATE、DELETE变慢。为什么因为每次数据变更MySQL都要同步维护索引结构。一张表建了5个索引写一条数据就要同时更新主键索引和这5个二级索引索引建得越多写操作就越慢。所以删改查的“改”和“删”其实是被索引反向影响的。实际工作中的经验是索引只建在查询频率高、区分度大的字段上区分度太低比如性别字段只有男和女的枚举建索引基本无效查询优化器也不会走它。7.3 怎么确认索引有没有生效很多同学建了索引但查询还是很慢原因多半是索引没被用上。判断方法是用EXPLAIN关键字EXPLAIN SELECT id, name FROM user WHERE age 20 AND status 1;看返回结果里的type列和key列。type列里从好到差大致是system const eq_ref ref range index ALL。如果type显示ALL说明是全表扫描你的索引没生效。这时候排查方向一般是WHERE条件里对索引列做了函数运算、隐式类型转换或者是LIKE模糊匹配以通配符开头这些都会导致索引失效。7.4 联合索引的最左前缀原则联合索引是最容易理解错的。idx_user_age_status这个索引按(age, status)的顺序建立。它能加速的查询模式是WHERE age 18—— 命中第一列WHERE age 18 AND status 1—— 命中两列WHERE status 1—— 注意只用第二列查索引失效因为联合索引的排序规则是“先按第一列排再按第二列排”单独用第二列相当于在已经有序的结构里跳过了第一层没法二分查找。这叫“最左前缀原则”。设计联合索引时把区分度高的字段放左边把等值查询的字段放左边这些细节都有讲究。多提一嘴热搜词里还有“mysql锁表”这个东西。其实锁表和索引密切相关不带索引条件的UPDATE和DELETE在InnoDB里可能锁住全表因为行级锁需要索引来定位行一旦没有索引可用就会退化成锁全表。这解释了为什么我看别人代码时只要UPDATE的WHERE字段没有索引我就会提醒他小心锁表。这在高并发场景下是致命的。8. 高频翻车现场连接不上、密码问题、锁等待一次说清这些年在网上看到的问题、在公司带人遇到的问题汇总起来最集中的就是下面这几个。整理成一张速查表遇到问题直接对号入座。8.1 最常踩的五个坑和排查方法问题描述常见原因快速处理命令行登录报ERROR 2002 (HY000)MySQL服务未启动或socket路径不对systemctl status mysqld检查服务状态确认my.cnf里socket路径忘了root密码安装时记录丢失跳过权限表启动mysqld_safe --skip-grant-tables然后修改密码生产环境慎用本地能连、远程连不上bind-address只监听127.0.0.1修改my.cnf的bind-address为0.0.0.0检查端口放行确认用户Host授权UPDATE/DELETE语句耗时特别久WHERE条件没有索引导致锁表看EXPLAIN确认是否全表扫描加索引检查是否存在长事务锁等待偶发死锁多个事务同时更新并发冲突让更新顺序一致开启死锁日志innodb_print_all_deadlocks1定位8.2 事务和锁等待的真实排查体验在MySQL中“查询卡住不动”是最让人头疼的场景之一。有一次我执行一个UPDATE感觉执行了十几秒都没返回一开始以为数据量大后来一看是另一条忘了COMMIT的事务把这张表的关键行锁住了。这种情况在新手中极其常见因为很多人在Navicat或者Workbench里执行了一条UPDATE没提交就关了窗口但实际上数据库里那个事务还在跑锁还在那悬着。排查方法是执行SHOW PROCESSLIST;看State列有没有Locked、Waiting for lock之类的字样。确认之后找到阻塞源然后执行KILL 线程ID;把占用锁的事务杀掉。生产环境杀会话要小心最好是在维护窗口做但排查思路就是这条路。9. 从增删查改到真正的实战能力增删查改只是MySQL的入口不是终点。你在实际项目里遇到的那些名词——存储过程、数据库连接池、JDBC、主从复制、分库分表、性能调优——它们全都建立在你对基础SQL操作的理解之上。没有扎实的基础直接上分布式那些高级玩法就像没学会走路就去跑步只能摔跟头。拿存储过程来说它其实就是把多条SQL语句打包成一个可调用的函数里面能写IF判断、循环本质还是在操作增删查改。数据库连接池比如HikariCP不过是帮你管理大量的数据库连接复用。主从复制解决的是高并发读多写少的问题但从库同步的数据靠的还是INSERT和UPDATE。任何高级话题落到最底层全是你学过的这一条条SQL。我给还在学习阶段的朋友一个很实在的建议把官方文档里的SQL语句分类过一遍然后找一套真实业务数据比如电商订单、用户系统自己从建库建表开始把增删查改反复练。练到什么样算合格手边没有搜索引擎、没有文档的情况下你能在五分钟内写出一个包含条件筛选、排序、分组、多表关联的查询语句而且逻辑清晰没有语法错误这就说明你的基础真正扎实了。我个人在实际面试候选人的时候也特别喜欢从增删查改这种“最简单”的问题开始问。能把这四个操作讲透、讲出细节、讲出业务场景的人比那些张口就是什么分布式、高并发、缓存重建的候选人可靠得多。数据库这行最重要的是手里有真功夫嘴上说得天花乱坠不如写一条漂亮的SQL来得实在。
企业数字化 ERP 产品动态
相关推荐
Kafka消息不丢实战:acks、ISR、位移提交与幂等消费全解析 1. 消息到底在哪个环节丢的:先给可靠性画一张路线图做Kafka的人几乎都听过一句话:“Kafka不丢消息”。可真到了生产环境,因为消息丢失半夜被叫起来排查的人不在少数。为什么会这样?因为“Kafka不丢消息”从来不是一个默认事实&… · 2026/9/24 19:48:45
3ds Max渲染提速揭秘:置换开关与V-Ray/Corona参数优化全攻略 作为一个常年和3ds Max渲染打交道的人,我太熟悉那种场景了——场景做完了,灯光打好了,材质调得差不多了,点下渲染,然后看着进度条一格一格地爬,一杯咖啡从热喝到凉,图还没出来一半。你要是查过各… · 2026/9/24 19:48:45
钢材涨价倒逼仓储自动化:从成本结构到TCO测算 钢铁涨价这件事,放在制造业里算得上坏消息中的坏消息,但对于我所在的仓储自动化行业,最近却多少带点“救命稻草”的味道。我这不是在玩标题党——过去这大半年,钢材价格持续拉涨,传统货架、钢平台、托盘这些仓储硬件的… · 2026/9/24 19:48:38
番茄工作法在软件测试中的实战应用与落地指南 我想先聊一个场景:你坐在工位上,刚把一条用例的前置数据准备好,正准备开始执行,微信弹了需求变更,紧接着测试环境挂了,等环境的时候顺手刷了十分钟网页,等环境好了,刚才那条用例的逻… · 2026/9/24 20:24:23
外贸必备:集装箱类型、尺寸对照与装柜计算全攻略 做外贸这些年,我最大的体会是:很多新手一开始把精力全扑在找客户、谈价格上,结果货快出了,却在"装什么柜子、能装多少、怎么装"上栽了跟头。集装箱的类型与尺寸,看似是物流环节里最不起眼的基础知识… · 2026/9/24 20:24:23
重组人IL-6蛋白实验应用全攻略:从信号通路到临床转化 在生物医学实验室泡久了的人,对IL-6这个名字绝对不会陌生。白介素-6(Interleukin-6)可以说是整个炎症网络里最核心的枢纽分子之一,几乎所有跟免疫、炎症、肿瘤、自身免疫病相关的课题,绕来绕去都会碰到它。但真正动手去… · 2026/9/24 20:24:17
IL-6重组蛋白研究从信号通路到临床应用的完整指南 我们实验室和IL-6打交道快十年了,从最初拿重组蛋白做细胞增殖实验,到后来用各种突变体和中和抗体去拆解信号通路,再到近几年参与几个抗体药物的临床前评估,这一路踩过的坑、积累的经验,确实值得好好写一写。很多人问我… · 2026/9/24 20:24:17
Flask+微信小程序构建寻亲平台:全栈实战与部署指南 “宝贝回家”这几个字,对做技术的人来说,不应该只是新闻里的感人故事。它背后是一个极其典型的 Web 全栈实战场景:地理位置、图片存储、模糊搜索、状态流转、消息通知,全部都在一个小程序里。用 Flask 做后端,配合微信… · 2026/9/24 20:24:17
蓝牙SoC产线反复升级问题排查:以中科蓝讯BT5756C为例 做蓝牙音频方案这些年,中科蓝讯的芯片没少折腾,BT5756C算是我手里出镜率比较高的一颗。前两天刚好有个做耳机的客户找过来,说产线测试盒升级固件的时候遇到了个怪现象:固件烧进去了,板子也重启了,可没跑两秒… · 2026/9/24 20:24:17
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44