MySQL入门实践:从建库建表到增删改查避坑指南
第一次真正上手MySQL是在一个再普通不过的周末。起因很简单我在做一个模拟订单记录的小系统之前所有数据都躺在Excel里等到数据超过一万行每次按条件筛选都要卡半天更别说把用户、订单、商品串起来做关联统计了。朋友听完直接说换数据库吧从MySQL开始。于是我就这么稀里糊涂地开始了MySQL的第一次。现在回头看那次“第一次”其实把数据库最核心的东西都过了一遍安装部署、建库建表、增删改查、数据关联、踩坑恢复。整个过程谈不上优雅但收获非常大。这篇文章就把我这次完整的实践过程复盘给你内容包括MySQL的选型理由、安装配置步骤、建库建表的方法、SQL增删改查的实操示例、一个简单的用户订单实战项目以及新手最容易踩的5个坑。适合完全没碰过数据库、准备从零开始学MySQL的朋友也适合刚装了MySQL但不知道下一步该干什么的人。1. 为什么第一次选MySQL先搞清楚它解决什么问题1.1 从Excel到数据库我为什么需要MySQL先说说我在用MySQL之前的状态。我的所谓“数据管理”就是一张Excel表一列是用户ID一列是商品名一列是价格一列是日期。刚开始几百条数据毫无压力筛选、排序都很快。但到后面问题接踵而至数据一多打开文件就变慢操作起来经常无响应。想把订单表、用户表、商品表关联起来看公式写得极其痛苦VLOOKUP嵌套到怀疑人生。每次改动都没法追踪我永远不知道这批数据是“谁在什么时候改的”。多个人同时要改数据时文件锁来锁去根本没法协作。MySQL解决的正是这些问题。它的核心逻辑是把数据按“表”存储表之间通过关键字段建立关系然后使用SQL结构化查询语言统一操作。类比一下Excel像手写记账本翻页靠肉眼MySQL像带有索引、货架编号和盘点规则的仓库管理系统数据再多也能在毫秒级定位到具体记录。我第一次意识到“该上数据库了”就是当业务逻辑开始强调“关系”的时候订单要关联到某个用户、某个商品要按用户统计消费总额要按商品统计销量。这种场景用Excel能做但那只是“能做”不是“可维护”。数据库允许你用标准化、声明式的查询描述“我要什么”而不是像Excel公式那样一个单元格一个单元格地拼。1.2 MySQL到底是什么一张表说清核心概念在写任何SQL之前必须先搞清楚几个概念。我当时是一边操作一边理解这些词现在直接给你整理出来术语含义直观类比数据库Database存放多个数据表的容器一个仓库表Table存储某类实体的数据结构由行和列组成仓库里的一个货架行/记录Row一条具体数据货架上的一个格子列/字段Column数据的一个属性维度格子上贴的标签主键Primary Key唯一标识一条记录的字段每个货物的唯一编号索引Index加速查询的数据结构货架旁边的检索目录SQL操作数据库的标准语言与仓库沟通的指令MySQL本身是一个关系型数据库管理系统你通过SQL指令告诉它“创建表”“插入数据”“查询数据”“修改数据”它负责把这些指令翻译成底层的文件读写和内存操作。你不需要关心磁盘上数据怎么组织只需要把表结构设计好、SQL写对。1.3 为什么是MySQL而不是其它数据库我第一次选型时也纠结过要不要用SQLite要不要用SQL Server甚至要不要直接上PostgreSQL最终选择了MySQL原因很实际开源免费个人学习和中小型项目没有任何授权负担。资料极其丰富遇到问题搜索一下基本都能找到答案这对新手太重要了。安装部署简单跨平台服务端和客户端工具都齐全。行业使用率非常高学会了MySQL后续去理解其他关系型数据库的迁移成本很低。SQLite虽然更轻量但它更适合嵌入式场景比如手机应用、小型工具内部存储PostgreSQL功能更强但对第一次接触数据库的人来说MySQL的上手路径更平稳教程和案例更多。我认为新手的第一次稳定和资料丰富比“极客酷炫”重要得多。2. 第一次安装MySQL从下载到跑起来2.1 版本选择8.0还是5.7安装前先定版本。我用的MySQL 8.0系列这也是目前的主流版本。相比老版本8.0有几个对新手特别友好的点默认字符集就是utf8mb4几乎不用折腾中文乱码。支持窗口函数ROW_NUMBER、RANK等做数据分析更顺手。性能和安全机制整体更强。如果你去官网下载建议选择MySQL Community Server版本的8.0系列。我自己的做法是选择“免安装”的压缩包版本ZIP Archive原因很简单所有配置都在一个my.ini文件里启动和初始化逻辑清清楚楚出了问题能看到每一步的痕迹。图形安装包虽然省事但很多配置会被向导“藏起来”新手反而不容易理解到底发生了什么。2.2 初始化配置my.ini的关键参数下载解压后先别急着启动。MySQL在启动前需要知道几个关键信息数据文件放哪里、服务端口是多少、用什么字符集。这些信息都写在配置文件my.ini里。第一次配置时我在MySQL根目录下新建了一个my.ini内容大致如下[mysqld] # 端口号 port3306 # MySQL安装目录 basedirD:/mysql-8.0.xx # 数据文件存储目录 datadirD:/mysql-8.0.xx/data # 默认字符集 character-set-serverutf8mb4 # 默认存储引擎 default-storage-engineINNODB # 最大连接数 max_connections100 [client] default-character-setutf8mb4这里最需要注意的是datadir。MySQL的数据文件、日志文件都存放在这个目录里初始化之后千万不要乱删。我见过有人把data目录当成临时文件夹清理掉结果整套数据库直接报废。选INNODB作为默认存储引擎也值得说一句它支持事务、支持行级锁掉电崩溃后还有恢复机制。对现在的业务场景来说选InnoDB基本是默认答案不用犹豫。2.3 初始化数据库并启动服务配置文件就位后打开命令行窗口建议以管理员身份运行进入MySQL的bin目录执行初始化命令mysqld --initialize-insecure这一步的目的是生成系统数据库和初始数据。--initialize-insecure参数的意思是创建一个root用户密码为空。默认的--initialize会生成一个随机临时密码虽然更安全但第一次登录要找半天密码新手体验不佳。所以我用了insecure方式启动后再立刻设置新密码。初始化完成后注册成系统服务mysqld --install MySQL然后启动服务net start MySQL如果一切顺利你会看到“服务已经启动成功”的提示。这时候在命令行输入mysql -u root -p因为root密码为空提示输入密码时直接回车就能进入MySQL的交互环境看到mysql提示符。第一次看到这个提示符的时候还挺有仪式感的意味着你的数据库已经在本地跑起来了。2.4 登录后立刻要做的事改密码和配环境变量进入MySQL后第一件事就是设置root密码ALTER USER rootlocalhost IDENTIFIED BY 你设置的新密码;注意MySQL 8.0的密码认证插件默认是caching_sha2_password很多老客户端连接时会报认证失败。如果你要用一些比较老的图形工具连接可以考虑临时改回mysql_native_password不过这不是必须的优先保留新版本默认设置即可。另外我还建议把bin目录加入系统环境变量PATH。这样以后在任何目录下都能直接敲mysql命令不用每次都完整输入路径。这个操作在Windows的“环境变量”设置里加一行就行。到这里MySQL算是成功跑起来了。我第一次完成这些操作时反而很平静因为真正的挑战从建库建表才开始。3. 第一次建库建表先想清楚再动手3.1 需求分析我要存什么数据安装只是热身建表才是数据库设计的核心。我第一次建表时犯过一个典型错误看到别人写了什么字段我就照着复制完全不理解为什么。这样建出来的表后面改起来非常痛苦。正确做法是先做需求分析。当时我要做一个用户管理功能需要保存以下信息用户名、邮箱、密码、注册时间、账号状态。于是逐个字段想清楚用户名变长字符串最长大概50个字符。邮箱变长字符串需要唯一防止重复注册。密码绝对不能存明文存的是哈希值长度固定。注册时间时间类型。账号状态只有几个枚举值用很小的整数表示。想清楚这些之后建表就不是“抄作业”而是“按需求设计”了。3.2 字段类型怎么选字符、数字、时间的取舍字段类型选错是新手最常见的坑尤其体现在时间、小数和字符长度上。场景错误选择正确选择原因用户IDINT不设UNSIGNEDINT UNSIGNED不需求负数无符号能扩大上限价格FLOATDECIMAL(10,2)FLOAT有精度误差金额必须精确注册时间VARCHARDATETIME用字符串存时间无法做区间和排序性别VARCHAR(50)TINYINT枚举值用数字更省空间查询更快描述/备注VARCHAR(50)VARCHAR(255)长度要预留但也不宜滥用TEXT金额用DECIMAL这点值得单独强调。我第一次存价格时用了FLOAT后来发现0.10.2这种浮点运算会产生尾差金额明明应该是100.5算出来却是100.49999……对钱来说这是大忌。DECIMAL是定点数用字符串方式存储十进制数值不会出现这种误差。DATETIME和TIMESTAMP的区别也容易混淆。简单说DATETIME存的是“字面上的时间”不随时区变化TIMESTAMP存的是UTC时间戳会随数据库时区设置自动换算。对大部分业务系统来说DATETIME更直观、更稳定所以我主要用它。3.3 主键、自增和字符集三个默认选择背后的逻辑建表时这些选项基本都是“必选项”但很多人只知其然。我逐个说清楚主键主键是表的灵魂它唯一标识每一条记录。最推荐的做法是设置一个与业务无关的自增整数ID。为什么不建议用邮箱当主键因为业务字段可能会变比如用户改邮箱主键一旦变化会牵连所有外键引用代价极高。AUTO_INCREMENT自增字段让数据库自动分配ID你只管插入数据它保证每个ID不重复。要注意自增计数器不会因为删除记录而回退。UNSIGNED无符号整数意味着只能存0和正数。ID本来就是从0开始往上增长的没必要允许负数。utf8mb4这是MySQL 8.0时代最稳妥的字符集选择。注意utf8mb4和utf8不是一回事utf8在MySQL里最多只能存3个字节遇到emoji等4字节字符就会报错utf8mb4才是完整的UTF-8编码。3.4 第一个建表SQL完整示例一切想清楚之后我写下第一个建库建表语句CREATE DATABASE shop_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE shop_demo; CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, password_hash CHAR(32) NOT NULL COMMENT 保存MD5哈希结果, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常 0禁用, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逐行解释几个关键点INT UNSIGNED NOT NULL AUTO_INCREMENT无符号整数、不能为空、自增。三个属性组合起来ID列的完整语义就是“数据库自动生成的正整数编号”。NOT NULL约束该字段必须有值。用户名、邮箱这类业务核心字段空值没有意义必须在入库前保证存在。DEFAULT 1插入数据时如果不指定status自动填1表示用户默认状态正常。DEFAULT CURRENT_TIMESTAMP插入时不指定时间就自动填当前时间。UNIQUE KEY uk_email唯一索引防止两条记录出现相同邮箱。你试一次重复插入就会看到报错这是数据库在做它该做的事。我现在回想起来第一次建表时我犯了个小错误一开始忘了加COMMENT。字段注释在项目初期看不出价值但等表多了、字段多了回头看时没有注释的表就跟天书一样。所以从第一张表开始就养成写注释的习惯后面能省很多沟通成本。4. 第一次增删改查SQL语句的入门四件套4.1 插入数据INSERT的两种姿势表建好了接下来就写入数据。INSERT语句有两种写法但语义基本一样-- 方式一指定字段插入 INSERT INTO users (username, email, password_hash) VALUES (zhangsan, zhangsanexample.com, e10adc3949ba59abbe56e057f20f883e); -- 方式二多行同时插入 INSERT INTO users (username, email, password_hash) VALUES (lisi, lisiexample.com, e10adc3949ba59abbe56e057f20f883e), (wangwu, wangwuexample.com, e10adc3949ba59abbe56e057f20f883e);不需要给id和created_at赋值它们是自增和默认填充的。明确指定字段的插入方式更推荐因为一旦表结构有调整只写部分字段的SQL不会出错。给了明文示例密码的哈希值不代表让你存明文密码。从一开始我就把密码哈希后入库用的是MD5、SHA2之类的哈希算法。这里有个重要提醒真实项目中请使用更安全的密码哈希方案比如bcrypt、argon2不要直接用简单MD5。我第一次做的时候用MD5只是学习演示真实上线必须换成带盐的强哈希算法。如果我只执行方式二这种批量插入几条数据瞬间导入这比在Excel里一行行粘贴字段从容多了。4.2 查询数据SELECT的常用组合插入数据后查询是每天用得最多的操作。第一次用SELECT时我体会到了和Excel筛选完全不同的爽快感-- 查询全部用户 SELECT * FROM users; -- 只看用户名和邮箱 SELECT username, email FROM users; -- 条件过滤只看状态正常的用户 SELECT * FROM users WHERE status 1; -- 模糊查询名字里含“张”的 SELECT * FROM users WHERE username LIKE %张%; -- 排序按注册时间从新到旧 SELECT * FROM users ORDER BY created_at DESC; -- 分页一页显示10条看第2页 SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10;这些语法看着简单但每个都是核心能力WHERE做过滤ORDER BY做排序LIMIT控制返回数量LIKE做模糊匹配。我第一次做分页时一直分不清LIMIT和OFFSET的顺序后来记了一个口诀LIMIT后面跟的是“取几条”OFFSET后面跟的是“跳过几条”。再进一步你会发现组合使用的时候WHERE、ORDER BY、LIMIT的书写顺序是有讲究的真正执行时数据库也是按这个顺序理解的先过滤行再排序最后取前N条。写复杂SQL时按这个思路拆解就不会乱。4.3 聚合统计COUNT、SUM、GROUP BY一起上单独的查询只是取数统计才是分析。我很快就需要回答这类问题一共多少个用户每个状态的用户分别有多少-- 用户总数 SELECT COUNT(*) FROM users; -- 按状态分组统计 SELECT status, COUNT(*) AS cnt FROM users GROUP BY status; -- 统计最早注册时间、最晚注册时间 SELECT MIN(created_at), MAX(created_at) FROM users;GROUP BY当时让我绕了一点弯。它的逻辑是“把相同值的行合并成一组然后对每个组做聚合计算”。比如GROUP BY status会把status1的所有用户合成一组把status0的合成另一组然后分别数出数量。理解了“分组后再聚合”这个顺序后面学更复杂的函数就顺了。4.4 更新与删除带WHERE是铁律UPDATE和DELETE是最危险的两个操作因为它们一旦写错代价是灾难级的。最基本的写法如下-- 更新指定用户的邮箱 UPDATE users SET email new_emailexample.com WHERE id 1; -- 删除指定用户 DELETE FROM users WHERE id 1;删数据这件事上MySQL的DELETE是按行删除。如果你想整个表清空还有TRUNCATE语句可选。DELETE是逐行删除支持WHERE删除后还可以配合事务回滚TRUNCATE是直接把表的结构和数据一起重建速度更快但无法回滚。新手阶段我对TRUNCATE的态度是尽量别用等真正理解它的语义后再碰。关于事务我第一次做两条数据的变更时就吃了没经验的亏。比如“给用户A的余额减100同时给用户B的余额加100”如果第一条成功、第二条失败钱就平白无故少了。解决这个问题的机制就是事务START TRANSACTION; UPDATE accounts SET balance balance - 100 WHERE user_id 1; UPDATE accounts SET balance balance 100 WHERE user_id 2; COMMIT;两条更新语句都在事务里只有都执行成功才COMMIT提交任何一步失败都可以ROLLBACK回滚数据回到事务开始前的状态。这才是数据库保证数据一致性的基本盘。5. 第一次实战做一个简单的用户订单数据表5.1 从单表到多表数据关系怎么设计学会了单表操作我开始向真实场景靠拢一个订单系统。这里的核心不是怎么写SQL而是怎么设计表之间的关系。我的需求是有用户有商品用户下单买商品。这个场景包含三种最基本的关系一个用户可以有多个订单一对多。一个商品可以被多个订单引用一对多。一个订单同时关联一个用户和一个商品多对一。在MySQL里表达这种关系靠的是“外键字段”。所谓外键就是在一个表中存放另一个表的主键值。比如orders表里放user_id就表示“这条订单属于哪个用户”。查询时用JOIN把两张表连接起来数据关系就完全打通了。我当时没有立刻使用“正式”的外键约束FOREIGN KEY而是先通过业务逻辑维护字段关联。原因在于外键约束有它的代价插入时校验、删除时限制新手如果没理解透反而会在日常操作中被各种报错卡住。但字段关联本身也就是在orders表里存users表的ID这个思路任何时候都是对的。5.2 完整建表SQL商品表和订单表基于上面的设计我补建了两张表CREATE TABLE products ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE orders ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_id INT UNSIGNED NOT NULL, product_id INT UNSIGNED NOT NULL, quantity INT UNSIGNED NOT NULL DEFAULT 1, order_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_product_id (product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里我专门给user_id和product_id加了普通索引KEY原因是它们在查询中经常作为过滤条件。索引的作用类似于书的目录没有目录就只能全文翻页。虽然这两张表的数据量不大不建索引也能跑但养成“高频查询字段建索引”的习惯非常重要。随后我插入了一些模拟数据几个用户、几种商品、一批订单。插入时我特意构造了一个“某个用户的多个订单”场景方便后面做关联统计。5.3 关联查询JOIN第一次让我觉得MySQL真香数据就位后我第一次写出带JOIN的查询那种感觉记忆犹新。比如我想查每一条订单对应的用户姓名和商品名称SELECT o.id AS order_id, u.username, p.product_name, o.quantity, o.order_time FROM orders o JOIN users u ON o.user_id u.id JOIN products p ON o.product_id p.id ORDER BY o.order_time DESC;JOIN的语法第一次看觉得陌生但它的本质很简单把两张表“按条件拼在一起生成一张大表”然后再在这个大表上筛选字段。ON后面的条件就是“用什么字段来对上”比如o.user_id u.id意味着“订单表里的user_id等于用户表里的id时这两行属于同一条数据”。再看一个带统计的查询统计每个用户一共下了几个订单SELECT u.username, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id u.id GROUP BY u.id, u.username ORDER BY order_count DESC;这里我用LEFT JOIN而不是JOIN是为了把没有下过单的用户也显示出来订单数为0。INNER JOIN简写JOIN只保留两表能匹配上的行而LEFT JOIN保留左表所有行右表匹配不上就填NULL。第一次用的时候我在这里栽过一查数据发现人少了后来才明白JOIN类型会影响结果集的行数。5.4 子查询另一种组合思路除了JOIN子查询也是新手应该掌握的。“查出下单数量超过3个的用户”这类需求子查询写起来比JOIN更直观SELECT username FROM users WHERE id IN ( SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(*) 3 );先执行括号里的子查询得出满足条件的user_id列表再用IN在外层匹配。这个思路跟写普通逻辑一样先算“有哪些用户满足条件”再取这些用户的信息。不过要注意子查询在数据量大时性能可能不如JOIN学习阶段理解语义优先优化的事情数据量大了自然会逼你研究。6. 第一次踩坑实录新手容易犯的5个错误6.1 忘写WHERE全表更新和全表删除的灾难这是MySQL新手最著名的“翻车现场”。我在练习的时候就差一点酿成大祸——本来想更新一个用户的邮箱漏掉了WHERE条件UPDATE users SET email whoopsexample.com;如果这条语句真的执行了整张表的邮箱字段都会变成同一个值而且是不可逆的除非你有备份。DELETE同理我练手时曾在一个测试库执行过不带WHERE的DELETE清空了整张表数据当时后背一凉。虽然当时只是测试数据但这个教训刻骨铭心。从那以后我给自己立了个规矩写UPDATE和DELETE时先把SELECT和相同WHERE条件写出来确认要影响的行数符合预期再改成UPDATE或DELETE执行。操作之前多花五秒比操作之后花两小时恢复要划算得多。6.2 字符集不一致导致的中文乱码乱码问题在MySQL里非常经典。我第一次建库时用的默认字符集插入中文后查询出来全是“???”或一堆乱码。排查下来发现三层都可能出问题客户端连接字符集、数据库表字符集、数据本身存储字符集。正确的做法是三层统一-- 查看当前连接字符集 SHOW VARIABLES LIKE character_set%; -- 统一设置为utf8mb4 SET NAMES utf8mb4;同时建库、建表时要显式指定DEFAULT CHARSETutf8mb4。从MySQL 8.0开始默认字符集已经是utf8mb4新项目基本不会遇到这个问题但如果接手老库一定要先检查字符集设置。6.3 端口被占用数据库起不来启动MySQL服务时如果报“端口被占用”通常就是3306端口被其他进程占用了。我第一次遇到时一头雾水排查命令很简单netstat -ano | findstr 3306看到占用端口的进程号后用任务管理器定位到对应进程。如果确认是无用进程关闭它再启动MySQL如果这台机器上确实有其他服务死占着3306也可以在my.ini里换一个端口比如3307但要注意所有连接串都要同步改。6.4 忘记root密码用跳过授权表方式重置练习了一段时间后我发现root密码忘了登录时总是提示Access denied。当时慌了一下但MySQL提供了恢复思路以跳过权限验证的方式启动服务再重新设置密码。具体操作是先停掉MySQL服务然后在命令行启动时加上跳过授权表的参数mysqld --skip-grant-tables此时登录MySQL不需要密码mysql -u root进入后执行FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY 新密码;注意--skip-grant-tables模式下数据库是不做权限校验的相当于大门敞开这种方式只能在本地操作、用完立刻恢复正常模式绝不能在生产环境这么干。我重置完密码后立刻关闭这个模式了。6.5 没有备份习惯mysqldump救了我一次那次全表DELETE之后如果想恢复数据靠的只能是备份。MySQL的备份工具是mysqldump逻辑很简单把数据库的结构和数据导成SQL文件需要恢复时再执行这个文件。# 备份整个数据库 mysqldump -u root -p shop_demo shop_demo_backup.sql # 恢复数据库 mysql -u root -p shop_demo shop_demo_backup.sql我第一次备份时忘了加--single-transaction参数后来才知道这个参数可以在备份过程中不锁表保证业务正常进行。虽然我这种学习场景无所谓但从一开始就养成“备份前加事务参数、备份后检查文件大小”的习惯后面接手真实项目时会从容很多。现在我的习惯是改表结构前备份大批量更新前备份每周固定全量备份一次。尽量做到随时都有后悔药可吃。6.6 常见问题速查表报错/现象常见原因解决思路Access denied for user密码错误或权限不足检查密码核对用户主机限制Unknown database库名写错先执行SHOW DATABASES确认Data too long for column字段长度不够调整VARCHAR长度或改用TEXT类型Incorrect string value字符集不匹配统一utf8mb4执行SET NAMES utf8mb4Cant connect to MySQL server服务未启动或端口错误确认服务状态、端口是否被占用Duplicate entry for key唯一键冲突检查重复数据或修改业务逻辑这张表是我后来整理出来的几乎覆盖了我第一次学习和后面带新人时遇到的大部分问题。遇到报错不用慌先读英文错误信息大部分提示已经把原因说得很直白了再结合搜索引擎基本都能解决。结尾过后的一些实在体会第一次接触MySQL我的直观感受是它不是一个需要“背命令”的软件而是一套需要“建立思维模型”的系统。你要想的是数据结构怎么组织、关系怎么维护、操作会不会造成不可恢复的后果。把这三个问题想明白了SQL语法反而是最简单的那部分。我在这次实践里学到最值钱的一课是“边界感”UPDATE和DELETE必须清楚边界事务和备份是最后的兜底建表阶段多想十分钟后面能省十个小时写注释、统一字符集、给高频查询字段建索引这些琐碎的习惯比任何“高级技巧”都重要。最后分享一个小技巧每次操作数据库前先在心里过一遍“我要动的是哪张表、哪些行、会不会影响其他数据、改错了能不能回滚”。这四句话看起来简单但它能帮你避免几乎所有灾难性操作。MySQL的第一次磕磕绊绊但非常值得。如果你也正处在从文件表格走向数据库的阶段希望这份复盘能让你少走一些我走过的弯路。