1. 为什么非要搞出个“数据库”不可:从Excel地狱说起
先讲个我早年经历过的真实场景。有个做电商运营的朋友,店铺从每天几十单做到上千单,一直用Excel管订单。结果某天下午,两个客服同时改一份订单表:一个在改收货地址,另一个在标记已发货,保存的时候后保存的人把先保存的覆盖了。客户投诉说地址改错了,货发到老地址去了。朋友折腾到半夜,最后只能在群里发了个公告:以后谁改完表格,都要在文件名后面加上自己的名字和时间。
这种办法当然不靠谱。第二天有人忘了加后缀,直接覆盖了全表。当时他来找我,问我怎么办。我跟他说:你的问题不在于表格用得好不好,而在于你已经到了该用数据库的时候了。数据库本质上解决的就是Excel这种单文件方案撑不住的那几件事。
第一个是并发控制。当多个人同时读、同时写同一份数据时,数据库会用锁机制、MVCC(多版本并发控制)来保证不丢数据。Excel的“后保存者覆盖先保存者”在数据库术语里叫“丢失更新”,数据库有成熟的机制来避免。
第二个是数据一致性。比如下单扣库存这个动作,订单要加一条,库存要减一条,两个操作必须都成功或者都失败。如果在Excel里手动操作,很可能出现订单建了但库存没扣的情况。数据库事务(Transaction)就干这个事。
第三个是崩溃恢复。Excel最怕的是什么?文件损坏打不开。数据库有自己的日志系统,哪怕机器断电崩溃,重启后也能通过日志把数据恢复到崩溃前的一致状态。
第四个是查询能力。几千行数据的时候Excel还能筛筛选选,到几十万行的时候,Excel基本就卡死了。数据库用索引、优化器来查询,百万行级别也是毫秒级响应。
第五个是安全与权限控制。数据库可以精确控制某个用户只能看某些表的某些列,Excel从机制上做不到这种细粒度。
所以,数据库系统概念这门课,拆开来看,核心就是在解决这几个问题:怎么存、怎么查、怎么改、怎么保证不崩、怎么保证多个人同时用还不出乱子。明白了这一点,后续所有概念都会围绕这几条主线展开。
注意:搜索引擎里有大量关于“数据库死锁”“数据库并发锁”“数据库增删改查”的搜索热词,本质都是这几个核心问题的延伸。先把问题的维度建立起来,后面学任何具体技术点都不容易迷路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库系统到底由什么组成:存储、引擎与那些看不见的日志
咱们平时说“装个MySQL”“装个Oracle”,感觉是个一体的软件。但实际上数据库系统是分层的,这一层一层拆开来看,概念就清楚多了。
2.1 存储层:数据和元数据怎么落盘
存储层解决的核心问题是:数据到底怎么放在磁盘上。传统数据库最朴素的做法是文件组织,表数据按行存储在一个个数据页(Page)里,一个数据页通常是4KB到16KB。页是数据库和磁盘打交道的最小单位,读一页和读一个字段的开销差不了太多,所以数据库宁愿一次多读点,也要尽量减少IO次数。
存储层里还得区分数据(Data)和元数据(Metadata)。数据是咱们存的业务内容,元数据是描述数据的数据——比如这个表有哪些字段、字段是什么类型、谁建的表、索引怎么组织的。MySQL的information_schema库就是干这个的,里面存的全是元数据。很多做数据库运维的人天天跟元数据打交道,就是这个道理。
2.2 存储引擎:决定了一个数据库的“脾气”
存储引擎是数据库中最核心的可插拔部件,它决定了数据怎么存、怎么取、支不支持事务、支不支持行锁还是表锁。MySQL最常用的InnoDB引擎支持事务、支持行级锁、支持崩溃恢复;而老旧的MyISAM引擎速度在某些场景下不错,但不支持事务,一崩就可能丢表。
打个比方:数据库是一个大餐厅,存储引擎是后厨。客人(应用)只需要跟服务员点菜(发SQL),完全不关心后厨用的是燃气灶还是电磁炉。换个后厨设备,不影响营业,只影响菜品的稳定性和出餐速度。
这个比喻可以延伸到面试里最常问的那类问题:“为什么事务要放在引擎层而不是Server层?”因为事务本质上是一种存储管理策略,它牵扯到日志的写入策略、锁的管理、数据页的回滚,这些是存储引擎的职责边界,不是语法解析器的职责边界。
2.3 日志系统:数据库崩溃后靠什么活过来
数据库里最容易被小白忽视但最值得花时间理解的部分,就是日志系统。关系数据库的日志主要分两类:重做日志(Redo Log) 和撤销日志(Undo Log)。
Redo Log解决“崩溃后已提交的数据不能丢”的问题。你执行一个UPDATE语句,数据库不会直接去改磁盘上的数据文件,而是先把这个修改记入内存缓冲区,并把修改操作顺序写入Redo Log。日志一旦写完,事务就算提交成功了。之后哪怕机器立刻断电,磁盘上的数据文件还是旧值,也没关系——数据库重启后,会根据Redo Log把已经提交的事务重新做一遍,让数据文件恢复到最新状态。
Undo Log解决的是“事务回滚时怎么恢复旧值”的问题。它记录了修改之前的旧值,执行ROLLBACK时,通过Undo Log把数据恢复到修改前。另一个重要用途是配合实现MVCC多版本读,让你在一个事务里反复读同一行数据时,始终看到的是事务开始时的那个版本。这也是为什么高并发系统里“读写不互斥”——读的其实是旧版本,不必等写操作完成。
经验之谈:很多新手会把Redo Log和Binlog搞混。Redo Log是引擎层的物理日志,记录的是“数据页怎么改”;Binlog是Server层的逻辑日志,记录的是“这个SQL执行了什么”。两者用途不同,主从复制用的是Binlog,崩溃恢复用的是Redo Log。面试时能把这个区分说清楚,已经可以秒掉一大半候选人了。
