MySQL数据库
一、索引问题
1. 索引的定义
索引是数据库表中一列或多列的值进行排序的一种数据结构,类似于书籍的目录。
可以通过索引直接定位数据,无需全表扫描,减少I/O操作;也可以通过索引的排序结构快速找到符合条件的记录,从而显著提高数据库的查询效率。
2. 索引的原理
MySQL索引的核心原理基于树状数据结构(如 B+树)或哈希表。以下是常见索引类型的工作原理:
B + 树索引(最常用)
结构特点:
每个节点可以有多个子节点(扇出大),形成平衡的多路搜索树。
叶子节点包含实际数据或指向数据的指针,非叶子节点仅存储索引键和指针。
所有叶子节点通过指针相连,形成有序链表,支持范围查询。
工作流程:
查询时:从根节点开始,通过比较索引键快速定位到叶子节点。
插入 / 删除时:自动调整树结构保持平衡,确保查询效率稳定。
适用场景:
范围查询(如WHERE age > 20)。
排序(如ORDER BY)。
等值查询(如WHERE name = 'Alice')。
哈希索引
结构特点:
使用哈希函数将索引键映射到哈希表中,直接定位数据。
仅支持等值查询(如WHERE id = 1),不支持范围查询。
工作流程:
查询时:计算索引键的哈希值,直接访问对应槽位。
冲突处理:若多个键哈希值相同,通过链表或开放寻址解决。
适用场景:
内存表(Memory引擎)默认使用哈希索引。
缓存系统(如 Redis)中的快速查找。
3. 聚簇 索引 VS 非 聚簇 索引
聚簇索引
索引的叶子节点直接存储数据行(数据即索引),找到了索引就找到了需要的数据,每个表只能有一个聚集索引(通常是主键)。
示例:InnoDB 存储引擎的主键索引就是聚集索引。
非聚簇索引
索引的存储和数据的存储是分离的,查询时需先通过索引找到主键,再通过主键回表查询数据。
示例:二级索引(如普通索引)属于非聚集索引。
4. 索引 的优缺点
优点:
大幅提高查询速度。
减少排序和分组操作的时间。
支持唯一性约束(如UNIQUE索引)。
缺点:
占用额外存储空间。
插入 / 更新 / 删除操作变慢(需维护索引结构)。
设计不当可能导致索引失效(如使用函数或模糊查询)。
5. 索引的使用建议
创建索引的时机:
经常用于WHERE、JOIN、ORDER BY的字段。
高基数(重复值少)的字段(如用户 ID、邮箱)。
避免创建索引的情况:
频繁更新的字段。
低基数字段(如性别、状态码)。
表数据量较小(全表扫描更快)。
- 示例:创建索引
sql
-- 创建普通索引CREATE INDEX idx_name ON users (name);
-- 创建唯一索引CREATE UNIQUE INDEX idx_email ON users (email);
-- 创建复合索引(多列索引)CREATE INDEX idx_name_age ON users (name, age);
总结
MySQL 索引通过树状结构(如 B + 树)或哈希表实现快速数据定位,显著提升查询效率。合理设计索引需要根据业务场景权衡查询性能和维护成本,避免过度索引。理解索引的底层原理是优化数据库性能的关键。
B树索引和B+树索引的区别是什么?
B 树(B-Tree)和 B + 树(B+ Tree)是数据库索引中常用的两种数据结构,它们的主要区别体现在结构设计和应用场景上。以下是具体对比:
- 结构差异
B 树(B-Tree)
节点结构:每个节点包含键值(key)和数据(data),非叶子节点也可能包含数据。
叶子节点关系:叶子节点之间无指针连接,不形成有序链表。
数据分布:所有节点都可能存储数据,查询可能在非叶子节点终止。
B + 树(B+ Tree)
节点结构:非叶子节点仅存储键值和指针,数据只存在于叶子节点。
叶子节点关系:所有叶子节点通过指针相连,形成有序链表。
数据分布:数据仅存在于叶子节点,所有查询最终都要到达叶子节点。
- 图示对比
plaintext
B树结构示例:
3, 7
/ | \
1,2\] \[4,5,6\] \[8,9
B+树结构示例:
3, 7
/ \
1,2,3\] -\> \[4,5,6,7,8,9\] (叶子节点通过指针相连) 3. 查询效率 B 树: 单次查询可能在非叶子节点结束,平均查询效率略高。 但每个节点存储数据,导致节点容量变小,树的高度可能更高。 B + 树: 所有查询必须到达叶子节点,单次查询效率可能略低。 叶子节点存储全部数据,节点容量更大,树的高度更低,减少 I/O 次数。 优势:支持范围查询,可通过叶子节点的链表快速遍历。 4. 插入 / 删除操作 B 树: 插入 / 删除可能导致非叶子节点的结构调整,操作复杂度较高。 每个节点都可能存储数据,维护成本更高。 B + 树: 插入 / 删除仅在叶子节点进行,操作更简单。 叶子节点之间通过指针相连,分裂时无需调整上层节点。 5. 应用场景 B 树: 适用于随机查询为主的场景(如文件系统索引)。 不适合范围查询,因为叶子节点无链表连接。 B + 树: 数据库索引的主流选择(如 MySQL InnoDB)。 支持高效的范围查询和排序(如ORDER BY)。 磁盘读写更友好,因为节点容量大,减少 I/O 次数。 6. 总结对比表 |--------|-------------|-------------| | 特性 | B 树 | B + 树 | | 数据存储位置 | 所有节点都可能存储数据 | 仅叶子节点存储数据 | | 叶子节点连接 | 无指针连接 | 通过链表有序连接 | | 范围查询效率 | 低(需重复遍历树) | 高(通过链表直接遍历) | | 节点容量 | 较小(需存储数据) | 较大(仅存储键值) | | 适用场景 | 文件系统、随机查询 | 数据库索引、范围查询 | 为什么 MySQL 选择 B + 树? 范围查询:B + 树的叶子节点链表支持高效的范围扫描(如WHERE age BETWEEN 20 AND 30)。 磁盘友好:节点容量大,树的高度低,减少磁盘 I/O。 稳定性:插入 / 删除操作仅影响叶子节点,减少树的调整频率。 B + 树通过牺牲部分随机查询效率,换取了更稳定的插入 / 删除性能和高效的范围查询能力,更适合数据库的应用场景。 MySQL的数据类型: 一、数字类型: 1、int:整数(4字节)id/序号/自增列 2、bigint:大整数(8字节)超大计数 3、decimal(M,D):定点数(精准小数)M总位数,D小数位,精准无误差,金额/汇率。 二、字符串类型: 1、char(N):定长字符串(N为长度),短固定长度(手机号码) 2、varchar(N):变长字符串(N为最大长度),名称/地址/描述 3、text:长文本(最大65535个字符),文章内容/备注 三、日期时间类型: 1、date:日期(年月日);格式:YYYY-MM-DD;生日/下单日期 2、time:时间(时分秒);格式:HH:MM:SS;具体时间/打卡时间 3、datetime:日期+时间;格式:YYYY-MM-DD HH:MM:SS;创建时间、更新时间(最常用) 4、timestamp:时间戳,记录时间(自动更新),受时区的影响,不建议使用。 四、布尔类型: 1、BOOLEAN:TRUE/FLASE 创建表的SQL语句 CREATE TABLE \`order_db\`.\`user_info\` ( \`id\` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID(自增)', \`user_id\` varchar(10) NOT NULL COMMENT '用户ID唯一索引', \`user_name\` varchar(50) NOT NULL COMMENT '用户姓名', \`user_sex\` char(2) DEFAULT NULL COMMENT '用户性别', \`user_phone\` char(11) DEFAULT NULL COMMENT '用户手机号码,11位,可为null', \`user_age\` int unsigned NOT NULL COMMENT '用户年龄(非负)', \`birthday\` date DEFAULT NULL COMMENT '生日(仅年月日)', \`address\` varchar(255) DEFAULT NULL COMMENT '用户地址', \`create_date\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间(默认当前时间)', \`update_date\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间(默认当前时间,更新时自动刷新)', \`flag\` int NOT NULL DEFAULT '1' COMMENT '是否有效:1=有效,0=无效', PRIMARY KEY (\`id\`), UNIQUE KEY \`uk_user_info_use_id\` (\`user_id\`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户信息表'; * 字段的增删改的SQL,以及索引的增删的SQL alter table user_info add column test1 varchar(10); alter table user_info add column test2 varchar(10); alter table user_info add column test3 varchar(10); alter table user_info drop column test3; create index index_user_info_create_date on user_info(create_date); alter table user_info add index index_user_info_update_date(update_date); * 字符串函数 |-------------|------------|--------------------------------|------------| | 函数 | 说明 | 示例 | 结果 | | CONCAT() | 拼接字符串 | CONCAT('Hello', 'MySQL') | HelloMySQL | | LENGTH() | 获取字符串长度 | length('MySQL') | 5 | | SUBSTRING() | 截取字符串 | substring('Hello, Kitty', 1,5) | Hello | | TRIM() | 去除字符串首尾的空格 | trim(' MySQL ') | MySQL | | REPLACE() | 替换字符串 | replace('MySQL', 'SQL', 'DB') | MyDB | * jion表的关联 where和on后面关联关系的区别 左表的谓词放在where后面有效,右表的谓词放在on后面有效。 都放在on后面,因为left join的特性,左表的数据全部显示,右表过滤后再关联。 左表的谓词放在on后面,右表的谓词放在where,先过滤右表,然后左右表因为t1.id = t2.id左右表的关联关系,导致左关联关系变成了内关联关系。 左表的谓词放在where,右表的谓词放在on后面,左右表先过滤,然后再关联,显示左表过滤之后所有的数据。 左表和右表都放在where后面,左右表先过滤,然后因为t1.id = t2.id的左右表的关联关系,导致左关联关系变成了内关联关系。 * 子查询 EXISTS的核心是判断子查询是否有结果行,只返回 TRUE/FALSE,不关心子查询的具体返回值; EXISTS常与关联子查询配合使用,是查询 "存在性" 场景的最优选择,大数据量下性能优于IN; NOT EXISTS是反向判断,用于查询 "不存在某类记录" 的场景,逻辑清晰且高效。 子查询的优化方法 1、优先使用join代替子查询 2、大数据量的使用exists,减少读取数据的次数。 3、在关联字段,查询字段增加索引 4、避免多层嵌套子查询 5、使用explain查看执行计划 * 窗口函数 开窗函数使用场景 1、求某个分组下的最大/最小值/TOP n值对应信息,如年级中每个班级的第一名,大区中销售额最高城市,此处用排序函数 row_number(),需用 order by 排序。 2、对某个分组求和/个数/均值,如城市历史截至昨天累积销售额/营业天数/平均销售额,此处用sum()/count()/avg(),需用 order by 默认的定位框架。 3、相邻时间求时间差,如用户复购时间周期,此处用lag(),需用 order by 排序。 4、计算产品销售的趋势/排名/占比,以及销售目标达成分析,此处需要用到 lag()/sum()/avg()/row_number()。 * sql优化,执行计划explain * 留存率问题 * 11 *