MySQL 全套学习笔记(JDBC+连接池)
MySQL 全套学习笔记(JDBC+连接池)一、DDL 数据库、表结构操作1. 数据库操作2. 表基础操作3. 建表模板4. ALTER TABLE 修改表结构5. 约束汇总6. 常用数据类型二、DML 增删改操作1. INSERT 插入数据2. UPDATE 更新数据3. DELETE 和 TRUNCATE三、SELECT 查询(重点)1. 基础查询2. 多表连接 JOIN自连接(同一张表自己连自己)四、外键 FOREIGN KEY1. 建表时定义外键2. 已有表追加外键3. 删除外键级联选项(写在references后面)五、高频错误速查表六、标准操作流程七、MySQL 排序+分页1. ORDER BY 升序、降序2. LIMIT 分页分页公式SQL书写顺序(不能乱)高频坑点八、MySQL 事务1. 什么是事务2. 事务四大特性 ACID(必考)3. 事务操作基本命令4. 事务的自动提交5. 并发事务会产生的3个问题6. 4种隔离级别(由低到高)7. InnoDB 与 MyISAM事务演示SQL(转账案例)九、常用函数 + 聚合函数 + 分组过滤 + MD5加密一、单行函数(一行返回一个结果)1. 字符串函数2. 数值函数3. 日期函数4. 流程函数(判断)二、聚合函数(多行返回1个结果,只对非NULL值统计)三、分组查询 GROUP BY + 分组过滤 HAVINGWHERE 和 HAVING 核心区别(必考)四、MD5加密配套练习SQL十、索引一、索引是什么二、索引分类三、索引操作语句(测试用)四、索引设计原则五、索引失效常见情况(高频考点)六、索引测试思路十一、MySQL 用户管理1. 创建用户2. 修改用户密码3. 删除用户4. 用户授权 GRANT(重点)5. 查看用户权限6. 撤销权限 REVOKE7. 刷新权限(考点!)8. 高频考点汇总十二、数据库设计完整流程(通用,MySQL为主)一、需求分析(第一步)二、概念模型:E-R图三、逻辑模型:设计表结构(核心)三大范式(尽量遵守)表字段设计要点四、表关系示例(文字格斗游戏举例)五、物理设计(建库建表、索引)六、校验与优化七、落地步骤总结十三、数据库备份mysqldump 命令行导出导入sql十四、JDBC & 数据库连接池(Druid)1. 是什么2. 核心好处3. 核心参数(必背)4. 常见连接池实现5. 核心API(JDBC规范:javax.sql.DataSource)6. Druid快速入门示例7. 连接池工作流程8. 对比:原生JDBC vs 连接池9. 面试考点10. 常见坑
一、DDL 数据库、表结构操作
DDL:定义库、表、字段,关键字
create / alter / drop
1. 数据库操作
CREATE DATABASE IF NOT EXISTS school; -- 创建数据库
DROP DATABASE IF EXISTS school; -- 删除数据库
USE school; -- 切换数据库(必做!)
SHOW DATABASES; -- 查看所有数据库
2. 表基础操作
SHOW TABLES; -- 查看当前库全部表
DESC student; -- 查看表字段结构
SHOW CREATE TABLE student; -- 查看完整建表语句
DROP TABLE IF EXISTS student; -- 删除表
3. 建表模板
CREATE TABLE IF NOT EXISTS `student` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
`birthday` DATE DEFAULT NULL COMMENT '生日',
`address` VARCHAR(20) DEFAULT NULL COMMENT '地址',
`email` VARCHAR(20) DEFAULT NULL COMMENT '邮件',
PRIMARY KEY (`id`)
);
建表注意事项
表名、字段名使用英文反引号 `````;字符串、注释使用英文单引号 ```'`
最后一个字段后面不要写逗号
AUTO_INCREMENT只能给整数主键使用全部符号必须英文半角,中文符号会报1064语法错误
4. ALTER TABLE 修改表结构
ALTER TABLE student ADD `age` TINYINT COMMENT '年龄'; -- 新增字段
ALTER TABLE student ADD `age` TINYINT AFTER `name`; -- 指定位置新增
ALTER TABLE student DROP COLUMN `age`; -- 删除字段
ALTER TABLE student MODIFY `age` TINYINT NOT NULL DEFAULT 0; -- 修改字段类型约束,不改名字
ALTER TABLE student CHANGE `age` `stu_age` TINYINT; -- 修改字段名+类型
ALTER TABLE student RENAME TO stu; -- 修改表名
ALTER TABLE student ADD PRIMARY KEY(`id`); -- 添加主键
ALTER TABLE student DROP PRIMARY KEY; -- 删除主键
注意:DROP删除字段,该列全部数据丢失;已有数据时修改字段类型不能冲突。
5. 约束汇总
| 约束 | 作用 |
|---|---|
| NOT NULL | 非空,字段不能为空 |
| DEFAULT '值' | 默认值,不赋值自动填充 |
| PRIMARY KEY | 主键:唯一、非空,一张表只能有一个 |
| AUTO_INCREMENT | 自增,整数类型,配合主键使用 |
| FOREIGN KEY | 外键,维护两张表引用关系 |
6. 常用数据类型
- 数值
-
INT:普通整数,id编号 -
TINYINT:小整数,年龄、状态 -
DECIMAL(m,d):小数,存储金额,不要使用float
- 字符串
-
VARCHAR(n):可变长度字符串,最常用 -
CHAR(n):定长字符串,适合短固定内容
- 日期
-
DATE:yyyy‑MM‑dd 日期 -
DATETIME:yyyy‑MM‑dd HH:mm:ss 日期时间不建议用字符串存储日期
二、DML 增删改操作
DML:
insert / update / delete,操作表里面的行数据
1. INSERT 插入数据
-- 格式1 指定字段(推荐)
INSERT INTO student(name,sex) VALUES ('张三','男');
-- 格式2 全部字段,顺序和建表完全一致,不推荐
INSERT INTO student VALUES(1,'张三','男',NULL,NULL,NULL);
-- 格式3 批量插入
INSERT INTO student(name,sex)
VALUES
('张三','男'),
('李四','女');
-- 自增字段:不写该字段,数据库自动生成id
INSERT INTO student(name,sex) VALUES ('王五','男');
-- 查询结果插入表
INSERT INTO 表A(字段1,字段2) SELECT 字段1,字段2 FROM 表B;
插入要点
字符串、日期加英文单引号;数字不加引号;NULL不加引号
NOT NULL字段必须给值,否则报错1364
有外键约束:子表外键值必须在主表主键存在,报1452代表值不存在
2. UPDATE 更新数据
--带where,正常使用
UPDATE student SET name='张三',sex='男' WHERE id=1;
--数值基于原值修改
UPDATE student SET score=score+5 WHERE id=1;
--多条件
UPDATE student SET gradeid=3 WHERE id>10 AND sex='男';
⚠高危!不加WHERE会修改全表所有行。更新前建议先用select校验where条件查询结果。
3. DELETE 和 TRUNCATE
DELETE FROM student WHERE id=5; -- 删除符合条件行,表结构保留
DELETE FROM student; -- 删除全部数据,自增不重置,可以回滚
TRUNCATE TABLE student; -- 清空整张表全部数据,表结构保留,id重置,不能回滚
DROP TABLE student; -- 删除整张表,结构+数据全部消失
| 对比项 | DELETE | TRUNCATE |
|---|---|---|
| 类型 | DML | DDL |
| WHERE | ✅支持 | ❌不支持 |
| 自增主键 | 不重置 | 重置从1开始 |
| 事务回滚 | 可以回滚 | 不可回滚 |
| 速度 | 大数据量慢 | 速度很快 |
三、SELECT 查询(重点)
1. 基础查询
SELECT * FROM student; -- 查询全部列
SELECT id,name FROM student; -- 查询指定列
SELECT name AS stuName FROM student; -- 字段别名
SELECT DISTINCT gradeid FROM student; -- 去重
SELECT * FROM student ORDER BY id DESC; -- 排序 DESC降序 ASC升序
SELECT * FROM student WHERE sex='男' AND id>2; -- where条件
2. 多表连接 JOIN
on写两张表关联条件;where是拼接完成后过滤数据
-- INNER JOIN 内连接:两边都匹配上,数据才展示
SELECT *
FROM student stu
INNER JOIN result r
ON stu.studentno = r.studentno;
-- LEFT JOIN 左连接:左表全部保留,右表匹配不到填NULL
SELECT *
FROM student stu
LEFT JOIN result r
ON stu.studentno = r.studentno;
-- RIGHT JOIN右连接:右表全部保留,开发很少使用
大坑:left join,不要把右表筛选条件写在where,会丢失左表数据,筛选右表条件写on后面。
自连接(同一张表自己连自己)
场景:一张表存在父子、上下级、层级关系;必须给表起两个不同别名
SELECT
e1.empname AS '员工姓名',
e2.empname AS '直接上级'
FROM emp AS e1
LEFT JOIN emp AS e2
ON e1.leaderid = e2.empid;
e1看做员工,e2看做上级,物理上是同一张表 LEFT JOIN保留没有上级的顶层数据;INNER JOIN只查询有上级的员工
四、外键 FOREIGN KEY
主表:被引用;子表:引用别人;子表外键字段引用主表主键 引擎必须InnoDB;先建主表,再建子表;字段类型完全一致
1. 建表时定义外键
CONSTRAINT `fk_student_gradeid`
FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
ON DELETE CASCADE
ON UPDATE CASCADE
2. 已有表追加外键
ALTER TABLE student
ADD CONSTRAINT fk_stu_gradeid FOREIGN KEY(gradeid) REFERENCES grade(gradeid);
3. 删除外键
SHOW CREATE TABLE student; --查看外键名字
ALTER TABLE student DROP FOREIGN KEY fk_stu_gradeid;
级联选项(写在references后面)
| 参数 | 作用 |
|---|---|
| CASCADE | 主表更新删除,子表跟着更新删除 |
| SET NULL | 主表删除,子表外键置NULL,字段必须允许null |
| NO ACTION/RESTRICT(默认) | 子表有引用,主表不能修改删除,直接报错 |
默认规则:插入先主表,再子表;删除先子表,再主表。 开发提示:很多项目不使用物理外键,业务代码做逻辑约束。
五、高频错误速查表
| 错误码 | 含义 |
|---|---|
| 1064 | 语法错误,中文符号、逗号、引号括号不匹配 |
| 1146 | 表不存在,忘记use切换数据库,表名拼写错误 |
| 1062 | 主键重复,插入已经存在的主键值 |
| 1364 | NOT NULL字段没有默认值,插入没有给该字段赋值 |
| 1452 | 外键约束失败,子表外键的值主表不存在 |
| 1052 | 字段模糊歧义,多表查询重名字段没有加表别名 |
六、标准操作流程
-
USE 库名;切换数据库
-
建表前
DROP TABLE IF EXISTS 表名;清理旧表 -
执行建表,确认全部符号英文半角
-
DESC 表名;校验表结构
-
修改表用ALTER;update delete执行前先用select校验where条件
-
有外键:先操作主表,后操作子表。
七、MySQL 排序+分页
1. ORDER BY 升序、降序
-- 升序 ASC:从小到大,默认,可以省略ASC
SELECT * FROM student ORDER BY id ASC;
-- 降序 DESC:从大到小,**不能省略**
SELECT * FROM student ORDER BY id DESC;
-- 多字段排序:先按第一个字段排,第一个值相同,再按第二个排
-- 先按age降序,年龄相同再按id升序
SELECT * FROM student ORDER BY age DESC, id ASC;
重点:ORDER BY写在WHERE条件后面
SELECT * FROM student WHERE sex='男' ORDER BY id DESC;
2. LIMIT 分页
语法:LIMIT 起始下标,每页条数 ⚠️ 下标从0开始!不是从1
-- 取前5条数据(第1页,0开始)
SELECT * FROM student LIMIT 0,5;
-- 第2页,每页5条:起始 = (页码-1)*每页条数
SELECT * FROM student LIMIT 5,5;
--简写:只写一个数字,代表从0开始取N条
SELECT * FROM student LIMIT 5;
分页公式
起始索引 = (当前页码 − 1) * 每页显示条数 例:页码3,每页10条 → (3‑1)*10 =20 → LIMIT 20,10
SQL书写顺序(不能乱)
SELECT → FROM → WHERE → ORDER BY → LIMIT
SELECT *
FROM student
WHERE age>18
ORDER BY id DESC
LIMIT 0,3;
高频坑点
-
LIMIT一定放整条SQL最后面,写在ORDER BY前面直接报错
-
LIMIT第一个参数是下标,从0开始,不是页码
-
DESC只作用于它紧贴的那一个字段,多字段排序每个字段要单独写DESC
-
大数据分页不建议直接用
LIMIT 100000,10,性能差
记忆口诀:排序order by,desc降,asc升; where过滤完,再去排顺序; limit放末尾,下标从零起; 页码算偏移,(页‑1)*条数。
八、MySQL 事务
1. 什么是事务
事务(Transaction):一组不可分割的SQL操作,要么全部执行成功,要么全部失败回滚。 典型场景:转账,A扣钱、B加钱,不能只执行一半。
2. 事务四大特性 ACID(必考)
-
A 原子性 Atomicity:最小单元,要么全成,要么全失败。
-
C 一致性 Consistency:事务执行前后,数据完整性不变(总额不变)。
-
I 隔离性 Isolation:多个事务并发,事务之间互相隔离,互不干扰。
-
D 持久性 Durability:事务提交后,修改永久保存,断电也不会丢失。
3. 事务操作基本命令
START TRANSACTION; -- 开启事务
-- 写DML语句(INSERT / UPDATE / DELETE)
COMMIT; -- 提交事务 → 数据永久生效
ROLLBACK; -- 回滚事务 → 撤销所有修改,恢复到开启前
注意:DDL语句(create/alter/drop)会自动提交事务,无法回滚。
4. 事务的自动提交
MySQL默认自动提交(autocommit=1),每执行一条DML就自动commit。
SELECT @@autocommit; -- 查看自动提交状态
SET autocommit = 0; -- 关闭自动提交(当前会话)
5. 并发事务会产生的3个问题
-
脏读:事务A读到了事务B未提交的数据(B最后回滚,A读到无效脏数据)
-
不可重复读:同一个事务内,两次读取同一数据,中间被别的事务修改并提交,两次结果不一样(侧重数据修改)
-
幻读:同一个事务内,两次查询,别的事务新增/删除数据,导致前后行数不一样(侧重新增删除)
6. 4种隔离级别(由低到高)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED 读未提交 | ✅存在 | ✅存在 | ✅存在 |
| READ COMMITTED 读已提交(Oracle默认) | ❌避免 | ✅存在 | ✅存在 |
| REPEATABLE READ 可重复读(MySQL InnoDB默认) | ❌避免 | ❌避免 | ✅基本解决 |
| SERIALIZABLE 串行化 | ❌避免 | ❌避免 | ❌避免 |
SELECT @@transaction_isolation; --查看隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL 级别名; --设置会话隔离级别
7. InnoDB 与 MyISAM
-
InnoDB:支持事务、行锁
-
MyISAM:不支持事务,表锁
一句话速记:事务ACID,开启提交回滚;并发会脏读、不可重复读、幻读;4个隔离级别,MySQL默认可重复读。
事务演示SQL(转账案例)
-- 创建数据库
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci;
USE shop;
-- 创建表
CREATE TABLE `shopping`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE = INNODB DEFAULT CHARSET=utf8;
INSERT INTO `shopping`(`id`,`name`,`money`)
VALUES
(1,'zhangsan',1000),
(2,'lisi',500),
(3,'laowu',700);
SET autocommit = 0;-- 关闭自动事务
START TRANSACTION; -- 开启一个事务(一组事务)
UPDATE shopping SET money=money-500 WHERE NAME = 'zhangsan'; -- 张三-500
UPDATE shopping SET money=money+500 WHERE NAME = 'lisi'; -- 李四+500
COMMIT; -- 提交事务就被持久化了
ROLLBACK; -- 回滚
SET autocommit = 1; -- 恢复初始化 开启自动事务
九、常用函数 + 聚合函数 + 分组过滤 + MD5加密
一、单行函数(一行返回一个结果)
1. 字符串函数
-
CONCAT(str1,str2,...)拼接字符串CONCAT('zhang','san') → zhangsan -
LENGTH(str)获取字节长度(utf8中文1字3字节) -
SUBSTRING(str,start,len)截取字符串SUBSTRING('abcde',2,3) → bcd -
TRIM(str)去掉前后空格 -
UPPER(str)转大写;LOWER(str)转小写
2. 数值函数
-
ROUND(x,n)四舍五入,n保留小数位数ROUND(3.1415,2) →3.14 -
FLOOR(x)向下取整(往小取)FLOOR(3.9) →3 -
CEIL(x)向上取整CEIL(3.1) →4 -
RAND()生成0~1随机小数
3. 日期函数
-
NOW()获取当前日期+时间 -
CURDATE()只获取当前日期 -
DATE_ADD(日期,INTERVAL 数字 单位)日期加减DATE_ADD(NOW(),INTERVAL 7 DAY)当前时间加7天 -
DATEDIFF(end,start)计算两个日期相差天数
4. 流程函数(判断)
-
IF(条件,值1,值2)条件成立返回值1,否则值2 -
IFNULL(字段,默认值)如果字段为null,返回默认值,否则返回字段本身重点:NULL参与运算结果一定是NULL,要用IFNULL处理
二、聚合函数(多行返回1个结果,只对非NULL值统计)
-
COUNT()统计行数-
COUNT(*):统计所有行(包含null行,推荐) -
COUNT(列名):统计该列不为null的行数
-
-
SUM(列)求和 -
AVG(列)平均值 -
MAX(列)最大值 -
MIN(列)最小值⚠注意:聚合函数不能直接写在WHERE后面!
三、分组查询 GROUP BY + 分组过滤 HAVING
语法模板
SELECT 分组列, 聚合函数()
FROM 表名
[WHERE 普通行过滤条件]
GROUP BY 分组字段
[HAVING 聚合结果过滤]
ORDER BY 排序;
WHERE 和 HAVING 核心区别(必考)
-
WHERE:分组之前 过滤原始数据;不能使用聚合函数
-
HAVING:分组之后 ,对聚合后的结果过滤;可以写聚合函数 执行顺序:
FROM → WHERE → GROUP BY → 聚合计算 → HAVING → SELECT → ORDER BY
示例:查询总金额大于1000的名字
SELECT name,SUM(money) FROM shopping GROUP BY name HAVING SUM(money)>1000;
规则:select后面,非聚合的字段,必须出现在group by中
四、MD5加密
MD5是不可逆哈希摘要,不能解密还原原文,常用于密码存储。 特点:相同明文加密结果永远一样;不可逆,只能对比密文。
-- 加密
SELECT MD5('123456');
-- 插入加密密码
INSERT INTO user(username,pwd) VALUES ('zhangsan',MD5('123456'));
-- 登录校验:输入密码加密后和数据库密文比对
SELECT * FROM user WHERE username='zhangsan' AND pwd = MD5('123456');
⚠重要考点 MD5不是加密算法,是哈希摘要,不可逆,无法解密 密文固定32位字符串 缺陷:简单密码可以被彩虹表破解;企业一般加盐
MD5(CONCAT('salt','123456'))
一句话速记:单行函数一行一结果;聚合多行算一个;WHERE分组前筛原始行,HAVING分组后筛聚合结果;MD5哈希不可逆,用来存密码。
配套练习SQL
CREATE DATABASE IF NOT EXISTS testfunc;
USE testfunc;
CREATE TABLE `shopping` (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DECIMAL(10,2),
create_time DATETIME
);
INSERT INTO shopping(NAME,money,create_time) VALUES
('zhangsan',1000,'2025-01-10 08:20:10'),
('lisi',500,'2025-01-11 09:10:20'),
('zhangsan',2000,'2025-01-12 10:30:00'),
('lisi',1500,'2025-01-13 14:20:10'),
('laowu',700,'2025-01-14 16:10:20');
-- 用户表,用于MD5练习
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
PASSWORD CHAR(32)
);
-- 1.查询 name,把名字转为大写显示,列名别名大写名字
SELECT UPPER (`name`)AS '大写名字' FROM shopping;
-- 2. 查询 name,截取 name 前 2 个字符,别名 short_name
SELECT SUBSTRING(`name`,1,2) AS short_name FROM shopping;
-- 3. 计算 money 向上取整、向下取整、四舍五入保留 1 位小数
SELECT FLOOR(`money`) AS '向下取整' FROM shopping;
SELECT CEIL(`money`) AS '向上取整' FROM shopping;
SELECT ROUND (`money`) AS '四舍五入' FROM shopping;
-- 4. 查询当前时间,以及当前时间往后加 10 天的时间
SELECT NOW();
SELECT DATE_ADD(NOW(),INTERVAL 10 DAY);
-- 5. 判断 money,如果大于 1000 输出 '大额',否则 '小额'
SELECT IF(money>1000,'大额','小额')FROM shopping;
-- 6. IFNULL 练习:查询 money,如果 money 为 NULL 显示 0
SELECT IFNULL(money,0)FROM shopping;
-- 7. 统计一共有多少条记录
SELECT COUNT(*) FROM shopping;
-- 8. 查询 money 总和、平均值、最大值、最小值
SELECT SUM(money)FROM shopping;
SELECT MAX(money)FROM shopping;
SELECT MIN(money)FROM shopping;
SELECT AVG(money)FROM shopping;
-- 9. 按 name 分组,统计每个人总金额、平均金额
SELECT `name`,SUM(money) AS 总金额, AVG(money)AS 平均金额 FROM shopping
GROUP BY `name`;
-- 10. 按 name 分组,查询个人总金额大于 2000 的用户名和总金额
SELECT `name`,SUM(money) AS 总金额 FROM shopping
GROUP BY `name`
HAVING 总金额>2000;
-- 11. 查询 money>800 的数据,按 name 分组,筛选分组后总和 > 1500 的记录
SELECT `name` ,SUM(money) AS 金额 FROM shopping
WHERE money>800
GROUP BY `name`
HAVING SUM(money)>1500;
-- 12. 插入用户,用户名:zhangsan,密码:123456,密码用 MD5 加密存入
INSERT INTO USER(`username`,`password`)VALUE('zhangsan',MD5('123456'));
-- 13. 校验登录:查询 user 表,匹配用户名 zhangsan,密码传入明文 123456 做 MD5 比对
SELECT PASSWORD FROM USER
WHERE username='zhangsan' AND PASSWORD=MD5('123456');
十、索引
一、索引是什么
索引相当于书的目录,帮数据库快速定位数据,加快查询速度;代价是占用磁盘空间,增删改(insert/update/delete)会变慢。 底层默认 InnoDB 使用 B+树。
二、索引分类
-
主键索引(PRIMARY KEY) 主键自带索引,唯一+非空,一张表只能1个。 InnoDB主键索引又叫聚簇索引,数据存在主键索引叶子节点。
-
唯一索引(UNIQUE) 列值不能重复,允许NULL。
CREATE UNIQUE INDEX idx_name ON 表名(字段);
- 普通索引(INDEX) 最基础索引,仅加速查询,值可重复、可为null。
CREATE INDEX idx_money ON 表名(money);
-
联合索引(复合索引) 多个字段一起建索引,最左前缀原则(重点考点) index(a,b,c),查询条件必须从最左边a开始匹配,否则不走索引。
-
全文索引 FULLTEXT 用于长文本模糊搜索(文章、描述),不替代like %关键词%
聚簇索引 vs 非聚簇索引 聚簇:主键索引,索引和数据放一起,InnoDB才有 非聚簇:普通/唯一索引,叶子节点存主键值,查到主键再回表查数据
三、索引操作语句(测试用)
-- 创建普通索引
CREATE INDEX idx_name ON student(name);
-- 查看表所有索引
SHOW INDEX FROM student;
-- 删除索引
DROP INDEX idx_name ON student;
-- 查看执行计划,测试是否走索引(核心!)
EXPLAIN SELECT * FROM student WHERE name='张三';
EXPLAIN看 type 字段判断索引使用: ALL → 全表扫描(没用到索引,最差) ref/range → 使用索引
四、索引设计原则
✅ 适合建索引
-
经常出现在WHERE条件、JOIN连接、ORDER BY、GROUP BY 的字段
-
字段区分度高(性别这种只有0/1不适合)
-
数据量大的表,小表没必要建索引
❌ 不要建索引
-
频繁更新修改的字段(update多,维护索引开销大)
-
低基数字段:性别、状态(只有少数几种值)
-
字段经常为NULL,或者大量重复值
-
模糊查询
like '%关键词',前缀百分号不走索引;like '关键词%'可以走
五、索引失效常见情况(高频考点)
-
违背最左前缀原则(联合索引跳过左边字段)
-
like 以 % 开头
-
索引列做运算、函数
-
字符串类型不加引号,发生隐式转换
-
or 连接,一侧无索引
-
MySQL优化器判断全表扫描比索引更快,自动放弃索引
六、索引测试思路
-
先用EXPLAIN查看执行计划
-
对比:不加索引的查询耗时 VS 加索引后的耗时
-
看type、key字段:key不为NULL代表用到索引
-
测试增删改:插入大量数据,观察加索引后速度变慢
十一、MySQL 用户管理
1. 创建用户
语法:CREATE USER '用户名'@'访问地址' IDENTIFIED BY '密码';
CREATE USER 'user1'@'localhost' IDENTIFIED BY '123456';
'用户名'@'localhost':只能本机登录 'user1'@'%':任意IP都能远程登录 不加IDENTIFIED BY,创建无密码用户
2. 修改用户密码
--方式1
ALTER USER 'root'@'localhost' IDENTIFIED BY '111111';
--方式2
SET PASSWORD FOR 'root'@'localhost' = '111111';
MySQL8.0 废弃 PASSWORD() 加密函数,不能再写PASSWORD('111111')
3. 删除用户
DROP USER 'user1'@'localhost';
4. 用户授权 GRANT(重点)
GRANT 权限列表 ON 库名.表名 TO '用户'@'地址' [WITH GRANT OPTION];
-
ALL PRIVILEGES:所有权限 -
ON *.*所有库所有表;shop.*shop库全部表;shop.tb_user单张表 -
WITH GRANT OPTION:该用户可以把得到的权限授予其他人
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
5. 查看用户权限
SHOW GRANTS FOR 'root'@'localhost';
6. 撤销权限 REVOKE
REVOKE 权限 ON 库.表 FROM '用户'@'地址';
--例子:收回全部权限
REVOKE ALL PRIVILEGES ON *.* FROM 'user1'@'localhost';
7. 刷新权限(考点!)
授权/撤销权限之后,执行刷新才生效
FLUSH PRIVILEGES;
8. 高频考点汇总
账号格式:'用户名'@'主机',两部分是整体,主机写错用户就不生效(locallost 拼写错误经典坑)
-
%代表所有ip远程访问;localhost仅本机,192.168.1.%代表这个网段 -
WITH GRANT OPTION:仅代表传递授权,不代表拥有全部权限 区分:CREATE USER:只创建账号,没有任何权限GRANT:专门分配权限
查看所有用户:
SELECT user,host FROM mysql.user;
一句话背诵:建用户CREATE USER,改密码ALTER USER; 授权GRANT,撤权REVOKE; 改完权限FLUSH PRIVILEGES刷新; 账号是用户@主机,%代表任意远程IP。
-- 创建用户 create user 用户名 IDENTIFIED BY '密码'
CREATE USER kuangshen IDENTIFIED BY '123456';
-- 修改密码
SET PASSWORD FOR 'root'@'localhost'='111111';
-- 重命名
RENAME USER kuangshen TO kuangshen2;
-- 用户授权 ALL PRIVILEGES 全部权限 库.表
-- ALL PRIVILEGES 除了给别人授权 什么都能干
GRANT ALL PRIVILEGES ON *.* TO kuangshen2;
-- root用户权限
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
-- 查看权限 查看指定用户的权限
SHOW GRANTS FOR kuangshen2;
-- 撤销权限 REVOKE 哪些权限 在那个库,给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM kuangshen2;
-- 删除用户
DROP USER kuangshen2;
十二、数据库设计完整流程(通用,MySQL为主)
一、需求分析(第一步)
搞清楚项目有哪些实体(业务里的事物) 例:文字格斗游戏 → 用户、角色、道具、背包、战斗记录
-
找出实体的属性 用户:id、账号、密码、昵称、创建时间
-
找出实体之间关系
-
一对一:用户 ↔ 用户详情
-
一对多:用户 ↔ 多个背包记录(一个用户多个道具)
-
多对多:角色 ↔ 技能(一个角色多个技能,技能可被多个角色拥有),多对多必须建中间表
二、概念模型:E-R图
实体(矩形)、属性(椭圆)、关系(菱形) 作用:只描述业务,不关心数据库语法
三、逻辑模型:设计表结构(核心)
三大范式(尽量遵守)
-
第一范式1NF:字段不可再拆分(不要把多个值放一个单元格,比如道具1,道具2存在同一字段)
-
第二范式2NF:非主键字段完全依赖主键,消除部分依赖(复合主键才会遇到)
-
第三范式3NF:非主键字段不能依赖其他非主键,消除传递依赖
注意:性能场景可以反范式(冗余字段,减少联表查询) 规范性和性能取舍: 关联查询的表不要超过三张表;商业化项目优先考虑性能,可适当增加冗余字段。
表字段设计要点
主键:id 一般用 BIGINT 自增,每张表都要有主键 外键:用来维护表关系,小项目可不用外键约束,代码层面控制关联
字段类型选择(MySQL)
-
主键id:BIGINT AUTO_INCREMENT
-
短字符串(账号、名称):VARCHAR(50)
-
长文本(描述):TEXT
-
数字(等级、数量):INT
-
金额:DECIMAL(10,2),不要用float
-
时间:DATETIME / TIMESTAMP,推荐DATETIME
-
布尔:TINYINT(1) 0/1
字段约束 NOT NULL:必填字段 DEFAULT:默认值 UNIQUE:唯一(账号、手机号)
四、表关系示例(文字格斗游戏举例)
-
user 用户表 id, username, password, nickname, create_time
-
item 道具表 id, item_name, attack, desc
-
user_bag 用户背包(一对多) id, user_id, item_id, count
user_id关联user.id,item_id关联item.id
五、物理设计(建库建表、索引)
建库:
CREATE DATABASE game_db DEFAULT CHARACTER SET utf8mb4;
utf8mb4 支持emoji,优先用这个字符集
索引设计
-
主键自带主键索引
-
外键关联字段(user_id、item_id)加普通索引,加快联表查询
-
查询频繁的字段加索引;不要滥用索引,索引会降低写入速度 引擎:默认 InnoDB(支持事务、外键)
六、校验与优化
检查:有没有重复数据、字段类型是否合理、关系是否正确 命名规范(推荐) 表名、字段名:小写+下划线,不要中文,不要驼峰 主键统一id;关联字段 user_id 预留扩展:预留状态字段status,时间字段方便后期排查
七、落地步骤总结
-
梳理业务,列出所有实体和属性
-
画E-R图,确定实体关系
-
拆分数据表,定义字段+类型+约束
-
设计索引,编写建表SQL
-
导入测试数据,验证查询逻辑
-
上线前做SQL性能测试
十三、数据库备份
为什么要备份: 保证重要的数据不丢失;数据转移
MYSQL数据备份的方式
-
直接拷贝物理文件
-
在sqlyog这种可视化工具中直接导出
用户备份
为什么要备份:
-
保证重要的数据不丢失
-
数据转移
MYSQL数据备份的方式
-
直接拷贝物理文件
-
在sqlyog这种可视化工具中直接导出
- 在想要导出的表或者数据库上直接右击 选择备份或导出


mysqldump 命令行导出
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 >物理磁盘:/文件名
mysqldump -hlocalhost -uroot -p123456 school student > Z:/a.sql
# 导出多张表
mysqldump -hlocalhost -uroot -p123456 school student result > Z:/a.sql
# 导出整个库
mysqldump -hlocalhost -uroot -p123456 school > Z:/a.sql
导入sql
# 登录的情况下,切换到指定的数据库
source d:/a.sql;
# 未登录的情况
mysql -u用户名 -p密码 库名<备份文件
用途:备份数据库,防止数据丢失;把数据库给别人,直接发送SQL文件。
十四、JDBC & 数据库连接池(Druid)
1. 是什么
数据库连接是重量级资源,创建、关闭连接开销很大。 连接池:预先创建一批Connection放到池子里复用,用的时候从池拿,用完归还池子,不销毁。
-
不使用连接池:每次操作 → new Connection → 用完close销毁(慢、耗资源)
-
使用连接池:启动预先建好连接 → 拿连接使用 → 归还(不销毁)
2. 核心好处
-
提升性能:避免频繁创建销毁连接,减少数据库压力
-
资源复用:连接重复使用
-
控制连接数量:限制最大连接数,防止数据库被大量连接打崩
-
统一管理:超时回收、空闲连接检测
3. 核心参数(必背)
-
initialSize初始连接数:池子启动时预先创建多少连接 -
maxActive最大连接数:池子里最多能有多少连接(上限) -
minIdle最小空闲连接:池子里最少保留多少空闲连接 -
maxWait最大等待时间:拿不到连接时,最多等待多久,超时抛异常 -
空闲连接超时:长时间不用的连接自动回收
4. 常见连接池实现
-
DBCP:Apache,老款
-
C3P0:老款,配置繁琐
-
Druid(德鲁伊,阿里):国内最常用,性能好、自带监控、防SQL注入
-
HikariCP:SpringBoot2 默认,速度最快,轻量,现在主流首选
面试一句话:SpringBoot2默认使用HikariCP。
5. 核心API(JDBC规范:javax.sql.DataSource)
所有连接池都实现DataSource接口,核心方法:
// 从池子获取连接
Connection conn = dataSource.getConnection();
// 使用完,不是关闭数据库,而是归还池子!
conn.close();
⚠️重点:连接池拿到的connection.close()不是销毁连接,是归还连接池。
6. Druid快速入门示例
Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.20</version>
</dependency>
Java代码
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
public class DruidTest {
public static void main(String[] args) throws Exception {
//1. 创建数据源对象(连接池)
DruidDataSource ds = new DruidDataSource();
//数据库配置
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/你的库名?useSSL=false");
ds.setUsername("root");
ds.setPassword("你的密码");
//连接池参数
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMinIdle(3);
ds.setMaxWait(3000);
//2. 获取连接(从池子里拿)
Connection conn = ds.getConnection();
System.out.println(conn);
//3. 归还连接,不是销毁
conn.close();
}
}
7. 连接池工作流程
-
程序启动,初始化连接池,创建initialSize个连接放入池子
-
业务代码调用dataSource.getConnection()
-
池中有空闲连接 → 直接取出
-
无空闲连接,且当前连接数 < maxActive → 创建新连接
-
连接已经达到maxActive → 等待maxWait时间,超时抛异常
-
-
使用完毕调用conn.close(),连接归还回池子,不关闭数据库连接
8. 对比:原生JDBC vs 连接池
-
原生JDBC:DriverManager.getConnection(),每次新建连接,close销毁
-
连接池:DataSource.getConnection(),复用连接,close归还
9. 面试考点
Q:为什么要用连接池? A:创建数据库连接开销巨大,连接池预先创建、复用连接,提升性能,控制连接数量保护数据库。
Q:conn.close()在连接池里做了什么? A:不是关闭数据库连接,把连接归还到连接池,等待下次复用。
Q:HikariCP和Druid区别? A:HikariCP性能极高,轻量,SpringBoot默认;Druid阿里出品,自带监控页面,防注入,功能多。
10. 常见坑
-
忘记归还连接:连接泄漏,池子连接耗尽,后面拿不到连接
-
maxActive设置过大,数据库扛不住
-
连接长时间空闲,数据库单方面断开连接,使用时报错