首页 / 资讯中心 / 文章详情

MySQL入门实战:从基础语法到高频查询与索引优化

MySQL入门实战:从基础语法到高频查询与索引优化 ★ FEATURED ARTICLE
干这行久了会发现一个规律不管你是做后端、搞数据分析还是转向运维MySQL都是绕不开的那道坎。市面上大多数项目的核心数据最终都躺在某个MySQL实例里。而“基础语法”和“高频查询”这两个词恰恰是新手最容易轻视、又最需要扎实掌握的区间——语法不难难的是知道“什么场景该用什么写法”。这篇是《MySQL 入门》系列的第 01 篇我把从建库、建表、增删改到单表查询、聚合分组、多表连接、索引认知这一条链路完整串了一遍不堆砌手册式的罗列而是从“一个真实业务需要的数据操作”出发把高频场景下的正确写法、执行顺序、性能隐患和经验教训讲透。适合刚接触数据库的初学者也适合学过一部分、但基础比较松散的人用来查漏补缺。1. 为什么第一门数据库课应该选MySQL1.1 不只是因为“到处都在用”而是因为它足够标准MySQL 是全球使用最广泛的开源关系型数据库之一。早期的 LAMP 架构、后来的 LNMP再到云时代的 RDS、各类 SaaS 系统的底层存储MySQL 几乎无处不在。初学者选它作为第一门数据库最大的理由不是“热门”而是它的语法和设计思路足够规范迁移成本极低你今天学的是 MySQL明天接触 PostgreSQL、Oracle、SQL Server会发现核心概念基本相通无非是数据类型细节、函数名称、分页写法有差异。先学 MySQL等于掌握了 SQL 世界的“普通话”。1.2 你在业务中会遇到的高频场景基本都能覆盖Web 应用的用户数据存储注册、登录、个人资料读写电商系统的商品、订单、库存管理日志、统计、报表类查询按时间段聚合、分组统计、多表关联大数据生态的边缘组件Flink、ClickHouse 等都提供了与 MySQL 对接的同步或维表查询能力面试考核的常客后端岗位面试必问 SQL 基础、事务、索引、锁。所以这篇虽然挂的是“入门”的名号但内容其实是硬通货。学完这一篇你会得到一个完整可用的知识框架而不是道听途说拼出来的一堆零散片段。1.3 这一篇的学习路径设计我把内容设计成一条“业务主线”先准备环境然后建库建表往里填数据再通过查询把数据取出来多表拆开后再用 JOIN 和子查询重组最后聊一聊索引——这是每个新人都会经历的一条从“存数据”到“取数据”再到“取快数据”的路径。你不需要背诵任何东西跟着敲一遍效果远好过啃语法手册。2. 安营扎寨版本选型、环境配置与连接工具2.1 版本到底怎么选8.0 还是 5.7我见过太多新手卡在第一步一搜“mysql安装教程”出来一堆 5.7 和 8.0 混杂的文章。这里直接给结论2025 年的现在但凡你不是在维护一套存量 5.7 系统一律装 8.0 系列。原因很简单8.0 是主流版本新特性、新性能优化都在 8.0 上持续迭代8.0 默认字符集是 utf8mb4对中文、Emoji、生僻字的支持更省心8.0 的窗口函数、通用表表达式 CTE、索引跳跃扫描等能力在写复杂统计查询时能少掉不少头发官方对 5.7 的支持已经进入生命末期新项目没有必要“起步即过时”。如果你看到某些教程还在教 5.7大概率是因为教程作者要兼容老旧项目。新学的同学可以直接忽略。2.2 操作系统不同安装路子也不一样Windows 环境下最省事的两种方式下载官方网站的 MSI 安装包图形化点击下一步适合完全新手下载 ZIP 绿色版解压后命令行初始化适合喜欢掌控细节的同学也方便后续多版本切换。ZIP 版的关键步骤如下Windows 上我实测过多次按这个顺序基本不会翻车。先解压到D:\mysql-8.0.x-winx64然后在系统环境变量Path里加上D:\mysql-8.0.x-winx64\bin。之后在解压目录下新建一个my.ini配置文件内容可以参考[mysqld] basedirD:/mysql-8.0.x-winx64 datadirD:/mysql-8.0.x-winx64/data port3306 character-set-serverutf8mb4 default-storage-engineINNODB接着以管理员身份打开命令行依次执行mysqld --initialize-insecure mysqld --install net start mysql--initialize-insecure会生成一个空密码的 root 用户首次登录后立刻设置密码这是每个新手最容易忽略的一步。Linux 上则简单很多以 CentOS 为例sudo yum install -y mysql-server sudo systemctl start mysqld sudo systemctl enable mysqldUbuntu 系推荐用 apt 安装mysql-server包。提示8.0 的 root 账号默认认证插件是caching_sha2_password很多老版本的客户端工具会连不上报错Authentication plugin caching_sha2_password cannot be loaded。遇到这个问题要么升级客户端要么在创建用户时显式指定IDENTIFIED WITH mysql_native_password BY 你的密码。2.3 连接工具命令行永远先征服再谈图形化不少新手一上来就装 Navicat反而把 SQL 基础丢了。我的建议是前两周老老实实用命令行。因为命令行会强迫你记住语法结构报错也更直接能帮你培养对 SQL 的“语感”。等基础稳了再上图形化工具提升效率。常用的连接工具有这么几个我直接列个表对比工具免费/付费适合场景备注MySQL 官方命令行免费学习、脚本操作必会无任何图形界面MySQL Workbench免费建表可视化、ER 图官方出品功能全但偏重DBeaver Community免费开源多数据库管理支持 MySQL、PG、Oracle 等推荐Navicat付费生产环境操作界面友好但 License 不便宜命令行登录的基本姿势mysql -uroot -p然后输入密码。如果 root 还没设密码在 8.0 里用ALTER USER来设置ALTER USER rootlocalhost IDENTIFIED BY 你的新密码; FLUSH PRIVILEGES;连接远程数据库时加-h指定主机mysql -h192.168.1.10 -P3306 -uroot -p注意远程连接前要确认 MySQL 用户是否允许外部主机访问默认的rootlocalhost只能在本机登录。开发环境里要允许远程访问可以创建一个专用账号而不是直接放开 root 的限制否则安全上迟早要交学费。3. 建库建表与增删改DDL 和 DML 的固定套路3.1 建库字符集和排序规则别乱选数据库层面的第一件事是建库。很多新手用默认设置一路回车结果后面插入中文出现乱码才回头找原因。建议建库时明确指定字符集CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;utf8mb4是 8.0 的默认字符集兼容全量 Unicodeutf8mb4_general_ci是常用排序规则ci表示不区分大小写搜索和排序时更符合业务直觉如果确实需要区分大小写可以用utf8mb4_bin。查看当前有哪些库SHOW DATABASES;切换库USE shop;3.2 建表先想清楚要存什么再动手写 CREATE建表是新手最应该花时间琢磨的环节。先看一个精简但完整的例子——用户表和订单表CREATE TABLE user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 用户名, phone CHAR(11) DEFAULT NULL COMMENT 手机号, age TINYINT UNSIGNED DEFAULT 0 COMMENT 年龄, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;这里有几个新手必须吃透的点BIGINT UNSIGNED做主键基本是互联网业务的标准做法容量大、自增不费心VARCHAR(50)存用户名CHAR(11)存手机号定长的用CHAR更省空间变长文本用VARCHARNOT NULL DEFAULT要养成习惯尽量不让字段为NULL因为NULL在查询、索引、聚合时有一堆额外的坑UNIQUE KEY给用户名加唯一约束这是业务上最实用的防重复手段ENGINEInnoDB是默认引擎支持事务和行级锁不需要考虑 MyISAM。订单表自然也会指向用户表CREATE TABLE order ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL COMMENT 下单用户, amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 订单金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0待支付 1已支付 2已取消, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;DECIMAL(10,2)是存钱的标准类型千万不要用 DOUBLE 存金额浮点数精度问题会让财务数据出现一分钱的误差这在真实项目里是红线级别的问题。3.3 增删改INSERT、UPDATE、DELETE 的用法与差异插入数据的分批写法INSERT INTO user (username, phone, age) VALUES (zhangsan, 13800000001, 25), (lisi, 13800000002, 30);更新时要牢记先写 WHERE再写 SET这个顺序能保命。UPDATE user SET age 26 WHERE username zhangsan;删除数据DELETE FROM user WHERE id 1;只清空表但要保留表结构用TRUNCATETRUNCATE TABLE order;DELETE和TRUNCATE的区别是DELETE 是逐行删除走事务可以搭配 WHERE 条件TRUNCATE 是直接重建表速度快但不可按条件筛选、不可回滚。修改表结构的常用语句也一并列出ALTER TABLE user ADD COLUMN nickname VARCHAR(50) DEFAULT NULL; ALTER TABLE user MODIFY COLUMN age SMALLINT UNSIGNED DEFAULT 0; ALTER TABLE user DROP COLUMN nickname;经验生产环境的表结构变更一定要在低峰期执行并且提前备份。ALTER TABLE在数据量大时会锁表尤其是 5.7 之前的部分场景8.0 的在线 DDL 已经改善不少但仍然要谨慎。4. SELECT 查询逐段拆解高频查询的底层执行顺序4.1 SQL 的书写顺序和执行顺序不是一回事这是新手和老手最大的分水岭之一。书写顺序是这样SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...但 MySQL 内部的执行逻辑顺序是FROM - WHERE - GROUP BY - HAVING - SELECT - DISTINCT - ORDER BY - LIMIT理解这个顺序有什么用最直接的价值是你在 WHERE 里不能使用 SELECT 中定义的别名但在 ORDER BY 里可以。举个例子-- 会报错WHERE 先于 SELECT 执行别名 c 还不存在 SELECT username AS c FROM user WHERE c zhangsan; -- 没问题ORDER BY 在 SELECT 之后执行 SELECT username AS c FROM user ORDER BY c;这个知识点面试爱问实际写复杂查询时也经常踩。4.2 WHERE 条件与常见运算符别把过滤逻辑写在 SELECT 里WHERE 是高频查询的绝对主力负责在行级别过滤数据。常用运算符比较、、、、、不等逻辑AND、OR、NOT范围BETWEEN ... AND ...集合IN (...)、NOT IN (...)模糊匹配LIKE、NOT LIKE几个典型的查询场景-- 查询年龄在 20 到 30 之间的用户 SELECT id, username, age FROM user WHERE age BETWEEN 20 AND 30; -- 查询手机号以 138 开头的用户 SELECT id, username, phone FROM user WHERE phone LIKE 138%; -- 查询用户名为 zhangsan 或 lisi并且年龄大于 20 SELECT id, username, age FROM user WHERE (username zhangsan OR username lisi) AND age 20;注意AND的优先级高于OR所以多个条件混用时加括号永远比赌运气稳妥。LIKE的模糊匹配里%表示任意多个字符_表示单个字符。LIKE 138%能用到索引LIKE %138则无法走索引这个在第六章细说。经验能用等值匹配就少用 LIKE能写IN就少写一串OR。不光因为可读性更因为优化器对IN的处理往往更高效。4.3 ORDER BY 与 LIMIT排序和分页里的细节陷阱排序用ORDER BY基本的写法大家都懂SELECT id, username, age FROM user ORDER BY age DESC, id ASC;DESC表示降序ASC表示升序默认是升序。多个排序字段从左到右逐级生效先按 age 降序age 相同再按 id 升序。排序里最容易被忽略的是NULL 值的排序位置。MySQL 默认认为NULL比任何值都小所以升序时NULL排在最前降序时NULL排在最后。如果你希望NULL固定沉底可以这样写SELECT id, username, age FROM user ORDER BY age IS NULL, age ASC;分页是后端接口最常见的需求。语法是SELECT id, username FROM user ORDER BY id LIMIT 20 OFFSET 40;LIMIT 20 OFFSET 40的意思是跳过 40 行取 20 行。等价写法是LIMIT 40, 20注意这个写法的参数顺序是“偏移量, 行数”非常容易搞混。页码计算公式LIMIT 每页条数 OFFSET (页码 - 1) * 每页条数;比如每页 10 条第 3 页就是LIMIT 10 OFFSET 20。经验深度分页性能很差LIMIT 100000, 10需要先扫描前面十万行再丢掉。更好的方案是记录上一页最后一条的 id用WHERE id 上一页最大值 ORDER BY id LIMIT 10来翻页实测在百万级数据下能快一个数量级。4.4 DISTINCT、聚合函数与 GROUP BY统计查询的地基去重是高频查询里的常客。DISTINCT的作用是从结果集中去除重复行SELECT DISTINCT status FROM order;而五个聚合函数是统计场景的核心工具COUNT(*)统计行数SUM(列)求和AVG(列)求平均值MAX(列)求最大值MIN(列)求最小值配合GROUP BY做分组统计才是真正的高频场景。比如统计每个用户的订单数和总金额SELECT user_id, COUNT(*) AS order_cnt, SUM(amount) AS total_amount FROM order GROUP BY user_id;GROUP BY 之后要筛选分组结果不能用 WHERE必须用 HAVINGSELECT user_id, COUNT(*) AS order_cnt FROM order GROUP BY user_id HAVING order_cnt 3;为什么不能写成WHERE COUNT(*) 3因为 WHERE 在 GROUP BY 之前执行此时聚合结果还没算出来。HAVING 是在分组之后执行的所以才能过滤聚合结果。这个逻辑顺一遍之后就不会再混淆 WHERE 和 HAVING 了。另一个容易混的是COUNT(*)和COUNT(某列)COUNT(*)数的是行数包含 NULL 值的行COUNT(某列)数的是该列非 NULL 的行数。如果某列常有 NULL 而你又想统计全部行用COUNT(某列)会少算这也是埋藏很深的数据 bug。统计日订单量和日销售额是分析场景最常见的写法SELECT DATE(created_at) AS day, COUNT(*) AS order_cnt, SUM(amount) AS total_amount FROM order GROUP BY DATE(created_at) ORDER BY day DESC;4.5 CASE WHEN在 SQL 里做条件分支很多查询需要把数据库里的编码值翻译成业务语义CASE WHEN是解决这个问题的标准手段。比如状态字段0/1/2想直接展示为“待支付/已支付/已取消”SELECT id, amount, CASE status WHEN 0 THEN 待支付 WHEN 1 THEN 已支付 WHEN 2 THEN 已取消 ELSE 未知 END AS status_text FROM order;CASE WHEN 还可以配合聚合函数做高级统计比如统计已支付订单的总金额SELECT SUM(CASE WHEN status 1 THEN amount ELSE 0 END) AS paid_amount FROM order;这招在做报表时很常用能避免多次扫描同一张表。5. 从单表到多表JOIN 与子查询的实战用法5.1 为什么要拆表又为什么要 JOIN 回来新手经常会困惑好好的数据为什么非要拆成 user 一张表、order 一张表直接全部塞进一张大表不香吗这里涉及一个最基础的范式思想数据冗余要控制。如果用户信息放在订单表里同一用户的手机号会在每个订单里重复存一份一旦用户改了手机号要么全表更新、要么出现数据不一致。拆表后订单表只存user_id用户信息单独维护一致性由外键和业务逻辑共同保证。但拆表之后查询时就需要把数据重新拼起来。JOIN 就是拼表的工具。5.2 三种 JOIN 的语义差异必须说到骨子里准备一个直观的例子user 表里有两个用户order 表里有三个订单其中有一个订单的user_id999在 user 表中不存在。-- 内连接只返回两边都能匹配上的行 SELECT u.id, u.username, o.id AS order_id, o.amount FROM user u INNER JOIN order o ON u.id o.user_id;INNER JOIN 的结果里只有 user 表和 order 表能对应上的记录才会出现。那笔user_id999的订单会被丢弃。-- 左连接左表全部保留右表匹配不到就用 NULL 填充 SELECT u.id, u.username, o.id AS order_id, o.amount FROM user u LEFT JOIN order o ON u.id o.user_id;LEFT JOIN 的结果里左表 user 的行全部保留哪怕这个用户没有订单订单列也会用 NULL 补齐。-- 右连接右表全部保留左表匹配不到用 NULL 填充 SELECT u.id, u.username, o.id AS order_id, o.amount FROM user u RIGHT JOIN order o ON u.id o.user_id;RIGHT JOIN 会把 order 表全部保留包括user_id999那笔而对应的用户字段是 NULL。实际工作中 RIGHT JOIN 很少用能用 LEFT JOIN 解决的就别折腾可读性更好。内连接和左连接的取舍是 JOIN 里最核心的判断。判断标准很简单保留哪边的全部数据。如果只想看有订单的用户就内连接想看所有用户以及他们的订单就左连接。5.3 JOIN 写多表时优先级、别名和去重多表连接最怕的是结果集莫名膨胀。比如 user 和 order 是一对多关系JOIN 之后 user 的字段会在每个订单行都出现一次这是符合预期的。但如果再 JOIN 一张一对多的表结果集就会成倍增长产生笛卡尔积式膨胀。排查这类问题时先用COUNT(*)对比 JOIN 前后行数能快速定位问题。小技巧JOIN 里的表一定要起别名。别名短查询写起来清爽也能避免同名字段歧义。SELECT u.username, o.amount FROM user AS u LEFT JOIN order AS o ON u.id o.user_id;AS可以省略但写上更清晰。5.4 子查询的三种常见形式子查询是嵌在另一个查询里的查询分几种常见用法。标量子查询返回单个值用在条件比较里。-- 查询订单金额大于平均金额的订单 SELECT id, amount FROM order WHERE amount (SELECT AVG(amount) FROM order);IN 子查询返回一列值用于集合判断。-- 查询下过单的用户 SELECT id, username FROM user WHERE id IN (SELECT DISTINCT user_id FROM order);EXISTS 子查询判断存在性适合大表场景。SELECT id, username FROM user u WHERE EXISTS ( SELECT 1 FROM order o WHERE o.user_id u.id );EXISTS和IN在数据量大的时候执行计划可能差异很大EXISTS更适合子表数据量大、外层表数据量小的情况。入门阶段建议先熟练掌握IN和标量子查询EXISTS遇到具体性能问题时再深入研究。经验子查询能解决 90% 的“先查一次再查一次”的需求但过多嵌套子查询会让执行计划难以优化。能用 JOIN 表达的关联查询优先用 JOIN可读性和性能通常都比嵌套子查询更好。子查询更适合“一次性条件过滤”的场景。5.5 一个综合实战统计每个用户最近订单状态把前面几节的东西串起来写一个稍微复杂的查询统计每个用户的最新一笔订单金额和状态。SELECT u.username, t.amount, t.status FROM user u LEFT JOIN ( SELECT user_id, amount, status, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM order ) t ON u.id t.user_id AND t.rn 1;这里用到了 8.0 的窗口函数ROW_NUMBER()对每个用户按订单时间倒序编号取第一名就是最新一笔订单。如果在 5.7 里做同样的需求写法会绕很多这也是我反复强调“新项目直接用 8.0”的原因。窗口函数在基础阶段的末尾可以只做了解但知道 8.0 能这么写会帮你打开新世界的大门。6. 查询慢的第一步排查索引是什么、怎么用、为何会失效6.1 索引的本质拿空间换时间的目录很多新手把索引想得很玄乎其实就是书的目录。没有目录时找某个知识点要一页一页翻有了目录直接翻到对应页码。MySQL 的索引原理与此类似它维护了一套额外的数据结构常见是 BTree让 WHERE 条件里的字段可以快速定位数据行而不必全表扫描。索引的代价也很直观一是占用额外磁盘空间二是写数据时需要同步维护索引写性能会有损耗。所以索引不是越多越好而是“命中高并发查询场景”才建。6.2 索引类型和创建方式开发中最常用的四类索引索引类型特点典型场景主键索引每张表只能有一个自动创建按 id 等值查询唯一索引列值不能重复用户名、手机号普通索引加速查询允许重复按 user_id 查订单联合索引多个字段组合成一个索引按 user_id created_at 查询创建索引的语法CREATE INDEX idx_user_id ON order(user_id);联合索引CREATE INDEX idx_user_created ON order(user_id, created_at);查看表的索引SHOW INDEX FROM order;6.3 EXPLAIN判断查询有没有走索引的试金石不要猜索引有没有生效用EXPLAIN看一下执行计划就知道。在查询语句前面加EXPLAINEXPLAIN SELECT * FROM order WHERE user_id 1;重点看两个字段typeconst、ref、range通常说明走索引了ALL说明全表扫描这是最需要警惕的key实际使用的索引名如果显示 NULL说明没走索引。6.4 索引失效的常见场景写 SQL 时绕开对索引列做了函数运算。比如WHERE DATE(created_at) 2025-01-01改成WHERE created_at 2025-01-01 AND created_at 2025-01-02就能走索引LIKE以通配符开头。LIKE %关键词无法利用索引LIKE 关键词%可以联合索引没遵循最左前缀。联合索引(user_id, created_at)能命中user_id单独作为条件的查询但只查created_at时索引失效隐式类型转换。WHERE phone 13800000001中 phone 是字符串却用数字去比MySQL 会做类型转换索引可能失效OR 连接条件。如果 OR 两侧有一个条件没有索引整个查询可能放弃走索引。新手最容易踩的是前两个。写完查询如果反应慢按这个清单逐条排除基本能解决大部分索引利用率问题。经验索引不是越多越好。每张表的索引数量建议控制在 3 到 5 个以内索引建得太多Insert/Update 的成本会明显上升。宁缺毋滥按真实查询场景建。7. 新人最常踩的坑五个高频报错与完整排查链路7.1 Access denied for user rootlocalhost这是新手遇到频率最高的报错。原因通常有三个密码确实不对、账号只允许本机登录而你用了远程主机、或者是权限没刷新。排查链路先确认命令行登录用的主机和端口是否正确mysql -uroot -p如果密码忘了8.0 的正规做法是用--init-file重置或者临时跳过权限表启动。跳过权限表属于最后手段学会后要立刻收回权限生产环境线上禁用。7.2 端口 3306 被占用MySQL 启动失败Windows 上最常见的场景是以前装过 MySQL 又没卸载干净或者某个程序占用了 3306。排查方式netstat -ano | findstr :3306看输出的 PID 对应哪个进程在任务管理器里结束掉它或者修改my.ini中的端口为 3307。对于初学者我建议直接改端口是最省事的方案但要注意后续连接时都要加上-P3307。7.3 中文插入乱码乱码基本可以锁定为字符集问题。先检查连接、数据库、表三层的字符集SHOW VARIABLES LIKE character_set%;如果character_set_server不是 utf8mb4建库时显式指定即可。连接层则可以在命令行登录后执行SET NAMES utf8mb4;关键点建表时如果没写DEFAULT CHARSETutf8mb4数据库默认字符集不是 utf8mb4表就会用默认值后续再改表字符集就比较麻烦。7.4 ERROR 1064SQL 语法错误这个报错会出现一条很长很吓人的信息但其实只是告诉你“你写的 SQL 有语法问题”。排查思路检查表名、字段名是否真的存在注意order是 MySQL 的保留字直接用会报语法错要加反引号检查字符串值有没有用单引号字段名有没有错误地用双引号检查每条语句末尾有没有写分号检查LIMIT、OFFSET的顺序和参数。最常见的还是保留字问题。字段名、表名尽量避开order、group、select这些内置词非要用就加反引号。7.5 MySQL 服务无法启动或启动后立刻停止Windows 下启动失败先看错误日志一般在 MySQL 数据目录下的.err文件里。快捷查询mysqld --console这个命令能把 MySQL 的启动日志输出到控制台之前my.ini里配置的basedir、datadir路径写错是头号原因。检查路径是否存在、结尾有没有多余的斜杠改完后重启服务net stop mysql net start mysql经验遇到服务反复启动失败不要反复点重启。先开日志看到底报的是什么。日志才是数据库告诉你真实问题的地方靠猜永远浪费时间。8. 写在最后把这条链路走通你就完成了从“看教程”到“能干活”的跨越我自己带新人时最欣慰的时刻就是对方从“只会零散地敲几个语句”变成“能完整地把一张业务需求翻译成 SQL”。这个转变没有捷径唯一可靠的路径就是把上面的内容跟着敲一遍再自己造点数据做练习。具体来说建议你这样做建一个shop库造 50 个用户、200 条订单的模拟数据先写一遍单表查询等值、范围、模糊、排序、分页、聚合、分组再写一遍多表查询内连接、左连接、子查询统计每个用户的订单数、总金额、最新订单最后用EXPLAIN查看你自己的查询看哪些走了索引、哪些在全表扫描。这大概会花掉半天时间但效果是记一辈子。我的亲身体会是这些“简单到不值得单独写文章”的基础操作恰恰是在真实工作中被用得最多、也最容易出问题的部分。把地基夯实后面不管是学事务、锁、性能优化还是高可用方案都会顺畅很多。下一期的入门篇我会接着把这些数据放进更复杂的业务场景里聊一聊事务和锁毕竟那才是多用户并发写入时真正让人头大的开始。
阅读完成 · 觉得有帮助?
咨询建站