数据库知识详解及实战:从表结构设计到扛住高并发

📅 发布时间:2026/10/20 22:49:45
数据库知识详解及实战:从表结构设计到扛住高并发
解析完说话人角色后我直接发出文章。数据库知识详解及解析从建表到扛住高并发一趟讲透这些年我带过不少新人也面试过很多简历上写着熟悉 MySQL、熟悉数据库的候选人。真到了项目里能把手头活儿干净利落干完的人其实不多。这里说的数据库知识不是指你会写几条 SELECT、能背出 ACID 四个字母、知道 B 树和哈希索引的区别而是说你拿到一个真实业务场景知道该选哪种类型的数据库、表结构怎么设计、索引往哪里放、事务边界画在哪、慢查询怎么排查、数据出了问题怎么恢复。这一整套东西才是数据库知识真正的样子。这篇文章想把我在实际项目中反复验证过的东西系统地过一遍。标题叫详解及解析实际上我更想把它写成一份数据库学习地图 落地实操指南把从建表到线上运维这条链路拆开揉碎。无论你刚入门还是已经写了几年业务代码想补短板或者正打算转岗做数据库相关的工作这套内容应该都能给你提供一条相对清晰的路径。不用怕我尽量用大白话讲必要时配一些类比和真实案例保证你能看懂、能带走、能直接用。1. 整体认知先行先搞清楚数据库到底是干什么的1.1 数据库的本质有组织的存取与可靠的保证很多人一上来就背 SQL 语法、背索引原理这其实是本末倒置。数据库最核心的价值一句话就能说清帮你把数据有组织地存下来并且在任何时候都能可靠地取出来。这句话拆开有三个关键词。有组织指的是数据不是散乱的一堆文件而是按表、按行、按列、按约束组织起来有结构、有规则。你写一条 SQL数据库知道你是在查用户表里所有状态为正常的人而不是让你自己遍历文件去碰运气。可靠地取出来意味着并发情况下数据不会乱断电情况下数据不会丢出错情况下数据还能回滚。你转账 100 块不能出现扣款成功但对方没收到的情况数据库靠事务机制保证这一点。任何时候强调的是持久化和高可用数据要落盘不能服务一重启数据就没了生产环境还得做主从复制、备份容灾保证硬件故障时数据还在。很多新人容易陷入一个误区觉得数据库就是个存东西的容器跟 Excel 差不多。实际上Excel 是给人看的数据库是给程序并发访问的。Excel 改一个单元格不需要考虑另一个用户同时在改同一个单元格怎么处理数据库必须处理。所以后面讲的所有知识点——事务、锁、隔离级别、索引、范式——本质上都是在回答同一个问题在复杂、并发、易出错的真实环境里怎么让数据依然存得进、取得出、错不了。理解了这一点你会发现数据库知识不是一堆孤立知识点的拼盘而是一套环环相扣的解决方案。1.2 关系型数据库与非关系型数据库的选型逻辑市面上的数据库百花齐放但大类上就两种传统关系型数据库如 MySQL、PostgreSQL和非关系型数据库Redis、MongoDB、Elasticsearch 等。关系型数据库的核心特征是表 行 列 关系数据之间存在明确的逻辑关联。它的优势在于支持事务、支持复杂的关联查询、有成熟的标准 SQL、有完善的约束机制可以保证数据完整性。适合业务核心数据——订单、用户、账户余额这类必须强一致、不允许出错的数据。非关系型数据库则各有各的专长。Redis 靠内存和高效的数据结构提供极低延迟的读写适合做缓存、计数器、分布式锁MongoDB 用文档模型存 JSON 结构开发灵活不用预先定义死表结构适合结构多变、关联查询不复杂的业务Elasticsearch 的核心是倒排索引擅长全文搜索和分析场景。这里最关键的是一个道理没有万能的数据库只有合不合适。我曾经见过一个项目把用户关系链全部放进了 MongoDB结果做好友列表分页 共同好友交集这种查询时性能奇差无比因为文档模型对这类多对多关系支持的效率远不如关系型。反过来也有人非要在 MySQL 里用 JSON 字段硬存半结构化日志数据导致统计查询悲剧——这时候其实更适合用专门的数据分析型数据库或者搜索型数据库。一份粗略的选型清单可以这么定业务场景推荐方向关键理由核心交易、账户、订单、用户关系型MySQL/PostgreSQL强事务、强一致、成熟稳定高并发读多写少、热数据缓存Redis内存级读写速度原子操作丰富结构灵活的产品配置、内容类数据MongoDB文档模型可扩展开发效率高搜索、日志分析、模糊匹配Elasticsearch倒排索引全文检索能力强海量数据实时分析列式存储 分布式计算平台按列聚合扫描效率远高于行存这不是说每类只能选一个实际上绝大多数中型项目是组合拳MySQL 存交易核心数据Redis 缓存热点数据Elasticsearch 提供搜索能力。选型不是单选题而是根据不同的数据特征选不同的存储引擎各司其职。1.3 从零搭建数据库学习路线的建议如果让我给新人一条务实的数据库学习路线大概分四层。第一层把 SQL 写熟。掌握 SELECT、JOIN、GROUP BY、HAVING、子查询、窗口函数能够准确表达业务逻辑。这一层不需要深挖原理但要练到不需要想就能写出正确 SQL。第二层理解表设计。学习范式理论、主键外键设计、字段类型选择、索引基础。能独立把一个业务需求拆分成合理的表结构知道哪些字段该索引、哪些不该。第三层深入机制。学事务隔离级别、锁机制、MVCC、执行计划。理解一条 SQL 在数据库内部是怎么被执行的遇到慢查询能通过 EXPLAIN 分析瓶颈。第四层练运维能力。备份恢复、主从复制、慢查询日志分析、连接数管理、容灾演练。不需要成为专业的 DBA但至少要知道线上出了典型故障你能从哪里下手排查。有人可能会问我以后又不做 DBA为什么要学这么深原因很简单——所有后端系统的绕不开的瓶颈最后几乎都落在数据库上。你写再漂亮的业务逻辑数据库卡住了整个系统就原地瘫痪。艺多不压身数据库这块的底子越厚你拍板技术方案时的底气越足。2. 表结构设计一切痛苦的根源都在这里2.1 三大范式的正确打开方式大学教材里把第一范式、第二范式、第三范式讲得复杂得像数学定理很多人背完就忘。用大白话讲范式理论其实是在做一件事消除数据冗余保证每一条数据只有一个地方需要修改。第一范式说字段不可再分。意思是不要把地址这一个字段里塞省 市 区 详细街道拆成多个字段更利于查询和统计。第二范式说非主键字段必须完全依赖主键不能只依赖主键的一部分。这个主要针对联合主键的情况比如订单明细表用订单ID 商品ID做联合主键那么商品名称只依赖商品ID不依赖订单ID应拆到商品表去。第三范式说非主键字段之间不能有传递依赖比如订单表里有客户ID和客户电话客户电话实际上依赖的是客户ID应该放在客户表里订单表只保留客户ID 就够了。但这里要说一个很多人没领悟到的重点范式是理想状态业务是现实状态。我参与过的项目里几乎没有一个项目是严格满足第三范式的。为什么因为极端范式化会让查询变得极其复杂一次列表展示可能要 JOIN 七八张表性能反而下降。实际工程里我们常做的是适度反范式化。比如订单表里冗余一个用户名字段虽然理论上它应该从用户表 JOIN 过来但为了减少高频查询的 JOIN 次数、降低查询耗时这种冗余完全可以接受。关键是有意识地去冗余并且通过代码逻辑或者后台任务保证冗余数据的一致性而不是稀里糊涂地乱加字段。记住一句话设计表结构没有绝对的对错只有是否适合当前业务阶段。2.2 主键设计自增主键与 UUID 之争主键是表设计的灵魂。选不好主键后续做分库分表、数据迁移、归档时都会想骂人。自增主键的优点占用空间小INT 四字节、BIGINT 八字节、写入有序B 树索引写入时直接在末尾追加页分裂少、对二级索引友好。缺点容易被爬虫猜到数据量、分布式场景下多个节点各自生成 ID 会碰撞、迁移数据时可能出现 ID 冲突。UUID 主键的优点全局唯一可以在应用层生成天生适合分布式场景。缺点占空间大36 个字符、无序随机 UUID 写入时索引页频繁分裂性能下降明显。很多大厂的实践经验是单库时代用自增主键简单高效微服务化后用全局唯一的分布式 ID 方案比如雪花算法改造版既保证唯一性、趋势递增又能满足跨库的全局约束。我在实际项目中最常用的是 BIGINT 自增 独立 ID 生成服务在写入前由应用层分配 ID既保留有序性又避免依赖自增计数器。一个很容易踩的坑是用业务字段做主键。比如用身份证号、手机号、订单号做主键。表面看省事但业务字段一旦发生变更比如手机号支持修改了你就要回改主键牵一发动全身。正确做法是业务字段用唯一索引来约束而主键用无任何业务含义的代理键。2.3 字段类型选错性能从骨子里就输了字段类型选择是很多业务开发忽略的细节也是后来慢查询的幕后黑手之一。整数类型优先选够用的最小范围。状态码用 TINYINT0-255就行别用 INT能省不少存储。用户量一两千万以内INT 够用如果规划上亿用户主键直接上 BIGINT 一劳永逸。有人图省事全部用 BIGINT数据量大了以后浪费非常可观。字符类型定长用 CHAR比如固定 11 位手机号、固定长度的编码变长用 VARCHAR。VARCHAR 长度不要拍脑袋设 5000MySQL 里 VARCHAR 超过一定长度后行格式会变会引发额外的行溢出存储影响性能。日期类型DATETIME 和 TIMESTAMP 容易混。TIMESTAMP 是 4 字节但有 2038 年上限问题DATETIME 是 8 字节范围更广。只有一个建议业务系统统一用 DATETIME且明确时区。否则将来你做全球化部署时间字段的时区换算会让人崩溃。数值金额绝对不要用 FLOAT 和 DOUBLE 存钱二进制浮点数精确表示不了 0.1会累积出各种诡异误差。金额统一用 DECIMAL需要明确精度时用 DECIMAL(10,2) 或者更大范围。这是数据库语义里最不能妥协的底线。3. 索引机制与 SQL 优化提升性能的核心战场3.1 索引为什么能快理解 B 树和哈希索引的本质就是一本书末附录的检索目录。没有索引时数据库要查一条数据只能全表扫描做全表扫描的时间复杂度和数据量成正比。有了索引数据库可以先通过索引快速定位到数据所在的页码IO 次数大幅下降。MySQL InnoDB 引擎默认使用 B 树索引。B 树相比普通的二叉搜索树、B 树的优势在于叶子节点存数据且用双向链表串连非常适合范围查询大于、小于、BETWEEN非叶子节点只存索引键和指针不存数据同样大小的内存页能装更多索引项树的高度更容易维持在 3-4 层。也就是说无论表里有一万条还是一亿条数据走主键索引查询也就几次磁盘 IO这是 B 树最了不起的地方。哈希索引呢哈希索引查找单条记录的效率理论上更高达到 O(1)。但它有两个致命缺陷不支持范围查询、不支持部分前缀匹配。所以哈希索引只适合等值查询场景Redis 的哈希结构、MySQL 的 MEMORY 引擎才可能用它。实操中绝大多数人只需要记住三件事第一主键索引是聚簇索引数据行本身存在叶子节点上第二二级索引普通索引的叶子节点存储的是主键值查询要回表才能拿到整行数据第三覆盖索引解决回表问题查询字段全在索引中时直接返回索引内容即可不需要回表。这三句话能解决你 80% 的索引疑惑。3.2 最左前缀原则复合索引的斯芬克斯之谜复合索引联合索引是最容易写错的地方。比如你建了一个复合索引 (a, b, c)它实际生效的查询组合是aa ba b c它不生效的查询是b 单独查、c 单独查、b c。因为 B 树是按从左到右的顺序构建排序的左边字段相同的情况下右边字段才有排序意义。这个规则叫最左前缀原则。实操中说三个结论。第一个复合索引不是建的越多越好。每多一个索引写入数据时就要多维护一棵 B 树插入、更新、删除的性能都会下降。正确做法是尽量用一到两个复合索引覆盖大部分常用查询。我就见过一张表被新同事建了十几个索引结果写入慢得要命删掉冗余索引后性能立刻恢复。第二个把区分度高的字段放左边把查询频率高的字段放左边把范围查询的字段放右边。区分度低比如性别只有男女两种值的字段放前列会导致选择率太低索引意义不大。第三个必要的时候可以做索引下推优化、覆盖索引设计但不要追求极致的索引套索引那会让报表类查询走进死胡同。3.3 慢 SQL 排查EXPLAIN 到底怎么看线上慢 SQL 是所有后端工程师的宿敌。排查慢 SQL第一步是开启慢查询日志把超过阈值如 1 秒的 SQL 捞出来第二步是用 EXPLAIN 分析执行计划第三步是针对瓶颈改写 SQL 或调整索引。EXPLAIN 输出最关键的几个列我逐个说type访问类型。从好到差依次是 system、const、eq_ref、ref、range、index、ALL。看到 ALL 就是全表扫描要警惕。key实际用到的索引。这里注意有时候 MySQL 优化器会忽略你创建的索引选择全表扫描——通常是优化器估算出来全表扫描更划算。这时你需要用 FORCE INDEX 或者改写 SQL 让优化器改变判断。rows预计扫描的行数。不是精确值但数量级有参考意义。Extra额外信息常见的是 Using filesort排序用了外部文件说明没有合适的索引支持排序、Using temporary用了临时表一般出现在 GROUP BY 和去重里、Using index覆盖索引这是好现象。一个真实案例某项目有个订单列表查询每天高峰期非常慢。EXPLAIN 后发现问题在 WHERE 条件里用了WHERE DATE(create_time) 2024-01-01。DATE() 函数套在索引列上导致索引失效全表扫描。改成WHERE create_time 2024-01-01 00:00:00 AND create_time 2024-01-02 00:00:00后命中索引查询从 3 秒降到 10 毫秒。永远不要对索引列使用函数这是最经典的一条索引失效规则。其他常见的失效点隐式类型转换字符串列比较数字或数字列比较字符串、LIKE 以 % 开头的模糊匹配、OR 条件两边有一边没有索引。索引优化要克制不要陷入一个极端里不能自拔。核心原则是索引是写给查询看的不是写给表看的。给常用的、核心的查询路径建立索引冷门查询宁可让它慢一点也别让热数据的写入背上过多包袱。4. 事务、锁与并发控制数据一致性不能靠运气4.1 ACID事务的四个底线为什么转账不会出现扣了钱但对方没收到因为数据库的事务机制。事务有四个基本属性ACID原子性Atomicity一个事务里的所有操作要么全部成功要么全部失败。不能执行一半就断电要是断电了数据库通过 undo log 回滚到事务开始前的状态。一致性Consistency事务执行前后数据库的完整性约束必须保持一致。比如转账前后A 和 B 账户的总金额不变。一致性更多是业务和数据库共同保证的光靠数据库约束不够业务代码也得守住。隔离性Isolation并发事务之间不能干扰。多个事务同时修改同一行数据必须像排队一样一个接一个或者通过锁机制让它们互不可见地执行。这个后面单独讲。持久性Durability事务提交后即使机器断电数据也不能丢。InnoDB 的做法是 redo log 双写机制先写日志再落盘靠日志来保证崩溃恢复。很多人把 ACID 当成八股背其实它是在描述数据库面对真实世界灾难断电、崩溃、并发时的一整套防御姿态。理解 ACID 最好的方式是想象一次转账在高并发环境中经历了什么把每一步对应的数据库机制对上号自然就记住了。4.2 隔离级别与它们带来的问题隔离性不是免费的。为了更高的并发数据库允许一定程度上的不隔离。SQL 标准定义了四级隔离级别隔离级别脏读不可重复读幻读读未提交READ UNCOMMITTED可能可能可能读已提交READ COMMITTED不会可能可能可重复读REPEATABLE READ不会不会可能串行化SERIALIZABLE不会不会不会脏读读到别的事务还没提交的数据如果对方回滚了你读到就是脏的。不可重复读同一个事务里同一条 SELECT 查两次结果不一样。因为别的事务提交了修改你第二次读到的是新值。幻读同一个事务里同一条范围查询两次结果集行数不一样。别的事务插入了新行导致你多出了本不该出现的行。MySQL InnoDB 默认隔离级别是可重复读REPEATABLE READ比标准要求更严格因为它通过 MVCC Gap Lock间隙锁在 InnoDB 引擎层面也解决了大多数幻读场景。但要注意在像 PostgreSQL 里默认是读已提交READ COMMITTED这是两个主流数据库之间很大的行为差异。在实际项目中我的经验是线上系统 90% 的场景用读已提交或可重复读都行核心交易场景往往需要显式加锁或者提升到串行化来牺牲并发保一致。关键不是背默认值而是要清楚每个级别的代价。4.3 MVCC 与锁并发控制的左右手MVCC多版本并发控制是 InnoDB 提升并发读性能的核心机制。简单说每一行数据会维护多个版本读操作读取的是符合自己事务可见性的某个版本读和写之间不会互相阻塞。这就是为什么你在数据库里能看到快照读和当前读两种读法普通 SELECT 走快照读不加锁性能极高UPDATE、DELETE、SELECT ... FOR UPDATE 走当前读必须拿最新版本加锁执行。锁则是解决写冲突的。从粒度分表锁、行锁。从模式分共享锁读锁、排他锁写锁。InnoDB 默认使用行级锁为了支持行锁索引是前提——如果更新语句没有走索引InnoDB 会退化成锁全表。这个点很重要我曾经排查过一个线上死锁就是 UPDATE 语句的 WHERE 条件没命中索引导致大量行被锁住互相等待最终死锁报错。死锁的排查思路很简单拿到SHOW ENGINE INNODB STATUS里最后一段死锁信息看两个事务各自持有什么锁、等待什么锁。预防手段常见的有几条事务尽量做到短小精悍不要在一个事务里做太多事情批量更新时所有事务按照相同的顺序操作数据行减少锁的粒度尽量走索引必要时降低隔离级别。这里给一个大家都会踩的坑事务里不要做远程调用或者耗时计算。比如事务内调第三方接口等响应这会让事务长时间持有锁线上稍微并发一冲数据库就堵成一锅粥。正确做法是先把事务要写的数据算好事务只做纯粹的数据库操作快速提交。这是无数线上事故的教训。5. 数据库运维与日常操作稳定的系统是防出来的5.1 备份恢复平时不练出事抓瞎数据丢失是每个技术人的噩梦。备份策略的价值不能用有没有备份来衡量要用恢复后能不能正常上线来衡量。我见过不少团队备份倒是每天都有 cron 跑但从来没做过一次恢复演练。真遇到误删数据才发现备份文件不完整、恢复步骤有 bug、恢复速度完全不可接受。所以这里强调一句话备份做得再好没演练过恢复等于没有备份。备份常见有三种方式逻辑备份用 mysqldump 导出 SQL物理备份直接复制数据文件快照备份基于 LVM 或云厂商快照能力。逻辑备份容易拿到、与版本无关但恢复时间长物理备份恢复快但对版本、平台一致性要求高快照速度最快是大型实例的首选。实操上建议组合策略每天凌晨做一次全量备份物理或逻辑每 5 分钟或者每小时做一次 binlog 增量备份这样误删数据后可以恢复到误删前最近的一个时间点损失控制在几分钟以内。同时一定要定期做恢复演练最好是随机挑一个月黑风高的日子让团队在模拟环境里实际跑一遍恢复流程把恢复时间RTO跑出一个真实数字来。5.2 连接数、慢日志、容量监控三件套线上数据库稳定运行靠的是平时监控到位。我最关注的核心指标有这几个活跃连接数/最大连接数比例接近 80% 就要报警因为连接数是数据库的最外层骨架打满之后应用层的报错会瞬间爆表。CPU 使用率长期超过 70%要考虑是慢 SQL 导致的 CPU 飙高还是确实到了容量瓶颈。慢 SQL 数量与耗时分布慢查询日志要开启定期分析 TOP N 慢 SQL。这是发现索引失效、SQL 写烂的最好窗口。磁盘空间使用率磁盘满是一个很低级但频繁发生的故障。一旦写不进去事务提交会失败但不一定会立刻报磁盘满更多时候表现为应用诡异超时。命中率与 IO 延迟缓存命中率低、IO 延迟持续走高说明数据没能全进内存物理读过多。监控报警一定要分级。我现在常用的标准是磁盘使用率超 70% 发警告超 85% 发严重连接数超 70% 发警告慢 SQL 超过阈值且数量激增时发严重。报警不是为了吓人是为了在用户感知到故障之前把事故掐死在萌芽状态。5.3 几个不算复杂的应急动作线上数据库出问题了沉着冷静比任何技巧都重要。以下是几个最实用的应急动作。第一连接数打满先查看SHOW PROCESSLIST找到大量 Sleep 状态的连接多半是应用连接池的配置问题或者程序没释放连接。临时可以批量 kill 掉 Sleep 连接但根治要去看应用侧连接池是否设置合理。第二慢 SQL 把数据库拖垮先停掉慢查询的执行。可以用KILL终止长时间运行的 SQL然后改代码、加索引、或者做限流分批放量。第三误删数据要么用备份 binlog 恢复要么用数据库的闪回工具如针对 MySQL binlog 解析的工具恢复指定时间点。这个操作切忌在线上裸奔先在测试环境验证恢复方案再在线上执行。这些应急动作我希望你永远用不上但一定要在平时演练排障流程。真出事的时候有没有演练过完全是两种反应速度。6. 数据库常见问题与排查技巧实录6.1 慢查询排查实录一次索引失效的完整复盘有一次我给某内部系统排查慢查询页面接口平均耗时 4 秒多用户投诉不断。我先开了慢查询日志抓到两条高频 SQL都是查同一个订单明细表WHERE 里都用了函数处理时间字段和状态字段。用 EXPLAIN 一看type 是 ALL全表扫描。原因有两个一是字段上用了DATE()和CAST()索引全部失效二是 OR 条件一边有索引一边没索引导致优化器放弃索引。修改方案不复杂SQL 改写成范围条件去掉函数OR 改成 UNION ALL 或者拆成两条查询给组合条件建了一个复合索引 (user_id, status, create_time)。改动上线后接口耗时从 4 秒降到 40 毫秒。整个过程的核心收获排查慢 SQL先用 EXPLAIN 看清执行计划再动手改不要盲目加索引。很多新手一遇到慢查询就加索引加完发现没效果就是因为没确认索引是否真的被使用。6.2 死锁问题排查实录同一批数据的更新顺序导致死锁某个定时任务在跑批时频繁报死锁报错位置都是同一个 UPDATE 语句。看了 InnoDB 状态里的死锁日志发现两个事务在抢同一批数据的锁而且加锁顺序不一致互相等对方释放造成循环等待。解决方法是把批处理任务的每条数据处理逻辑改成先按主键排序再处理。加上ORDER BY id之后所有事务对同一组数据都以相同顺序加锁死锁自然消失。这里有个值得记下的经验并发环境下加锁顺序的一致性是避免死锁最有效的手段之一。分布式系统设计里也可以把多个资源的加锁顺序统一写进代码规范。6.3 数据误删应急恢复靠 binlog 兜底有一次同事误删了一张小表的部分数据大约几千行。我在测试环境先验证了恢复思路用全量备份恢复到误删前的时间点再由 binlog 增量重放到误删操作之前然后导出数据导入线上。具体操作是用mysqlbinlog解析 binlog定位到误删语句对应的位置导出它之前的所有变更操作在测试实例上重放拿到恢复数据。整个过程大约 40 分钟线上业务影响最小。事后复盘我们做的对的地方有两点一是备份制度健全全量 binlog 都在二是没有慌乱地在线上瞎操作而是先测试环境演练了一遍。这两个原则值得每一个团队刻在脑子里。7. 写在最后从会写 SQL到懂数据库数据库知识面很广但从实用角度讲最核心的东西无非就是我上面写的内容理解数据库的本质目标会做合理的表设计懂索引原理和优化手段明白事务和锁的机制并具备基础的运维应急能力。这一套组合起来你就能在绝大多数业务系统里把数据库用好。我个人在实际项目中最深的一个感受是数据库出了问题很少是单个原因往往是设计、运维、代码三个层面一起欠了债。表结构没设计好后面所有 SQL 都绕索引没规划好数据量一上来就现原形事务边界没控制好并发一冲就死锁。所以与其等问题爆发了再补救不如在开发阶段就带着数据库思维去写每一行代码和每一条 SQL。最后再分享一个小技巧养成随手看执行计划的习惯。任何一条 SQL写完之后在测试环境跑一下 EXPLAIN确认 type 不是 ALLrows 的预估值和实际数据量在一个数量级再上线。这个习惯几乎不花时间却能帮你挡掉 90% 的线上慢查询事故。愿你的数据库永远稳定愿你永远不需要在深夜被报警电话叫醒解读死锁日志。就这样共勉。