数据库基础-牛客

MySQL 的存储引擎有哪些?它们之间有什么区别?默认使用哪个? #

MySQL的存储引擎是用于存储、处理和数据保护的核心组件。

  • InnoDB
    • 特点:支持事物、行级锁、外键约束
    • 优点:数据完整性高,适合高并发读写,崩溃后自动恢复
    • 缺点:需要权衡其性能和资源消耗
    • 应用场景:需要事务支持的应用
  • MyISAM
    • 特点:不支持事务,使用表级锁,访问速度快
    • 优点:读操作性能高,占用空间小
    • 缺点:数据完整性差,奔溃后恢复困难
    • 应用场景:读多写少的场景

SQLite 与 InnoDB 数据库引擎的主要区别 #

SQLite 和 InnoDB 都是流行的数据库引擎,但它们在设计理念、架构和应用场景上有显著差异:

1. 基本架构差异 #

特性 SQLite InnoDB
架构类型 嵌入式、无服务器 客户端-服务器(作为MySQL存储引擎)
进程模型 库形式直接链接到应用程序 作为MySQL服务器的一部分运行
部署方式 单文件数据库 多文件存储系统

2. 并发处理能力 #

特性 SQLite InnoDB
锁机制 数据库级锁(写独占) 行级锁
并发写 同一时间只允许一个写操作 支持多事务并发写入
隔离级别 默认SERIALIZABLE 支持多种隔离级别

3. 存储与性能 #

特性 SQLite InnoDB
存储格式 单个磁盘文件 表空间文件(ibd)系统
索引类型 B-tree B+tree
缓存 页面缓存 缓冲池(更复杂的内存管理)
全文搜索 需要FTS扩展 内置FULLTEXT索引支持

4. 功能特性对比 #

特性 SQLite InnoDB
外键 需要显式启用 默认支持
ACID 完全支持 完全支持
复制 不支持 通过MySQL主从复制支持
分区 不支持 支持表分区

SQLite的B-tree与InnoDB的B+tree区别 #

SQLite和InnoDB虽然都使用基于树结构的存储引擎,但它们在实现上有显著差异:

核心数据结构差异 #

特性 SQLite的B-tree InnoDB的B+tree
基本结构 纯B-tree结构 B+tree变种(所有数据存储在叶子节点)
节点结构 每个节点都包含键和数据 内部节点只存键,数据全在叶子节点
叶子节点链接 叶子节点没有相互链接 叶子节点通过指针双向链接形成有序链表
数据存储位置 数据可存在于任何节点 数据仅存在于叶子节点

性能特点对比 #

特性 SQLite的B-tree InnoDB的B+tree
点查询性能 平均O(log n) 平均O(log n),但更稳定
范围查询性能 需要多次树遍历 通过叶子节点链表高效扫描
插入/删除效率 节点分裂/合并频率较高 更平衡的结构,操作更稳定
空间利用率 通常较低(约50-70%) 更高(通常75%以上)

实现细节差异 #

  1. SQLite的B-tree
    • 使用称为"B*-tree"的变体
    • 页面大小固定(默认4KB)
    • 支持表使用B-tree,索引使用B-tree
    • 没有预读优化
  2. InnoDB的B+tree
    • 高度优化的B+tree实现
    • 支持可变页面大小(默认16KB)
    • 表数据以聚簇索引形式存储
    • 具有预读和缓存优化

InnoDB 是如何存储数据的? #

InnoDB通过表空间、页和行的结构化方式存储数据,将数据保存在磁盘上的数据文件中,采用聚簇索引来组织数据行,支持事务外键和行级锁,从而实现高效的的数据检索和管理。

  • 表空间
    • InnoDB使用表空间来存储所有数据,包括表、索引和事务日志
    • 表空间可以是共享的(系统表空间)或独立的(每一个表一个.ibd文件)
    • 数据在表空间中以页为单位存储,每页大小通常为16KB
    • 页是InnoDB的最小存储单位,包含行数据、索引等。
    • 行是数据存储的基本存储单位,每行存储在页中
    • InnoDB支持行级锁,运行高并发访问
  • 索引
    • InnoDB使用B+树来加速数据检索
    • 主索引(聚簇索引)将数据存储在叶子节点,辅助索引存储指向主键的指针。
  • 事务日志
    • 用于记录事务操作,支持奔溃恢复。

图示说明 #

+-------------------+
|  表空间            |
| +--------------+  |
| | 页 (16KB)     | |
| | +----------+  | |
| | | 行        | | |
| | | 行        | | |
| | +----------+ | |
| +--------------+ |
+-------------------+
  • 表空间:存储所有数据。
  • :最小存储单位。
  • :基本数据单元。

通过这种结构化的存储方式,InnoDB 能够高效地管理和检索数据,支持事务和并发访问。

MySQL 一行记录是怎么存储的? #

一行数据存储在数据页中,包含行头信息、实际数据和可变长度字段的偏移量,具体结构因存储引擎而异。

    • 数据以页为单位存储,每页通常16KB
    • 页中包含多行记录
  • 行记录结构

    • 行头信息:存储行的元数据,如删除标记,下一行指针等
    • 固定长度字段:存储定长数据,如整数、定长字符串
    • 可变长度字段列表:存储变长数据的偏移量
    • NULL值列表:记录哪些字段为NULL
  • 行溢出

    • 如果行数据过大,可能会导致行溢出,数据存储在溢出页中
  • 压缩

    • InnoDB 支持行压缩,减少存储空间

详细描述一条 SQL 在 MySQL 中的执行过程 #

一条sql在mysql中的执行过程包括解析器解析sql语句,优化器生成执行计划,存储引擎执行计划并返回结果,最后由mysql服务层处理和返回客户端。

  • 连接管理

    • 客户端通过连接器与MySQL服务器建立连接
  • 查询解析

    • SQL语句被发送到解析器,进行语法和语义检查
    • 生成解析树
  • 查询优化

    • 优化器对解析树进行优化,选择最优执行计划
    • 考虑索引,表连接顺序等
  • 执行计划生成

    • 根据优化结果生成执行计划
  • 执行引擎

    • 执行引擎根据执行计划访问存储引擎,获取数据
  • 结果返回

    • 将结果集返回给客户端
  • 缓存

    • 查询结果可能会被缓存,以提高后续相同查询的速度

MySQL 的查询优化器如何选择执行计划? #

通过分析sql语句的多种执行路径,结合表的统计信息、索引的可用性、查询条件等因素。评估每种路径的成本,选择代价最低的执行计划。优化器会考虑使用索引、表连接顺序、排序和分组等操作,以提高查询效率。最终优化器生成的执行计划将传递给执行器执行。

  • 语法分析与语义检查

    查询优化器首先解析SQL语句,将其转换为内部的逻辑查询树,并进行语义检查,确保查询的语法和逻辑是正确的。

  • 成本估算

    优化器会根据表的统计信息(如行数、索引分布、列的基数等)来估算不同执行计划的成本。这些统计信息是通过ANALYZE TABLE或自动更新机制获取的。成本估算包括I/O成本、CPU成本和网络成本等。

  • 生成可能的执行计划

    优化器会生成多种可能的执行计划,包括不同的表连接顺序、索引使用方式、连接算法(如嵌套循环连接,哈希连接等)和子查询处理方式。

  • 选择最优计划

    优化器通过比较不同执行计划的成本,选择成本最低的计划作为最终执行计划。优化器会考虑多种因素,如索引的选择,表的大小,过滤条件的效率等。

  • 动态调整

    在某些情况下,优化器还会根据运行时信息(如缓存命中率,实时数据分布等)动态调整计划,进一步优化查询性能。

SQL 中 select、from、join、where、group by、having、order by、limit 的执行顺序? #

from,选择查询的表,join连接关联表,where,根据条件过滤表数据,group by,对数据进行分组,having,对分组进行过滤,select选择查询的列,order by,对数据排序,limit限制返回数据大小

SQL 的执行顺序通常为:from->join->where->group by->having->select->order by->limit。

MySQL 中的数据排序(ORDER BY)是如何实现的? #

使用排序算法对结果集进行排序,可能设计文件排序和内存排序

  • 索引排序

    如果查询的order by字段上有合适的索引,mysql可以直接利用索引的有序性来返回排序结果,而无需额外的排序操作。这种方式效率最高,因为它避免了额外的内存或磁盘排序开销。

  • 文件排序

    当无法使用索引进行排序时,mysql会使用文件排序算法。如果数据量较小,排序会在内存中完成;如果数据量较大,超过了系统配置的内存限制,则会将部分数据写入磁盘临时文件,然后进行外部排序。文件排序可能会涉及多次磁盘I/O,因此性能相对较低。

  • 优先队列排序

    对于某些特定的查询,如带有limit的查询,mysql可能会使用优先队列(也称为堆排序)来优化排序过程。优先队列排序可以在数据到达时动态维护一个有序队列,从而减少排序开销。

排序算法

  • mysql使用快速排序或合并排序等高效算法

多列排序

  • 支持按多列排序,先按第一列排序,再按第二列排序

排序方向

  • 支持生序和降序排序

如何实现数据库不停服迁移? #

关键步骤包括:使用主从复制或数据同步工具复制数据,采用双写机制确保数据一致性,逐步切换读写流量

并监控系统稳定性,进行数据校验,准备回滚机制以应对问题,以及实时监控和优化系统性能。

  • 数据复制

    使用主从复制或数据同步工具,将数据从源数据库复制到目标数据库

  • 双写机制

    在迁移期间,应用程序同时写入源数据库和目标数据库,确保数据一致性。

  • 流量切换

    逐步将读写流量从源数据库切换到目标数据库,监控系统稳定性

  • 数据校验

    在迁移完成后,进行数据校验,确保源数据库和目标数据的数据一致性

  • 回滚机制

    准备好回滚方案,以应对可能出现的问题

  • 监控和优化

    在迁移过程中,实时监控系统性能,及时调整策略

UNSIGNED 属性有什么用? #

unsigned 属性用于指定整数类型字段不允许存储负数,从而将可用的存储空间全部用于表示非负数,增加了字段的正数范围

  • 扩展正向范围

    unsigned讲整数类型的范围从负数扩展到正数

  • 节省存储空间

    在不需要负值的情况下,使用unsigned可以更高效地利用存储空间

  • 数据完整性

    确保数据不会出现负值

  • 性能优化

    在某些情况下提高计算性能,因为不用处理负数

MySQL 中 int(11) 的 11 表示什么? #

表示显示宽度,影响数据在某些工具中的显示格式,但不影响存储范围或实际存储大小

  • 显示宽度

    • int(11)中的11指定了显示时的最小字符宽度
    • 仅在使用zerofill时有效,自动填充前导0
  • 存储大小

    int类型的存储大小固定为4字节,与显示宽度无关

  • zerofill

    • 使用zerofill时,数字会被填充前导0以达到指定宽度
  • 实际应用

    显示宽度通常用于格式化输出,不影响数据存储和计算

CHAR 与 VARCHAR 有何区别? #

char是固定长度的字符串,适合存储定长数据,而varchar是可变长度的字符串类型,适合存储长度不固定的数据,varchar会根据实际数据长度分配存储空间。

  • 存储方式
    • char固定长度,空白填充到指定长度
    • varchar可变长度,实际存储长度加上一个字节用于记录长度
  • 长度限制
    • char最大长度为255字节
    • varchar最大长度为65535字节,受限于行大小
  • 性能
    • char适合存储定长数据,访问速度快
    • varchar适合存储变长数据,节省空间

VARCHAR(100) 与 VARCHAR(10) 的区别? #

区别在于它们允许存储定最大字符数不同,前者最多存储100个字符,而后者最多存储10个字符,影响字段的存储容量和数据长度限制

  • 最大长度

    • VARCHAR(100):最多可存储 100 个字符
    • VARCHAR(10):最多可存储 10 个字符
  • 存储空间

    实际存储空间取决于字符串的实际长度,可以加上一个或两个字节用于记录长度

  • 灵活性

    • VARCHAR(100) 提供更大的灵活性,适合存储长度不确定的字符串。
    • VARCHAR(10) 适合存储长度较短且固定的字符串。
  • 性能

    • 较大的VARCHAR可能导致更多的内存分配,但在实际应用中影响较小。

DECIMAL 与 FLOAT/DOUBLE 的区别? #

decimal是用于存储精确小数据的定点数类型,适合财务计算,而float和double用于存储近似小数的浮点数类型,适合科学计算,float和double在存储时可能会有精度损失

  • 精度

    • DECIMAL:精确存储,适合需要高精度的场景,如财务计算。
    • FLOAT/DOUBLE:近似存储,可能有舍入误差。
  • 存储方式

    • DECIMAL:以字符串形式存储,精度固定。
    • FLOAT/DOUBLE:以二进制浮点数存储,精度随数值变化。
  • 性能

    • DECIMAL:计算速度较慢,因需处理精确数值。
    • FLOAT/DOUBLE:计算速度快,适合科学计算。

DATETIME 与 TIMESTAMP 的区别? #

datetime存储绝对时间,与时区无关,而timestamp存储相对时间,受时区影响,timestamp通常用于记录事件发生的时间点

  • 存储范围

    • date time:范围从1000-01-01 00:00:00到9999-12-31 23:59:59。
    • TIMESTAMP:范围从1970-01-01 00:00:01UTC 到2038-01-19 03:14:07UTC。
  • 时区处理

  • DATETIME:不受时区影响,存储的是绝对时间。

  • TIMESTAMP:受时区影响,存储的是相对时间,自动转换为当前时区。

  • 存储大小

    • DATETIME:占用 8 字节。
    • TIMESTAMP:占用 4 字节。
  • 自动更新

    • TIMESTAMP:可以设置为自动更新为当前时间。
    • DATETIME:需要手动更新。

NULL 与 ’’ 有什么区别? #

null表示未知或缺失的值,意味着没有数据,而‘’是一个空字符串,表示已知但为空的值,占用存储空间,可以与其他字符串比较。

Boolean 类型在 MySQL 中如何表示? #

在 MySQL 中,Boolean 类型通常用TINYINT(1)表示,其中0代表false,1代表true。

存储占1字节

为什么不推荐使用 TEXT 和 BLOB? #

不推荐使用text和blob因为它们会导致性能下降,索引和查询效率低,且占用较多存储空间,影响数据库的整体性能

  • 性能问题

    处理大数据时,text和blob会导致查询速度变慢

  • 索引限制

    不能直接对text和blob列创建完整索引,只能索引前缀

  • 存储管理

    大数据可能导致表空间膨胀,影响存储管理

  • 备份和恢复

    备份和恢复需要更多时间和资源

  • 应用场景

    适合存储大文本或二进制数据,但不适合频繁查询和更新

在 MySQL 中存储金额应使用什么数据类型? #

在 MySQL 中存储金额应使用DECIMAL数据类型,因为它能够精确存储小数,避免浮点数类型可能导致的精度损失。

  • 存储精度

    decimal以字符串形式存储,提供精确的定点数表示

  • 避免浮点数误差

    使用float和double可能导致舍入误差,不适合财务计算

  • 灵活性

    可以指定精度和小数位数,如decimal(10,2)表示最多10位数字,其中两位小数

  • 性能

    虽然decimal的计算速度略慢于浮点数,但在财务应用中,精度优先于速度

  • 应用场景

    是用于所有需要精确数值的场景

MySQL 如何存储 IP 地址? #

MySQL 存储 IP 地址可以使用VARCHAR、INT或VARBINARY类型,具体选择取决于存储需求和查询性能。

  • 使用VARCHAR

    • 适合存储 IPv4 和 IPv6 地址,格式直观。
    • 优点:易读易写,支持直接存储点分十进制格式。
    • 缺点:占用空间较大,查询性能较低。
  • 使用INT

    • 适合存储 IPv4 地址,将其转换为整数。
    • 优点:占用空间小(4 字节),查询性能高。
    • 缺点:仅支持 IPv4,不支持 IPv6。
  • 使用VARBINARY

    • 适合存储 IPv6 地址,将其转换为二进制格式。
    • 优点:支持 IPv4 和 IPv6,占用空间合理。
    • 缺点:不易读写,需要转换。

什么是数据库视图? #

数据库视图是基于一个或多个表的查询结果创建的虚拟表,用于简化复杂查询,提高数据安全性和提供数据抽象,基于SQL查询定义。

  • 虚拟表

    视图不存储数据,只存储查询逻辑

  • 数据简化

    通过视图,可以简化复杂查询,提供更易理解的数据表示

  • 安全性

    视图可以限制用户访问特定数据,增强数据安全性

  • 可更新性

    某些视图是可更新的,允许通过视图修改基础表的数据

  • 性能

    视图的性能取决于底层查询的复杂性,可能影响查询速度

什么是数据库游标? #

数据库游标是一种数据库对象,用于逐行处理查询结果集,允许在结果集中进行遍历、检索、更新和删除操作,适合需要逐行处理数据场景。

  • 指针机制

    游标是一个指针,指向查询结果集中的某一行

  • 逐行处理

    允许在结果集中逐行移动,进行逐行处理

  • 使用场景

    适用于需要逐行处理数据的复杂操作,比如批量更新或计算

  • 性能考虑

    游标操作通常比批量操作慢,需要谨慎使用

  • 游标类型

    支持不同类型的游标,如只进游标、可滚动游标等

为什么不建议直接存储大对象(图片 / 音频 / 视频)? #

主流数据库大多采用 行存储结构 页大小默认为16kb 大对象一般为MB级别 远超存储限制 造成行溢出 数据库索引(如 B + 树)主要针对结构化数据(如数字、字符串)设计,对二进制大对象无法建立有效索引,导致: 无法通过内容快速检索(如按图片颜色、视频关键词查询),需全表扫描。 即使使用 UUID 等标识索引,也无法避免大对象本身的 IO 开销。 大对象的读取 / 写入属于随机 IO(需频繁跳转不同数据页或溢出页),而数据库的优势在于顺序 IO(如批量查询结构化数据)。 大对象传输占用大量带宽和数据库连接资源

数据库的三大范式是什么? #

  1. 第一范式(1NF):确保每列的值都是不可分割的原子值。
  2. 第二范式(2NF):在满足第一范式的基础上,确保每个非主属性完全依赖于主键。
  3. 第三范式(3NF):在满足第二范式的基础上,确保每个非主属性不传递依赖于主键。