ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL数据库与表操作实战指南:从建库建表到索引与同步

MySQL数据库与表操作实战指南:从建库建表到索引与同步 1. 先搞清楚数据库和表到底是什么关系数据库和表的操作是玩 MySQL 的第一道门槛也是后端开发每天逃不掉的日常。哪怕你用了十年的 ORM、天天写面向对象的代码最终落到磁盘上还是一张张二维表、一行行数据、一个个主键索引。很多人刚开始学 MySQL装完环境第一件事就是建库建表但真到了动手的时候要么被字段类型选错坑一把要么被 ALTER TABLE 锁表卡到怀疑人生要么干脆删库跑路失败被领导约谈。我先把这套东西讲透。这篇内容适合三类人刚入行的后端开发想把 SQL 基础打牢已经写了一年业务代码但没系统梳理过 MySQL 底层逻辑的人以及正在准备数据库相关面试、需要把表结构设计和操作细节理清楚的人。我会把数据库层面的操作、表层面的操作、字段设计、索引和同步全部走一遍中间穿插一些实际工作中踩过的坑。先理解一个最基本的层级关系MySQL 服务器实例下面有多个数据库Database每个数据库下面有多张表Table每张表里才是真正的一行行数据。这个三层结构和文件系统非常像——一个磁盘分区下面有多个目录目录里放文件文件里才是内容。所以你在命令行里敲 CREATE DATABASE实际上是在实例里面划出一个独立的命名空间而 CREATE TABLE是在这个命名空间里创建一个结构化的文件集合。为什么要设计这么多层因为隔离。一个小公司可能在同一台 MySQL 实例上同时部署了订单库、用户库、日志库通过数据库隔离保证彼此的权限、字符集、备份策略都可以独立设置。到了表这一层每一张表承担一个明确的业务实体比如用户表、商品表表里字段描述实体的属性。理解了这个分层逻辑后面所有的操作语句都不难记。2. 数据库层级的操作创建、查看、修改、删除2.1 创建数据库时字符集是第一个坑创建数据库的命令很简短CREATE DATABASE user_db;但直接这么建大概率会在后续踩到中文乱码的坑。因为默认字符集可能是 latin1 或者跟随实例配置如果实例没有设定 utf8mb4那么你往表里插入“张三”时存进去的可能就是一堆问号。正确的做法是建库时就明确字符集和排序规则CREATE DATABASE user_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;用 utf8mb4 而不是 utf8是因为 utf8 在 MySQL 里最多只支持3字节字符像 emoji 这种4字节符号存不进去utf8mb4 才是真正完整的 UTF-8。排序规则里的_ci表示 case insensitive也就是说查询时 apple APPLE 成立这对大多数业务场景更友好。建库时还有个容易忽略的点数据库名字的规范。我见过团队里有人用驼峰、有人用中划线、有人直接叫 Test123最后查问题的时候非常痛苦。建议统一小写字母加下划线比如user_db、order_2024避免因为大小写敏感导致的迁移麻烦。2.2 查看和修改数据库配置日常运维中确认当前库的字符集、校验规则和建库语句是个高频操作。-- 查看所有数据库 SHOW DATABASES; -- 查看当前正在使用的数据库 SELECT DATABASE(); -- 查看指定数据库的建库语句 SHOW CREATE DATABASE user_db; -- 查看字符集相关变量 SHOW VARIABLES LIKE %character%;如果你的实例还是老旧的 latin1想对已有数据库做转换可以用 ALTERALTER DATABASE user_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;注意这个操作只影响后续新建的表已存在的表需要单独转换。我遇到过几次数据库改完字符集还是乱码的情况都是因为表还是旧的字符集白白排查了大半天。2.3 删除数据库一句话千万谨慎DROP DATABASE user_db;在开发环境随手删一删没事生产环境一条 DROP DATABASE 等于把整库数据全部清掉连回收站都没有。我习惯在删除前先执行SHOW DATABASES再次确认并且用SHOW CREATE DATABASE看清是不是目标库。如果真的误删了唯一的恢复手段是回到备份里做恢复没有备份就只能认栽。所以我的建议是生产环境的账号权限、备份策略、操作审批流程这三件事在项目第一天就要定好不要等出了事故再补救。3. 表操作的核心字段类型和约束决定一切3.1 字段类型怎么选才算合理建表看起来简单真正要抠的是字段类型。类型选错了轻则浪费空间重则查询性能直线下降、数据精度受损。整数类型方面INT是默认选项但一个TINYINT1字节范围-128~127能搞定的事没必要用BIGINT。比如用户状态、性别标识、支付渠道编码用 TINYINT 不仅省空间语义上也更明确。只有像订单号、雪花ID这种大概率超过 21 亿的值才需要BIGINT。有个细节INT(11)这种写法只是显示宽度不影响存储范围千万别以为INT(5)就只能存五位数。字符串类型是最容易翻车的地方。VARCHAR是变长的按内容长度存储适合用户名、邮箱、备注这种长短不一的字段CHAR是定长适合身份证号、手机号、状态码这种固定长度的字段。注意VARCHAR要指定长度但长度不是越大越好——VARCHAR(255)和VARCHAR(5000)在内存临时表处理时的表现完全不同过长的 varchar 还会导致索引大小超标。时间类型我推荐直接用DATETIME范围大支持到 9999 年不会像TIMESTAMP那样有 2038 年问题。这里说的 2038 年问题可别不当回事如果系统要跑十年以上老旧的 32 位时间戳会在 2038 年溢出选 DATETIME 能直接绕开。3.2 约束主键、唯一、默认值是表结构的设计核心建表的时候约束就是业务规则在数据库层的表达。主键约束 PRIMARY KEY 的作用是唯一标识一行InnoDB 引擎下主键就是聚簇索引所以选主键要慎重。我在生产环境基本只用两种方案自增BIGINT或雪花算法生成的BIGINT。自增主键写入性能最好但分布式场景下容易撞车雪花ID没有顺序性但在分库分表场景下兼容性更强。特别注意不要在线上用 UUID 字符串做主键因为随机字符串写入时会导致大量页分裂插入性能差到让人怀疑人生。唯一约束 UNIQUE KEY 保证某个字段的值在表内不重复比如用户邮箱、手机号这种业务上天然不允许重复的字段就应该建唯一索引。但要注意唯一索引对 NULL 是放行的多个 NULL 可以共存设计时要想清楚业务上是否允许。默认值约束 DEFAULT 看起来不起眼但能省很多事。比如订单表的status字段默认 1待支付创建时间默认CURRENT_TIMESTAMP逻辑删除标记默认 0。热点词里提到的MySQL 设置默认值为 0其实就是类似这种场景CREATE TABLE order_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL, status TINYINT NOT NULL DEFAULT 0, deleted TINYINT NOT NULL DEFAULT 0, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );DEFAULT 0 的价值在于应用层代码可以少写大量状态赋值逻辑而且数据库层面的默认值不会因为代码漏传而产生 NULL。外键约束 FOREIGN KEY 招致的争议比较大。我在互联网公司工作这几年主流做法是业务表之间不建物理外键而是通过应用层保证逻辑一致性。原因是外键会在写操作时触发级联检查高并发下对性能影响明显而且一旦分库分表物理外键基本没法用。如果你在做传统单体系统外键还能用如果是互联网高并发业务建议只在设计文档里画逻辑关系不建物理外键。3.3 字段级别的坑NOT NULL 和保留字表设计阶段最容易被问住的一个问题字段到底该不该允许 NULL我的原则是能用 NOT NULL 就用 NOT NULL并给默认值。为什么因为 NULL 在索引、聚合、比较时都有特殊语义COUNT会无视 NULL 行WHERE name 也查不出 NULL 的记录NULL 参与计算时结果也是 NULL。你写应用层代码时还要处处判空纯属给自己添乱。还有一个小坑是字段命名撞了 MySQL 保留字。比如某次我把业务表里一个字段叫desc描述结果 SQL 直接报语法错误。解决办法是字段名加上反引号CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, desc VARCHAR(255) NOT NULL DEFAULT );但更推荐的做法是从源头规避直接改名成description或remark省得以后写 SQL 都要带反引号又丑又容易忘。4. 修改表结构ALTER TABLE 的高级操作4.1 增加和删除字段通常业务迭代到第二版就开始疯狂加字段。加字段的标准语句ALTER TABLE user_db ADD COLUMN nickname VARCHAR(64) NOT NULL DEFAULT AFTER real_name;AFTER real_name是控制字段位置如果省略默认在最后。注意在大表上加字段并不是立刻完成的MySQL 5.6 用了 INSTANT 或 INPLACE 算法时大部分 DDL 不阻塞 DML但对内存和磁盘还是有一定压力。在大表上执行 DDL 最好放在业务低峰期或者用pt-online-schema-change这种工具来跑避免长时间拿锁。删除字段就是一句话的事ALTER TABLE user_db DROP COLUMN nickname;但删字段和删库一样都要三思。之前处理过一个线上事故运维误删了用户表的某个业务字段该字段虽然在应用层看起来没用但下游报表任务还在定时读取结果第二天报表直接断层。删除之前建议先确认所有下游依赖最好只做逻辑下线而不是物理删除。4.2 修改字段名和类型改名ALTER TABLE user_db CHANGE COLUMN nick_name nickname VARCHAR(64) NOT NULL DEFAULT ;CHANGE的完整语法是必须写一遍新的字段定义所以很容易写漏默认值。还有一个更轻量的写法是RENAME COLUMNALTER TABLE user_db RENAME COLUMN nick_name TO nickname;改类型比如把某个字段从 VARCHAR 改成 TEXTALTER TABLE user_db MODIFY COLUMN bio TEXT NOT NULL;MODIFY不改变字段名只需要写新定义但同样的坑是容易忘记保留原有约束和注释。我建议改字段类型时用SHOW CREATE TABLE先把当前定义完整抄一遍再在上面改比凭记忆写稳妥得多。4.3 锁表与在线 DDL很多人在数据库课程里学过 ALTER TABLE但没人提醒你这操作可能锁表。MySQL 5.5 及更早版本做 DDL 大部分是 COPY 算法先把整张表复制一遍期间所有写入被阻塞。5.6 之后 InnoDB 支持了 ONLINE DDL大部分增删字段操作可以在线完成但重建索引、修改主键这类操作还是会锁。真实场景里一个千万级用户表上直接跑ALTER TABLE ... MODIFY COLUMN高峰期拖了十几秒应用层请求超时紧接着就是雪崩。所以我现在对大表 DDL 的处理原则是评估表大小 → 选低峰期 → 先在小表上演练 → 必要时用工具拆分执行。这套流程不复杂但能避免大多数由改表引发的生产事故。5. 数据操作四件套INSERT、UPDATE、DELETE、SELECT5.1 INSERT 的几种常见写法最简单的插入INSERT INTO user_db (name, age) VALUES (张三, 18);批量插入建议一条语句多值INSERT INTO user_db (name, age) VALUES (张三, 18), (李四, 19), (王五, 20);好处显而易见一次网络往返事务开销远小于逐条 INSERT。但如果要插入 10 万行这种量级VALUES列表太长也会导致单条 SQL 过大建议分批比如每批 100~500 条兼顾性能和事务粒度。还有一个高频使用的 INSERT 扩展主键冲突时的处理INSERT INTO user_db (id, name) VALUES (1, 张三) ON DUPLICATE KEY UPDATE name VALUES(name);这种写法的核心价值是幂等重复执行不会报错适合在同步、补数场景用。注意MySQL 8.0 之后推荐用新语法AS new代替VALUES()但老写法也还能工作。5.2 UPDATE 和 DELETE不加 WHERE 就是事故更新语法UPDATE user_db SET age age 1, update_time NOW() WHERE id 1001;这里有个容易被忽略的点SET age age 1这种原子操作比先 SELECT 再 UPDATE 安全得多。因为并发下先查后改会出现丢失更新而一条 UPDATE 语句在数据库内部是行锁级别的原子操作。DELETE 同理DELETE FROM user_db WHERE id 1001;不加 WHERE 的 DELETE 整表清空是 DBA 最怕看到的操作之一。为了规避风险很多团队开发库权限里干脆不给 DELETE 不带 WHERE 的权力。如果你只是想清空表数据用TRUNCATE TABLE user_db更高效但它不能带 WHERE并且会重置自增ID。想保留主键 ID 继续累加、又想清数据可以用 DELETE 不带 WHERE但生产环境一般不建议这样宁可先确认再操作。还有个大坑是 DELETE 和 UPDATE 的 WHERE 条件走了慢查询。如果不小心在几十万行的表上执行DELETE FROM order_log WHERE status 0而 status 字段没有索引MySQL 会做全表扫描行锁逐步升级最后整个表的写操作被堵死。所以删除前先跑一遍SELECT COUNT(*)验证数据量并确认条件字段有索引这是基本职业素养。5.3 SELECT 的查询基础和排序查询是最常写的 SQL但基础语法值得反复推敲SELECT id, name, age FROM user_db WHERE age 18 ORDER BY create_time DESC LIMIT 20;ORDER BY对应的就是热词里的 mysql 排序。默认 ASC 升序DESC降序。排序字段如果没有索引MySQL 就要做 filesort把结果集先放到内存或磁盘排序数据量一大就会变慢。所以要记住频繁排序的字段尽量建索引多字段排序要保证顺序一致比如ORDER BY age DESC, id DESC索引设计也要按相同的顺序来。LIMIT分页的坑更隐蔽。在深分页场景下LIMIT 100000, 20这种写法MySQL 要先读前面 10 万行再丢弃性能极差。我在做后台系统翻页时被这问题坑过页面到第 1000 页直接卡死。后来换成基于上一页最大 ID 的写法性能提升了几个数量级SELECT id, name, age FROM user_db WHERE age 18 AND id 100001 ORDER BY id ASC LIMIT 20;这种方案适合数据只追加、不频繁删除的场景。如果数据会删除用时间戳或上一页最后一条记录的排序字段来做游标思路是一样的。6. 索引操作建了索引就万事大吉吗6.1 索引的基本语法创建索引ALTER TABLE user_db ADD INDEX idx_age (age);或者在建表时直接声明CREATE TABLE user_db ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, age INT NOT NULL, KEY idx_age (age) );查看索引SHOW INDEX FROM user_db;删除索引ALTER TABLE user_db DROP INDEX idx_age;索引为什么能加速查询核心就是底层用了 B 树组织数据查找一个值只需要沿着树从根节点走到叶子节点查询路径长度基本是树的高度而树的高度在数据量千万级时也才三四层所以查询时间接近常数。生活化类比就是查字典你不可能从第一页翻到最后一页而是根据偏旁部首先定位到页码区间再快速锁定具体条目。6.2 辅助索引与回表问题热词里有一条很扎心辅助索引如何避免回表。我来把回表这个事讲清楚。InnoDB 的数据存储在聚簇索引主键索引里叶子节点直接就是整行数据。辅助索引普通二级索引的叶子节点存的是主键值不存完整行。所以当你用WHERE name 张三查询如果 name 上有辅助索引MySQL 先在辅助索引里找到 张三 对应的主键 id再用这个 id 去聚簇索引里查整行这个过程就是回表。回表多一次磁盘 IO数据量大时性能差距明显。避免回表的方法就是让辅助索引覆盖查询需要的所有字段。比如你只查 id 和 nameSELECT id, name FROM user_db WHERE name 张三;只要建立一个联合索引 (name, id)——实际上 name 索引本身就带了主键 id所以直接建 name 索引就够了不需要 select * 就不会回表。更常见的场景是查询 name 和 ageCREATE INDEX idx_name_age ON user_db (name, age);这样辅助索引的叶子节点里既存 name 又存 age 还带主键 id查询时一查一个准根本不用回表。这就是热词里说的覆盖索引。设计辅助索引时把查询最频繁的列放在联合索引前面这就是最左前缀原则也是面试必问的一个点。6.3 索引失效的常见场景我讲几个实践中踩过多次的索引失效场景大家可以对着自查在索引列上做计算或函数操作WHERE YEAR(create_time) 2024索引直接失效应该改成WHERE create_time 2024-01-01 AND create_time 2025-01-01。隐式类型转换字段是 VARCHAR却传了整数 123优化器一看类型不对索引失效。前导模糊查询LIKE %关键字因为不知道前缀是什么B 树没法定位。但LIKE 关键字%是可以走索引的。联合索引不满足最左前缀索引是 (name, age)但你只按 age 查询索引走不了。索引不是越多越好。每张表索引太多写入时维护成本成倍增加重建索引变慢存储空间也涨。我见过新人为了查询快给一张表建了十几个索引结果写入性能崩了。合理的索引应该服务核心查询每增加一个索引你都要能说出它服务哪几条高频 SQL。7. 表同步、主从复制与远程库同步7.1 主从复制原理简述热词里出现了一系列跟同步相关的关键词怎么使用 MySQL 主从复制、把远程库的这张表同步到本地、数据库同步软件。只要系统开始有多台实例同步就是绕不开的话题。主从复制的核心原理简单说就三步主库写数据的时候会产生二进制日志 binlog从库的 IO 线程把主库的 binlog 拉过来写入自己的中继日志 relay log然后从库的 SQL 线程把中继日志里的 SQL 重放执行。整个过程是异步且追加式的所以从库不会阻塞主库写入。配置主从复制的大概步骤主库开启 binlogmy.cnf中配置log_bin mysql-bin、server_id 1。创建复制账号并授权CREATE USER repl% IDENTIFIED BY password; GRANT REPLICATION SLAVE ON *.* TO repl%;主库执行SHOW MASTER STATUS;拿到当前 binlog 文件和位置。从库配置CHANGE MASTER TO MASTER_HOST 192.168.1.10, MASTER_USER repl, MASTER_PASSWORD password, MASTER_LOG_FILE mysql-bin.000001, MASTER_LOG_POS 154; START SLAVE;在从库执行SHOW SLAVE STATUS\G重点看Slave_IO_Running和Slave_SQL_Running是否都是 Yes。这个方案适合主丛架构、读写分离以及灾备场景。但注意几点默认复制是异步的主库挂了没来得及把最新 binlog 传给从库从库数据就会滞后复制是单线程的MySQL 8.0 支持增强的多线程复制大事务会拖慢从库追进度。日常巡检我必看Seconds_Behind_Master这个值越接近 0 越好。7.2 不同场景下的同步工具选型主从复制解决的是实例之间的复制但实际工作中还有一种更轻的需求把远程库的一张表定期同步到本地或者把线上表拉一份到测试环境。这种场景用主从复制太重却有更简单的工具和方案。我常用的方案有几个层次单表单次同步全量覆盖直接mysqldump导出再导入简单粗暴。直接通过 SQL 建表并同步数据用CREATE TABLE local_db.t1 LIKE remote_db.t1;复制表结构再INSERT INTO local_db.t1 SELECT * FROM remote_db.t1;灌数据。定期、增量同步可以用 MySQL 自带的 binlog 解析工具也可以用开源的数据同步组件。选型思路看数据量和实时性要求。如果只是测试环境偶尔拉一把mysqldump足够如果要准实时同步就要用专业的同步工具做增量解析。7.3 远程表同步到本地的实操步骤针对把远程库的这张表同步到本地这个具体需求我给出一个最直接的实操方案第一步确认远程库的访问权限和本地的网络连通性。生产环境一般会有跳板机本地直连不了需要先确认策略是否允许。第二步用mysqldump导出单张表mysqldump -h 192.168.1.100 -u username -p database_name table_name table_dump.sql第三步把导出的 SQL 文件拷贝到本地机器然后在本地库执行mysql -u localuser -p local_database_name table_dump.sql第四步验证数据量和行记录SELECT COUNT(*) FROM local_database_name.table_name; SELECT * FROM local_database_name.table_name ORDER BY id DESC LIMIT 5;这种全量同步方案的问题在于每次都是全量覆盖数据量大时耗时很长。更科学的做法是增量同步核心思路是利用主键或时间戳字段做增量抽取INSERT INTO local_table SELECT * FROM remote_table WHERE update_time 上次同步时间;前提是源表有update_time字段且会自动更新。我在做数据仓库 ETL 时用的就是这种方案每天凌晨从线上库抽取前一天更新过的记录清洗后落库比每次全量拉取快得多。8. 常见错误与排查技巧实录8.1 报错信息速查表我整理了一份高频报错对照基本覆盖了新手到中级开发者最常遇到的几种问题报错信息原因解决方案ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sockMySQL 服务没启动或者客户端与服务器 socket 路径不一致检查服务状态systemctl status mysql或service mysql status确认 socket 路径在 my.cnf 中设置一致ERROR 1045 (28000): Access denied for user账号密码错误或账号没有主机访问权限检查授权GRANT ALL ON db.* TO userhost刷新权限FLUSH PRIVILEGESERROR 1064 (42000): You have an error in your SQL syntaxSQL 语法错误常因保留字未加反引号或逗号位置不对用SHOW CREATE TABLE核对字段定义检查关键字是否带反引号ERROR 1175 (HY000): Safe update mode客户端开启了安全更新模式DELETE/UPDATE 没有带主键或者索引条件临时关闭SET SQL_SAFE_UPDATES 0或给 WHERE 条件加上索引字段ERROR 1216 (23000): Cannot add foreign key constraint外键关联的字段类型或字符集不一致检查两侧字段类型、字符集、排序规则是否一致ERROR 1347: table is not BASE TABLE视图或临时表被当成普通表操作用SHOW TABLE STATUS查看表类型确认对象是表还是视图其中 ERROR 2002 是最容易让新人崩溃的报错。很多人刚装完 MySQL执行mysql -u root -p就撞上这个以为是密码错了其实是服务没起来。先ps -ef | grep mysqld确认进程在不在再检查 socket 配置。如果是 Docker 部署要注意 socket 文件是否映射到宿主机端口是否真的暴露出来。8.2 锁表与阻塞的排查热词里有 mysql 锁表这确实是日常运维里很头疼的问题。数据库长时间不返回大概率是锁等待。可以用下面的 SQL 查看当前有哪些锁等待SELECT * FROM information_schema.INNODB_TRX\G;再看阻塞关系SELECT * FROM sys.innodb_lock_waits;定位到持锁事务后如果确认是无用的空闲事务可以把它 KILL 掉KILL 12345;这里的 12345 是trx_mysql_thread_id不是事务 ID。我曾经遇到过一个定时任务每半小时跑一次事务结果代码里忘了 COMMIT事务一直持锁不放导致所有写操作排队MySQL 连接数瞬间打满。从那以后我给自己定了个规矩凡是写操作一定要看清事务边界代码 review 时重点看有没有遗漏COMMIT或ROLLBACK。SELECT ... FOR UPDATE是另一个容易出锁的地方。它会给命中的行加排他锁锁没释放前其他事务连读某些隔离级别都会受影响。如果你在代码里连续执行多条这种语句要确保它们顺序一致否则并发事务互相等对方的锁就会出现死锁。MySQL 遇到死锁会选择回滚其中一个事务但业务方会收到报错所以更好的做法是应用层加锁顺序约定。8.3 数据误删后的补救经验误删是每个开发者的噩梦但有些情况其实可以补救。MySQL 事务意味着还未提交的 DELETE 可以直接ROLLBACK撤销。如果已经提交了那就看 binlog 是否开启。如果 binlog 是 row 格式日志里记录了变更前的原始数据理论上可以用工具反解出 DELETE 语句数据并重新插入。这个过程极其痛苦而且依赖工具熟练度不是每个人都能在半小时内搞定。所以我强烈建议重要的表日常就要做备份至少要有周期性全量备份加 binlog 增量。很多公司会做每天凌晨全量备份但年轻人容易忽略的是备份的恢复演练。备份文件从来没恢复过等于没有备份。我见过同事在事故当天打开备份文件发现文件损坏、或者备份脚本凌晨报错没人看见日子一长备份早就断了。定期抽时间做一次恢复演练在测试库把备份恢复出来对账这是最稳妥的保险。8.4 几件我会一直坚持的实操习惯最后分享几个我长期踩坑后总结的习惯这些不算高深理论但真的能救命第一所有 DDLCREATE、ALTER、DROP执行前先把SHOW CREATE TABLE全量留档任何操作都有退回依据。改完结构后立刻更新留档避免文档和实际结构越差越远。第二写 UPDATE 或 DELETE 时把 WHERE 条件先写成 SELECT 验证行数SELECT COUNT(*) FROM user_db WHERE id 1001; -- 确认无误再执行 DELETE FROM user_db WHERE id 1001;这个习惯在测试环境看不出价值在线上环境能拦住 90% 的误操作。第三建表语句永远显式声明字符集、默认值和注释。注释尤其重要因为半年后再看这张表光看字段名根本想不起它存的是什么。每个字段都写 COMMENT这个成本极低收益极高。第四生产环境不要乱用SELECT *。一方面浪费 IO另一方面如果表结构变化应用层拿到的列顺序变了可能直接抛异常。明确列出需要的字段既是性能习惯也是代码健壮性要求。第五对自己的数据库操作权限要有敬畏感。公司给你生产库的写权限不代表你可以在任何时间执行高危 SQL。高风险操作尽量在夜间低峰期做并且提前通知团队观察。数据库和表的操作看起来简单但要真正做到不出事故、不埋坑、性能好背后靠的还是对底层原理的理解和一次次真实故障中沉淀下来的经验。这篇几乎涵盖了我在一线干活时每天都会用到的那部分 MySQL 知识希望你的第一步踩得比我当年稳。
返回列表