MySQL数据库的DML
一、创建(Create)
1. 基本语法
INSERT INTO 表名 [(列名1, 列名2, ...)]
VALUES (值1, 值2, ...);
- 省略列名条件:当值的顺序与表结构完全一致时,可省略列名(需包含所有字段值)
- 批量插入:单条语句插入多行数据提升效率
INSERT INTO student (id, name, score) VALUES (1, '张三', 99), (2, '李四', 88), (3, '王五', 77);
2. 高级技巧
- 自增主键处理:使用
AUTO_INCREMENT
时,无需显式插入主键值 - 大数据量优化:调整
max_allowed_packet
参数避免数据包过大错误SET GLOBAL max_allowed_packet = 1024*1024*100; -- 扩容至100MB[1][8]
- 核心功能:
max_allowed_packet
定义了 MySQL 服务器和客户端之间传输的数据包最大允许大小。当执行大容量插入、更新或查询时,若数据包超过该限制,会触发PacketTooBigException
错误。 - 适用场景:处理大型 BLOB 字段、批量导入数据、数据迁移等需要传输大容量数据的操作。
- 临时生效:通过
SET GLOBAL
修改的参数仅在当前 MySQL 服务运行期间生效,重启服务后会恢复为配置文件中的默认值。若需永久生效,需修改配置文件(如my.cnf
或my.ini
)并重启服务。 - 单位限制:
- 在命令行中设置时,只能使用字节数(如
1024*1024*100
),不可直接使用M
或G
单位。 - 在配置文件中则支持
M
/G
单位(如max_allowed_packet=100M
)。
- 在命令行中设置时,只能使用字节数(如
- 取值范围:最小值为
1KB
,最大值为1GB
(超过会自动调整为1GB
)。
二、读取(Retrieve)
1. 基础查询
- 全列查询:
SELECT * FROM 表名
(需警惕性能问题,建议指定必要字段) - 别名设置:增强结果可读性
SELECT name AS 学生姓名, age+5 AS 修正年龄 FROM student;
2. 聚合函数与分组
- 核心聚合函数:
SELECT COUNT(*) AS 总人数, AVG(score) AS 平均分,MAX(score) AS 最高分 FROM exam_result;
COUNT
统计行数时推荐使用COUNT(*)
,避免NULL值干扰
- 分组查询:
与WHERE区别:WHERE在分组前过滤,HAVING在分组后过滤SELECT course_id, AVG(grade) FROM study GROUP BY course_id HAVING AVG(grade) > 80; -- HAVING对分组后数据筛选[4][5]
3. 子查询
- WHERE子句嵌套:
SELECT name FROM student WHERE id IN (SELECT student_id FROM study WHERE course_id = 'CS101' );
- FROM子句派生表:
SELECT t.dept_name, avg_salary FROM (SELECT dept_id, AVG(salary) AS avg_salary FROM emp GROUP BY dept_id ) t;
4. 多表连接
- 内连接:仅返回匹配记录
SELECT s.name, sc.score FROM student s INNER JOIN study sc ON s.id = sc.student_id;
- 左外连接:保留左表所有记录
SELECT s.name, sc.score FROM student s LEFT JOIN study sc ON s.id = sc.student_id;
三、更新(Update)
1. 基础语法
UPDATE 表名
SET 列名1=值1, 列名2=值2
WHERE 条件; -- 必须指定条件避免全表更新[8][9]
示例:UPDATE emp SET salary=salary*1.1 WHERE dept='研发部';
2. 级联更新
UPDATE study
SET grade=grade+5
WHERE course_id IN (SELECT id FROM course WHERE teacher='张教授'
);
四、删除(Delete)
1. 条件删除
DELETE FROM 表名 WHERE 条件; -- 未加条件将清空全表[8]
示例:DELETE FROM log WHERE create_time < '2023-01-01';
2. 高效清空(巧用DDL)
TRUNCATE TABLE student; -- 重置自增主键,性能优于DELETE[1][8]
限制:外键约束存在时不可用,需先解除约束
五、约束与完整性
1. 主键与外键
CREATE TABLE student_course (student_id INT REFERENCES student(id) ON DELETE CASCADE,course_id INT REFERENCES course(id),PRIMARY KEY(student_id, course_id) -- 复合主键[1][5]
);
- 级联操作:
ON DELETE CASCADE
实现主表删除时自动清理关联数据
2. 唯一性与默认值
CREATE TABLE user (id INT AUTO_INCREMENT PRIMARY KEY,email VARCHAR(255) UNIQUE, -- 唯一约束status TINYINT DEFAULT 1 -- 默认值约束[8][9]
);
小结
MySQL CRUD操作是数据库开发的基础,掌握其正确使用规则可以提高开发效率和数据安全性。在实际应用中,需要注意以下几点:
- 避免频繁使用
SELECT *
,尽量指定需要的列。 - 更新和删除操作时,务必添加
WHERE
条件,防止误操作。 - 使用
TRUNCATE
时,确保表中没有外键约束。 - 合理设计表结构和约束,提高数据的完整性和一致性。
MySQL数据库优化
一、查询优化:从 SQL 到索引的全面调优
-
EXPLAIN 分析查询(查执行计划)
EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';
- 关键字段解读:
type
:ALL
(全表扫描,需优化)→ 目标优化到ref
或range
key
:显示实际使用的索引,若为NULL
表示未用索引rows
:预估扫描行数,数值越大性能越差Extra
:出现Using filesort
(额外排序)或Using temporary
(临时表)需警惕
- 关键字段解读:
-
避免全表扫描的 3 大技巧
- 索引覆盖:确保 WHERE、JOIN、ORDER BY 涉及的列都有索引
- 函数陷阱:禁止在索引列用函数(如
YEAR(created_at)
),改用范围查询-- 错误:索引失效 SELECT * FROM users WHERE YEAR(created_at) = 2023; -- 正确:索引生效 SELECT * FROM users WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';
- 模糊查询优化:避免
LIKE '%abc%'
,改用LIKE 'abc%'
(前缀匹配可用索引)
-
索引优化的黄金法则
- 覆盖索引:查询字段全在索引中,无需回表
CREATE INDEX idx_email_name ON users(email, name); -- 联合索引 SELECT email, name FROM users WHERE email = 'user@example.com'; -- 直接命中索引
- 前缀索引:对长文本(如地址)截取前 20 字符建索引,节省空间
CREATE INDEX idx_title_prefix ON articles(title(20));
- 索引避坑:
- 删除未使用的索引(如单字段索引被联合索引覆盖)
- 联合索引顺序遵循最左前缀原则(
(a,b,c)
索引对a
、a,b
生效,对b,c
无效)
- 覆盖索引:查询字段全在索引中,无需回表
-
JOIN 与分页的高效写法
- JOIN 优化:
- 用小表驱动大表(如
FROM 小表 JOIN 大表
) - 确保关联字段有索引,避免笛卡尔积
- 用小表驱动大表(如
- 分页优化(百万级数据场景):
-- 传统分页(慢):需扫描前 100000 行 SELECT * FROM users LIMIT 100000, 10; -- 优化方案:通过覆盖索引跳过偏移量 SELECT * FROM users WHERE id >= (SELECT id FROM users ORDER BY id LIMIT 100000, 1) ORDER BY id LIMIT 10;
- JOIN 优化:
二、表结构优化:从设计到存储的进阶
-
数据类型选择
- 数值型优先:用
INT
存 IP(INET_ATON()
转换),而非VARCHAR
- 避免 NULL:用默认值(如空字符串)替代,减少索引复杂度
- ENUM 妙用:有限值字段(如性别)用 ENUM 比 VARCHAR 更省空间
- 数值型优先:用
-
范式与反范式的平衡
- 范式化(减少冗余):适合写多读少场景(如日志表)
- 反范式化(适当冗余):读多写少场景(如用户表冗余常用字段)
-
分区与分库分表
- 分区表:按时间切分历史数据,加速查询
CREATE TABLE logs (id INT, log_date DATE) PARTITION BY RANGE (YEAR(log_date)) (PARTITION p2020 VALUES LESS THAN (2021),PARTITION p2023 VALUES LESS THAN (2024) );
- 分库分表:单表超千万行时用 ShardingSphere 分片
- 分区表:按时间切分历史数据,加速查询
三、服务器参数调优:关键配置详解
-
InnoDB 核心参数
innodb_buffer_pool_size = 16G # 物理内存的 70%-80% innodb_file_per_table = ON # 每个表独立表空间
-
查询缓存慎用
- 适用场景:读多写极少(如静态配置表)
- 禁用场景:高并发写入时,缓存频繁失效反降低性能
query_cache_type = 0 # 高写入场景关闭
-
连接与内存管理
max_connections = 500 # 根据业务负载调整 wait_timeout = 600 # 空闲连接 10 分钟断开 tmp_table_size = 64M # 增大临时表内存
四、架构优化:高可用与扩展方案
- 读写分离
- 主库处理写操作,从库处理读请求(用 ProxySQL 路由)
- 高可用方案
- MHA:自动故障转移,主库宕机 30 秒内切换
- Galera Cluster:多主同步,适合写负载均衡场景
- 缓存与负载均衡
- Redis 缓存热点数据:减少数据库压力
- HAProxy:均衡读请求到多个从库
五、锁与事务优化:并发控制秘诀
-
隔离级别选择
- 默认
REPEATABLE READ
适合多数场景 - 高并发读写可用
READ COMMITTED
减少锁竞争
- 默认
-
死锁监控与处理
innodb_print_all_deadlocks = 1 # 记录死锁日志
- 重试机制:代码层捕获死锁异常后自动重试
六、监控与工具:数据库的“健康管家”
-
内置工具
- 慢查询日志:定位耗时 SQL
slow_query_log = 1 long_query_time = 2 # 记录超过 2 秒的查询
- SHOW PROCESSLIST:实时查看活跃连接
- 慢查询日志:定位耗时 SQL
-
第三方利器
- Percona Toolkit:分析索引效率与表结构
- Prometheus + Grafana:可视化监控 QPS、连接数等
七、硬件与系统优化:底层性能基石
-
磁盘与文件系统
- SSD 替代 HDD:随机读写性能提升 10 倍+
- XFS 文件系统:禁用
atime
减少磁盘写入mount -o noatime,nodiratime /dev/sdb1 /data
-
内核参数调优
- TCP 缓冲区:增大网络吞吐量
- 文件句柄数:避免
Too many open files
错误
八、持续维护:数据库的“养生之道”
- 定期维护
- 每月优化碎片化表:
OPTIMIZE TABLE large_table;
- 清理历史数据:分区表直接
DROP PARTITION
- 每月优化碎片化表:
- 避免过度优化
- 二八原则:优先优化 20% 高频查询
- 业务优先:架构扩展前评估投资回报率(如分库分表成本高)
优化顺序指南
- 紧急处理:慢查询优化(见效最快)
- 结构调优:索引、表设计、分区
- 参数调优:InnoDB 配置、连接数
- 架构扩展:读写分离、缓存层
- 硬件升级:SSD、内存扩容
总结
MySQL 优化是持续过程,需结合业务场景选择策略。建议从 EXPLAIN 分析和索引优化入手,逐步深入架构设计。记住:“没有银弹,只有最适合的方案!”