当前位置: 首页 > news >正文

MySQL (三):库操作、表操作、性能分析

一、数据库操作基础 

1.1 查看现有数据库

在 MySQL 中,可以使用SHOW DATABASES;命令查看当前服务器上所有的数据库。这个命令会返回一个包含所有数据库名称的列表,方便用户了解服务器上的数据库资源。

SHOW DATABASES;

 

1.2 创建新数据库

使用CREATE DATABASE语句可以创建一个新的数据库。例如,创建一个名为chat的数据库:

CREATE DATABASE chat;

CREATE DATABASE chat;看似简单,但背后涉及字符集和排序规则的默认配置。实际开发中,建议显式指定字符集以避免乱码问题:

CREATE DATABASE chat CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  • utf8mb4:支持包括 emoji 在内的所有 Unicode 字符(传统 utf8 仅支持 3 字节字符)​
  • utf8mb4_unicode_ci:按 Unicode 标准排序,支持多语言正确比较​

查看数据库详细信息的命令:

SHOW CREATE DATABASE chat; -- 查看创建语句及字符集配置

1.3 使用数据库

在操作特定数据库之前,需要先使用USE语句切换到该数据库:

USE chat;

1.4 删除数据库

删除数据库需要谨慎操作,因为这会永久删除数据库中的所有数据。使用DROP DATABASE语句:

DROP DATABASE chat;

DROP DATABASE是高危操作,生产环境中应:​

  1. 执行前先备份(mysqldump -u root -p chat > chat_backup.sql)​
  2. 启用数据库审计日志(如开启 MySQL 的 general log 记录操作)​
  3. 限制 DROP 权限(通过 GRANT 语句控制用户权限)

二、数据表操作详解

2.1 创建数据表

创建数据表时需要定义表结构,包括字段名、数据类型和约束条件。以下是一个创建user表的示例:

CREATE TABLE user (id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '用户唯一标识',name VARCHAR(50) UNIQUE NOT NULL COMMENT '用户名,最长50字符',age TINYINT UNSIGNED NOT NULL COMMENT '年龄,0-255范围',sex ENUM('w','m') NOT NULL DEFAULT 'm' COMMENT '性别:w-女,m-男'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基本信息表';

这个表包含四个字段:

  • id:自增主键,确保唯一性
  • name:用户名,唯一且不能为空
  • age:年龄,使用 TINYINT 节省空间
  • sex:性别,使用 ENUM 类型限制取值范围
  • AUTO_INCREMENT 的隐患:自增主键达到最大值(INT UNSIGNED 为 4294967295)后,新插入会报Duplicate entry错误,需提前规划字段类型(如改用 BIGINT)​
  • ENUM 类型的取舍:适合值固定且数量少的场景(如性别),但修改枚举值需 ALTER TABLE,不如 tinyint 灵活(可关联字典表)

2.2 查看表结构

使用DESC命令可以查看表的详细结构:

DESC user;

DESC user;只能看到基础结构,更全面的信息来自: 

SHOW FULL COLUMNS FROM user; -- 显示字段注释、字符集等完整信息
SHOW TABLE STATUS LIKE 'user'; -- 查看表引擎、数据量、碎片率等

2.3 删除数据表

删除表会删除表中的所有数据和表结构:

DROP TABLE user;

2.4 查看表创建语句

使用SHOW CREATE TABLE可以查看创建表的原始 SQL 语句:

SHOW CREATE TABLE user;

三、数据增删改查(CRUD)

3.1 插入数据

单条插入
INSERT INTO user(name, age, sex) VALUES('zhangsan', 20, 'M');
批量插入
INSERT INTO user(name, age, sex) VALUES('zhangsan', 20, 'M'), ('lisi', 22, 'M');
两种插入方式的性能差异

从网络通信角度分析,这两种插入方式存在显著差异:

单条插入与批量插入的性能差异,本质是TCP 连接开销的影响:​

  • 单条插入:N 条数据需要 N 次 TCP 三次握手(3N 个数据包)+ N 次四次挥手(4N 个数据包)​
  • 批量插入:1 次 TCP 连接即可完成,仅 3+4=7 个数据包​

批量插入的最佳实践:​

  • 单次插入不超过 1000 行(避免数据包过大导致超时)​
  • 结合事务:START TRANSACTION; ... COMMIT;减少日志刷新次数​
  • 使用LOAD DATA LOCAL INFILE导入超大量数据(比 INSERT 快 10 倍以上)

在大数据量插入场景下,批量插入的性能优势明显。

3.2 删除数据

删除指定条件的数据:

DELETE FROM user WHERE id=1;

注意事项

DELETE FROM user WHERE id=1;执行后:​

  • 自增主键不会回退(AUTO_INCREMENT值保持当前最大值)​
  • InnoDB 会标记数据为删除(墓碑标记),不立即释放空间(产生碎片)​
  • 关联表的外键约束可能触发级联删除(需提前了解表关系)

清理碎片的方法:

OPTIMIZE TABLE user; 
-- 适用于MyISAM,InnoDB推荐ALTER TABLE user ENGINE=InnoDB;

3.3 更新数据

更新数据可以修改表中的现有记录:

UPDATE user SET age=age+1 WHERE name='zh

性能取决于:​

  • name字段是否有索引:无索引则全表扫描,有索引则快速定位​
  • 更新字段是否为索引列:修改索引列会导致索引重建,开销更大​
  • 事务隔离级别:高隔离级别(如 REPEATABLE READ)可能产生更多锁等待

四、单表查询

4.1 基础查询

查询所有字段:

SELECT * FROM user;

查询指定字段:

SELECT id, name, age FROM user;

4.2 条件查询

使用WHERE子句进行条件筛选:

SELECT id, name, age FROM user WHERE age >= 20 AND age <= 22;

使用BETWEEN简化范围查询:

SELECT id, name, age FROM user WHERE age BETWEEN 20 AND 22;

    使用INNOT IN进行枚举值查询:

    SELECT id, name, age FROM user WHERE age IN (20, 21);
    SELECT id, name, age FROM user WHERE age NOT IN (20, 21);
    

    以WHERE age BETWEEN 20 AND 22为例,MySQL 的执行步骤:​

    1. 检查age是否有索引:​

    • 有索引:通过 B + 树快速定位范围数据​
    • 无索引:全表扫描(逐行判断条件)​

    2. 过滤出符合条件的记录(server 层操作)​

    3. 提取需要的字段返回

    范围查询的注意事项:​

    • BETWEEN包含边界值,与>= AND <=完全等价​
    • IN (20,21)在值数量少时高效,超过 5 个建议用BETWEEN或子查询

    4.3 模糊查询

    使用LIKE进行模糊匹配:

    SELECT id FROM user WHERE name LIKE 'zhang%';
    

    %匹配任意个字符,_匹配单个字符。

    LIKE 'zhang%'与LIKE '%zhang'的本质区别:​

    • zhang%:可利用name字段的前缀索引(前提name索引有效)​
    • %zhang:无法使用索引(必须全表扫描)​

    优化模糊查询的方案:​

    • 前缀匹配优先('zhang%')​
    • 数据量小时可用LOCATE('zhang', name) > 0替代%zhang%​
    • 大数据量场景引入全文索引(FULLTEXT INDEX)或搜索引擎(如 Elasticsearch)

    4.4 去重查询

    使用DISTINCT去除重复值:

    SELECT DISTINCT age FROM user;
    

    4.5 结果集合并

    使用UNIONUNION ALL合并多个查询结果:

    SELECT id, name, age FROM user WHERE age >= 20 UNION ALL SELECT id FROM user WHERE name='zhangsan';
    

    4.6 回表

    回表是指在索引中找到记录的主键后,还需要回到主键索引中获取完整数据的过程。例如:

    SELECT * FROM user WHERE name='zhangsan';
    

    如果name字段有索引,查询会先通过索引找到主键,再通过主键获取其他字段的值,这就是回表。

    当执行SELECT * FROM user WHERE name='zhangsan';且name有普通索引时:​

    1. 先在name索引树找到对应的主键 id(索引扫描)​
    2. 再到主键索引树查询完整记录(回表操作)

    避免回表的方法:使用覆盖索引

    -- 创建包含所需字段的联合索引
    CREATE INDEX idx_name ON user(name, age, sex);
    -- 此时查询无需回表
    SELECT name, age, sex FROM user WHERE name='zhangsan';

    五、分页查询

    5.1 基本分页

    使用LIMIT实现分页:

    SELECT * FROM user LIMIT 3;  -- 取前三条记录
    SELECT * FROM user LIMIT 1, 3;  -- 从第2条记录开始取3条
    SELECT * FROM user LIMIT 3 OFFSET 1;  -- 同上
    

    当偏移量超过 10 万时,LIMIT 100000, 20的执行逻辑是:​

    1. 扫描前 100020 条记录​
    2. 丢弃前 100000 条,返回最后 20 条​

    这就是为什么随着页码增大,分页查询会越来越慢。

    5.2 分页性能优化

    当偏移量很大时,传统分页方式性能较差。例如:

    SELECT * FROM user LIMIT 10000, 20;
    

    这种查询会扫描前 10000 条记录,然后丢弃,只返回 20 条,效率极低。

    优化方案:使用主键限制替代偏移量

    SELECT * FROM user WHERE id > 10000 LIMIT 20;
    

    这种方式直接从指定主键位置开始查询,避免了大量的扫描操作,提高了分页效率。

    优势:​

    • 直接通过主键索引定位,无需扫描前置记录​
    • 索引查找复杂度为 O (logN),而非全表扫描的 O (N)​

    局限性:​

    • 仅适用于连续主键且无删除的场景(有删除会出现跳页)​
    • 无法支持 "跳转到第 100 页" 的业务需求(除非前端记录上一页最后 id)

    5.3 执行计划分析

    使用EXPLAIN命令可以查看 SQL 语句的执行计划:

    EXPLAIN SELECT * FROM user LIMIT 3;
    

    执行计划可以帮助我们了解查询是如何执行的,是否使用了索引等,从而进行性能优化。

    关注type列:​

    • ALL:全表扫描(性能差)​
    • range:范围索引扫描(性能好)​
    • ref/eq_ref:精确索引匹配(性能最优)

    六、排序

    6.1 基本排序

    使用ORDER BY进行排序:

    SELECT * FROM user ORDER BY name;  -- 默认升序
    SELECT * FROM user ORDER BY name ASC;  -- 显式指定升序
    SELECT * FROM user ORDER BY name DESC;  -- 降序
    SELECT * FROM user ORDER BY name, age;  -- 多字段排序
    

    ORDER BY name的执行方式:​

    • 内存排序:数据量小(小于sort_buffer_size)时,在内存中完成排序​
    • 外排序:数据量大时,需写入临时文件(磁盘 IO 开销大)

    6.2 排序性能优化

    优化排序的核心:让排序在索引中完成(避免文件排序)

    -- 创建排序字段的索引
    CREATE INDEX idx_name ON user(name);
    -- 此时排序直接使用索引顺序,无需额外排序
    SELECT name FROM user ORDER BY name;

    多字段排序的索引设计:

    -- 按name升序、age降序排序
    CREATE INDEX idx_name_age ON user(name ASC, age DESC);
    SELECT name, age FROM user ORDER BY name, age DESC;

    七、分组查询

    7.1 基本分组

    使用GROUP BY进行分组统计:

    SELECT age, COUNT(age) AS number FROM user GROUP BY age;
    

    注意:分组查询中,SELECT子句中的字段要么是分组字段,要么是聚合函数。否则可能会得到无意义的结果。

    7.2 分组条件筛选

    使用HAVING子句对分组结果进行筛选:

    SELECT age, COUNT(age) AS number FROM user GROUP BY age HAVING age > 20;
    

    性能优化:优先使用WHERE条件而不是HAVING,因为WHERE可以在分组前过滤数据,而HAVING是在分组后过滤。如果分组字段有索引,WHERE还可以利用索引提高性能。

    SELECT age, COUNT(age) AS number FROM user WHERE age > 20 GROUP BY age;
    

    7.3 分组与排序

    分组查询默认会对结果进行排序,这可能会影响性能。如果不需要排序,可以使用ORDER BY NULL禁用排序:

    SELECT age, COUNT(age) AS number F

    GROUP BY age本质是先排序后分组,因此会继承排序的性能问题。执行计划中出现Using temporary; Using filesort表示使用了临时表和文件排序,需优化。

    八、存储过程示例

    8.1 创建存储过程

    以下是一个批量插入数据的存储过程示例:

    DELIMITER $
    CREATE PROCEDURE add_t_user(IN N INT)
    BEGINDECLARE i INT DEFAULT 0;-- 开启事务减少日志刷新START TRANSACTION;WHILE i < N DOINSERT INTO t_user VALUES(NULL, CONCAT(i+1, '@fixbug.com'), i+1);SET i = i + 1;-- 每1000条提交一次,避免事务过大IF i % 1000 = 0 THENCOMMIT;START TRANSACTION;END IF;END WHILE;COMMIT;
    END$
    DELIMITER ;

    8.2 调用存储过程

    CALL add_t_user(20000);
    

    存储过程可以将复杂的业务逻辑封装在数据库端,减少客户端与服务器之间的交互次数,提高处理效率

    九、性能优化总结

    1. 批量操作:尽量使用批量插入、更新,减少 TCP 连接开销
    2. 索引优化:为经常用于查询条件和排序的字段创建索引
    3. 分页优化:大数据量分页使用主键限制替代偏移量
    4. 避免回表:查询时尽量只选择需要的字段,避免SELECT *
    5. 合理分组:优先使用WHERE过滤数据,避免分组后的HAVING操作
    6. 禁用不必要排序:分组查询中不需要排序时使用ORDER BY NULL

    通过以上优化方法,可以显著提高 MySQL 数据库的查询和操作性能,提升系统整体效率。

    相关文章:

  • 2023国赛linux的应急响应-wp
  • ChatboxAI 搭载 GPT 与 DeepSeek,引领科研与知识库管理变革
  • 白皮精读——2024年数据要素化新阶段的数据基础设施白皮书【附全文阅读】
  • web网页开发,在线%旅游景点管理%系统demo,基于Idea,vscode,html,css,vue,java,maven,springboot,mysql
  • 1.2 基于蜂鸟E203处理器的完整开发流程
  • 系统架构设计师论文分享-论ATAM的使用
  • 【分布式机架感知】分布式机架感知能力的主流存储系统与数据库软件
  • Python爬虫实战:研究sanitize库相关技术
  • 第2048天:我的创作纪念日
  • 什么是DPoS(Delegated Proof of Stake,委托权益证明)
  • 展开说说:Android之ContentProvider源码浅析
  • 【文献阅读】风速和植被覆盖度主导了风蚀变化
  • ThinkBook 15 IIL(20SM)恢复开箱状态预装OEM原厂Win10系统包
  • C 语言中的数组指针数组与函数指针数组
  • WPF XAML 格式化工具(XAML Styler)
  • 黑马python(十八)
  • SpringAI大模型应用开发
  • IDEA2024.3 tomcat需要按两次停止按钮停止问题
  • React用户交互事件
  • 使用vue3构建一套网站