
我这两年带团队招人面试过不少后端候选人发现一个很有意思的现象很多人写接口、调框架很顺手但一聊到数据库就只会说存数据、查数据这两个动作。真要问他数据库怎么保证一致性、为什么加个索引查询就快了、并发一高怎么处理锁竞争就开始含糊其辞。这种状态做小功能够了但一到线上出问题或者业务量上来根本接不住。这个数据库——1系列帖我想从最底层把数据库这件事讲透。不是给你念手册而是站在一个常年跟数据库打交道的从业者角度把数据库到底是什么、为什么这样设计、实际用起来会遇到什么这些事串成一条线。这一篇是整个系列的地基先把全景图和核心机制建立起来后续再逐块深入。不管你是刚入门的学生、写了两年代码的开发还是准备系统梳理数据库知识的转行者这篇都值得花半小时慢慢读。1. 数据库到底在解决什么问题从存数据到管数据1.1 为什么说会用Excel不等于会数据库很多人觉得数据库不就是个高级点的Excel吗这个认知我在刚入行时也有过直到第一次遇到数据错乱才明白其中的差距。Excel处理数据的模式是单机、单用户、手动保存。一个人打开文件改完保存另一个人只能等人家关掉才能再改。就算用共享表格也只能支撑十几个人同时在线而且一旦并发写同一个单元格你根本不知道哪次修改会覆盖掉其他人的数据。数据库从诞生起就是冲着大量人员并发读写同一批数据这个场景去的。它解决的问题可以拆成四个维度持久化进程崩溃、机器断电之后数据还在。这靠的是一套完整的日志和存储机制不是简单写个文件。并发控制几千个人同时读写同一条记录数据库要保证数据最终是一致的不会出现你改了我看不到、我删了你还能查到这种事。查询能力几千万行数据里找出符合条件的几十条不能全表扫一遍。这就要靠索引和执行计划来保证查询在毫秒级完成。运维与恢复数据误删、磁盘损坏时能恢复到最近一致的状态而不是直接撂挑子。这是我理解的数据库的本质一套有纪律、有机制、可恢复的数据管理系统。Excel解决的是一个人整理数据的问题数据库解决的是一个组织共享和处理数据的问题。一个是笔记本一个是银行金库差的不是存储介质是管理机制。1.2 现代数据库的三层结构存储、查询、事务你往数据库里塞数据的时候会不会好奇数据到底怎么落盘的其实现代数据库不管外表多不一样内里都是三层结构在协作存储引擎层负责把数据组织成文件格式写到磁盘还要管理内存里的缓存。MySQL里常说的InnoDB、MyISAM就是这一层的东西。打个比方存储引擎就是图书馆的书架编排规则和档案柜决定书数据怎么物理存放。查询引擎层你写一条SQL它负责解析、优化最后生成一个执行计划告诉计算机怎么拿数据。就像图书馆的检索咨询台你把需求告诉服务人员他快速查目录告诉你书在第几排第几格而不是让你自己绕着书架挨个找。事务管理层负责把一组操作打包成一个整体要么全部成功要么全部不生效。比如银行转账扣款、加钱必须是一个整体不能扣了钱对方没收到。事务日志的写入是数据库崩溃恢复的后悔药和速记本。这三层设计让存数据这件事变得非常精巧。很多人在应用层只关心SQL怎么写从不关心底层机制结果遇到慢查询、数据错乱、死锁时完全无从下手。如果你真想搞懂数据库我建议从这三层结构开始建立心智模型后面看任何问题都会清晰很多。2. 数据库家族图谱关系型、非关系型与专用数据库2.1 关系型数据库为何是绝对主力数据库领域的第一大阵营就是关系型数据库代表有MySQL、PostgreSQL、Oracle。所谓关系就是把数据组织成一张张二维表表里每一行是一条记录每一列是一个字段表与表之间通过外键或逻辑关系关联起来。为什么它能统治数据库界几十年核心是ACID——原子性、一致性、隔离性、持久性。这四个特性保证了数据在复杂并发场景下依然可靠这是交易、账务、订单这类业务绝对不能丢的底线。我在实际项目里凡是涉及资金、订单、用户核心信息的一律先考虑关系型数据库不是因为它新是因为它稳。关系型数据库的查询语言叫SQL结构化查询语言。它的设计非常优雅你只需要告诉数据库我要什么不需要告诉它怎么拿。比如你写一条查询数据库内部会自动决定是走索引扫描、还是走临时表、还是做哈希连接。这种声明式编程让普通工程师也能写出高效的数据操作代码而不需要手动控制每一条数据指针。2.2 非关系型数据库的定位与选型关系型数据库也不是万能的。早期大家发现日志类数据、用户会话、临时缓存、海量浏览记录这种场景用关系型数据库存起来又贵又慢而且表结构一变就要迁移数据非常痛苦。于是非关系型数据库NoSQL四面开花。我用下来最直观的感受是Redis键值存储纯内存操作读写速度可以到百万QPS级别适合做缓存、计数器、分布式锁。MongoDB文档数据库数据以JSON文档形式存储结构灵活字段想加就加适合内容类、社区类产品。Elasticsearch全文检索基于倒排索引专攻模糊搜索比如电商搜索、日志检索。我在选型时有一个非常朴素的原则不要把数据模型本身当成选型唯一依据要看业务模式匹配度。如果你的数据天生就是行列表结构、强事务、强关联查询就用关系型如果你的数据结构嵌套复杂、字段不稳定、读写并发巨大但事务要求不高就上NoSQL。很多系统里两者同时出现并不违和MySQL做核心交易Redis做热点缓存ES做搜索各司其职。2.3 时序、向量与国产数据库特定场景下的答案数据库世界还有一批专科选手它们在特定场景下比通用数据库好用得多。时序数据库如TDengine、InfluxDB专吃物联网设备采集、监控指标这类带时间戳的连续数据流。TDengine我在物联网项目里实测过性能确实猛而且它对C绑定写入的支持很到位用taos_stmt_prepare做预编译批量写入吞吐比逐条insert高一个数量级。如果你是做设备接入、数据采集页面的一定会跟它打交道。向量数据库这两年因AI应用被带火了。它专门存向量嵌入用相似度检索的方式支持大模型问答、推荐、以图搜图。业务里如果要做语义检索数据库层面的核心就是向量索引和余弦距离计算。国产数据库如达梦、人大金仓在信创和政企领域非常常见语法生态兼容主流数据库部署模式也有Docker化方案。实际工作中遇到这类库重点在于快速上手它的兼容模式而不是从零学因为你现有的SQL能力基本可以平移。我做了一张简表方便你对照选型数据库类型典型代表核心优势适合场景关系型MySQL / PostgreSQL / Oracle强事务、标准SQL、生态完善交易、订单、用户中心键值型Redis极速读写、数据结构丰富缓存、会话、实时计数文档型MongoDB灵活模式、横向扩展内容管理、日志、物联网元数据时序型TDengine / InfluxDB时序写入与聚合分析强设备监控、指标采集检索型Elasticsearch全文检索、分词能力强搜索、日志分析向量型Milvus / Qdrant向量近似检索AI语义检索、推荐系统3. 增删改查背后的三座大山事务、索引与并发控制3.1 事务ACID与隔离级别的转账案例新人最容易忽略的就是事务。很多开发写过insert、update、delete但没认真想过如果第三步失败了前两步怎么办。我用一个最经典的转账场景给你讲明白。假设A给B转账1000元代码层面至少要执行两步A账户减1000B账户加1000。如果减完A的账户后程序突然崩溃B没收到钱那1000元就凭空蒸发了。有了事务这两步会被包进一个不可分割的操作任何一步失败数据库会自动回滚让两个账户都恢复到转账前的状态。事务的ACID就是一整套保证规则原子性Atomicity全部成功或全部失败。一致性Consistency事务执行前后数据都符合业务规则约束。隔离性Isolation多个事务并发执行时互不干扰。持久性Durability事务一旦提交数据永久保存不怕断电。这里尤其要注意隔离级别它是并发场景下最容易出问题的地方。默认级别下两个事务同时改同一条数据后提交的会覆盖或等待前一个这还好。但万一你用了较低隔离级别就可能出现读已提交和可重复读之间的脏读、不可重复读、幻读问题。我在项目里一般会先想清楚业务容忍哪种不一致再定隔离级别而不是盲目沿用默认值。3.2 索引结构为什么B树是默认选择索引是解决查询性能的核心手段也是面试必问的话题。索引的本质是以空间换时间——多维护一套有序的数据结构让查询不需要扫全表。数据库里最常用的索引结构是B树。很多人背过B树非叶子节点不存数据这类结论但真要说为什么其实核心就三点树的高度低几千万条数据B树一般也就三四层意味着定位一条数据只需要几次磁盘IO。叶子节点有序连接非常适合范围查询和排序操作比如查订单金额在100到500之间的记录可以顺着叶子链表一路扫。非叶子节点只存索引键每个节点能容纳更多键值进一步压低树高。我给读者的实操建议是为每个查询条件建索引时先想清楚它是等值查询还是范围排序查询。等值查询可以简单用普通索引排序频繁的在索引字段上设计联合索引时要注意最左前缀原则。比如你是按status和create_time一起查、一起排序联合索引就应该让status在前、create_time在后否则索引基本白建。3.3 锁与死锁并发场景下最容易翻车的地方并发一高锁问题和死锁问题就像地雷一样。先说锁的类型共享锁读锁多个事务可以同时加共享锁读同一行数据互不排斥。排他锁写锁一旦事务对某行加了排他锁其他事务既不能写也不能读直到锁释放。悲观锁就是在事务里主动给数据行加锁防止别人改乐观锁则是通过版本号或时间戳在更新时校验谁先提交谁生效后提交的就失败重试。做高并发秒杀这类场景我习惯用乐观锁版本控制减少数据库加锁等待的时间消耗。死锁是并发场景最恶性的事件类似于两个人吃饭互相等对方先放筷子结果谁都不放。数据库里死锁的经典场景是两个事务以不同顺序锁同一批资源。排查死锁我一般分三步查事务状态、看锁等待关系、找被杀掉的事务回放。MySQL里可以直接用SHOW ENGINE INNODB STATUS看到最近死锁的日志里面会标出持有锁和等待锁的语句对照一下就能定位到业务代码里的加锁顺序问题。3.4 建唯一索引撞上重复数据的处理这里想单独说一个我在项目里踩过的高频坑给已有数据的表加唯一索引时提示Duplicate entry。原因很简单你表中历史数据里已经有重复记录了而索引要求唯一数据库自然拒绝创建。热搜词里mysql设置唯一已经有重复数据库说的就是这个场景。处理方式有三种清理重复数据先查哪些字段组合重复保留一条删除其余再建索引。暂时不严格要求唯一时改用普通索引在应用层做唯一校验。归档旧数据把历史数据迁到另一个表让新表结构干净再建索引。实际项目中方案1最常用。记住一个顺序先备份、再查重、然后清理、最后加索引。不要直接跑delete万一删错了没有备份就是事故事故中的事故。4. 一条务实的数据库学习路线从装环境到真正的项目实践4.1 第一阶段本地环境搭建与经典坑这条路线我建议以MySQL为主线因为它是国内用得最多的数据库资料最全面试最常问。装环境其实是个很好的第一课因为很多人在这一步就卡住了。以MySQL 8.4.11 LTS版本为例下载解压后你至少要做三件事初始化数据目录、启动服务、配置root账号。经典配置项里大家最容易忽略的是字符集我强烈建议直接用utf8mb4因为默认utf8在MySQL里不是真正的UTF-8存emoji和生僻字会直接报错。建库时统一指定CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;还有个非常经典的坑Navicat或代码连接MySQL时提示Cant connect to MySQL server或认证插件不支持。两种情况最常见一是端口被防火墙拦了二是MySQL 8默认的认证插件是caching_sha2_password老版本客户端不识别。解决办法是创建用户时显式指定认证插件或者升级客户端驱动。CREATE USER app% IDENTIFIED WITH mysql_native_password BY your_password; GRANT ALL PRIVILEGES ON mydb.* TO app%; FLUSH PRIVILEGES;4.2 第二阶段SQL基本功与表设计规范环境跑通之后花两周时间把SQL打扎实。学习重点不用贪多把这三块练到条件反射增删改查CRUD单表查询、多表join、聚合分组、子查询、分页。DDL结构定义建表、改字段、加索引、删表。特别注意ALTER TABLE在大表上是高危操作字段变更可能长时间锁表。事务控制BEGIN、COMMIT、ROLLBACK、隔离级别、锁等待。表设计规范我建议你心中牢记五个词主键明确、字段有界、注释完整、范式合理、冗余可控。很多人一上来就搞大宽表或者过度拆分其实都不对。拿订单表举例订单主表存订单号、用户ID、总金额订单明细表存商品ID、数量、单价两张表用订单号关联这就是典型的第三范式。但如果你追求性能把商品名称冗余到订单表里避免每次查询都去关联商品表又属于合理反范式。设计没有绝对标准核心是业务跑的顺、数据不容易错。4.3 第三阶段性能优化、连接池与备份同步基础SQL熟练后要开始接触生产环境才有的那些东西。连接池应用连接数据库不能每次请求都新建连接那太慢了。连接池如HikariCP、Druid维护一批现成连接复用。配置时有几个核心参数initialSize初始连接数、maxActive最大活动连接数、maxWait获取连接的最长等待时间、minIdle最小空闲连接数。调参思路是流量少就小池子省资源流量大就大池子扛并发但maxActive千万别设成无限大否则数据库会被连接数打爆。慢查询排查开启慢查询日志分析执行计划。EXPLAIN输出的type字段很关键const、eq_ref是走主键/唯一索引的好现象ALL就是全表扫描基本意味着索引没建对。我查慢SQL的第一步就是看这里。备份与同步生产环境数据必须有多重保障。全量备份如mysqldump适合低频灾难恢复binlog二进制日志同步适合增量复制和主从同步。热搜词里提到的数据库同步软件核心原理就是解析binlog再重放到目标库。做主从时要注意复制延迟从库查询在高峰期可能读到旧数据业务设计要容忍这种情况。4.4 用数据库课程设计锻炼实战能力很多在校读者会搜数据库课程设计我特别建议抓住这个机会做一个小而全的项目这比刷题有用得多。比如做一个简洁版的图书管理系统要求自己设计读者表、图书表、借阅记录表外键怎么设索引放哪并实现借书、还书、查询逾期、统计热度这些功能。做完之后问自己五个问题如果两个用户同时借同一本书怎么用一个事务保证库存正确扣减逾期查询的SQL走索引了吗如果数据量涨到100万条现在的设计还扛得住吗万一电脑断电数据怎么恢复整个系统怎么部署让别人也能访问这五个问题覆盖了事务、索引、扩展性、备份、部署每一个都能对应到博客里继续深挖。真实项目经历说出去比简历上写熟悉MySQL有力得多。5. 我在实战中反复遇到的三类问题与排查思路5.1 连接不上数据库从网络、认证、权限三个维度排查连接不上是最常见也最让人上火的报错。我遇到过的案例里三分之二是小问题但不知道排查顺序就会绕圈。我自己固定了一套排查顺序网络可达性先用ping和telnet确认主机和端口通不通。很多云服务器默认安全组没放行3306端口或者本机防火墙没开端口这种问题应用层面怎么查都查不出结果。认证失败看报错是Access denied for user还是Communications link failure。Access denied基本就是用户名、密码、host限制的问题link failure才是网络层面的连接中断。权限范围MySQL的权限是针对库和host维度的比如你只授权了applocalhost那从远程连必然失败。解决办法就是一步一步扩大范围验证用最小化命令测试比如从应用服务器上用mysql客户端手工连一下数据库先排除代码层面的干扰。5.2 突然变慢慢查询日志与执行计划分析系统原本好好的突然某天接口响应时间变成10秒这种问题最容易让开发慌。我的固定动作是先看数据库的慢查询日志找到耗时最长的SQL。然后EXPLAIN看执行计划重点看索引利用情况和扫描行数。一个我印象很深的例子上线初期数据量只有几万某条查询用了LIKE %关键字%当时完全没感觉。半年后数据量到五百万这条SQL一秒都跑不完。原因就是%关键字%这种前置通配符的模糊查询没法走索引数据库只能全表扫。解决方案是在应用层改成记录时预计算关键词或者单独引入Elasticsearch做搜索让主库专心处理核心事务。不要等到慢才发现问题上线前用大数据量压一遍哪怕单机也值得。5.3 表结构变更的安全姿势热搜词里有个mysql数据库修改结构这看着是个基础动作实际操作里充满了险情。对生产环境的大表直接执行ALTER TABLE可能锁住整个表的读写影响所有线上功能。如果你是在大线上库加字段推荐的做法是评估表行数和变更类型估算锁表时间窗口。低峰期执行或者用数据迁移工具在不锁表的情况下异步变更表结构。先加索引再加应用代码或者反过来保证新旧代码任何一刻都是兼容的。变更前完整备份宁可备了不用也不能用的时候没有。我在处理这类问题时还会加一个多环境验证步骤先在测试库跑同样的变更观察影响和耗时再上生产。数据库运维的原则永远是稳字当头快是给业务看的稳是给数据保底的。数据库这门学问越深入越觉得自己懂得少。搭建一个库可能只需要十分钟但理解它背后的每一层设计可能要花上数年。这篇数据库——1先把全景梳理出来后续我会接着聊索引原理的具体实现、事务隔离级别的实验对比、以及慢查询实战排查的完整链路都是实际跑过的场景到时候一件一件分享出来。