8.1 ACE 引擎与存储内幕


8.1 ACE 引擎与存储内幕

本节摘要:.accdb 文件内部是一座按"页"组织的仓库:系统表管家底、数据页存货、VBA 工程与窗体定义塞在专门的储藏间;多人共写时靠锁文件 .laccdb 报信、靠事务日志兜底。看懂这套构造,第 3 章的性能经验、第 7 章的运维动作就都有了原理层的解释。

前七章我们一直把 Access 当黑盒用:按钮按下去,数就存进去了。这个盒子关得越久,排障时越心虚——就像开车的人听不见发动机异响。这一节把盖子掀开一角。你不需要成为引擎开发者,但知道了数据放在哪、锁加在哪、断电时靠什么恢复,你在生产现场的每一次判断都会更有底气。

从 Jet 到 ACE:换过心脏,没换骨架

Access 的第一代引擎叫 Jet,1992 年随 Access 1.0 出生,负责解析 SQL、维护索引、执行查询。2007 年,伴随新的 .accdb 格式,微软发布了重写后的引擎并更名 ACE(Access Database Engine)。这次换代加了两样当时很超前的东西——附件字段和多值字段,增强了加密,还内置了 SharePoint 列表通道;但对旧格式 .mdb 的读写能力被完整保留。

维度 Jet 4.0(.mdb 时代) ACE(.accdb 时代)
出场年代 Access 2000–2003 Access 2007 至今
特色字段 备注、OLE 对象 加附件字段、多值字段、计算字段
安全模型 可选的工作组文件 去掉工作组,改用加密与信任中心
兼容关系 ACE 向下可读写 新特性只在 .accdb 生效

给接单人的启示只有一条:新项目一律用 .accdb,除非客户还留着必须在老版本上跑的古董环境。两种格式混用的团队,迟早会在"附件字段怎么读不出来"这种问题上耗掉一个下午——答案很简单,.mdb 里根本没有那个存储结构。

掀开盖子:一个 .accdb 里住了谁

.accdb 采用复合文档的二进制格式组织内容,概念上可以理解成"文件里有个小文件系统"。它至少住着四类住户:

  • 系统表:一批以 MSys 开头的隐藏表,MSysObjects 记着全部对象的清单——每张表、每个查询、每个窗体的定义都在这里挂号;MSysRelationships 存关系约束。在前端设置里勾选"显示隐藏对象与系统对象"后,你能像查普通表一样查它们;
  • 用户数据表:真正存放业务记录的区域,数据按数千字节一页切分存放,一行记录物理上紧挨着它的邻居们;
  • VBA 工程:所有模块编译后的代码流;
  • 窗体报表定义:控件的属性、位置、事件绑定,以序列化形式打包存放。

想亲眼确认的话,启用系统对象显示后跑一句:

-- 数一数这个库里到底注册了多少对象、都是什么类型 SELECT MSysObjects.Type, Count(*) AS cnt FROM MSysObjects WHERE Left(Name, 4) <> "MSys" And Left(Name, 1) <> "~" GROUP BY MSysObjects.Type;

类型数字对应表、查询、窗体等不同种类。华彩后端跑出来是五张表九个查询——清单和导航窗格对得上号,说明没有来历不明的孤儿对象。这类"体检式"的小技巧在接手别人做的库时特别好用。

图:.accdb 复合文件的内部布局

图:.accdb 复合文件的内部布局

一条 UPDATE 的完整旅程

在 DAO 里写下一条更新时,引擎内部发生的事比肉眼可见的多:修改先进缓存,同时把"打算做什么"写进流水记录;事务提交时才正式改写数据页;如果提交前程序崩了或断了电,下次打开库时引擎按流水把未完成的事务撤干净,保证库不会停在半新半旧的中间态。

' 转账式操作:两步要么都成功,要么都不算 Dim ws As DAO.Workspace, db As DAO.Database Set ws = DBEngine(0) Set db = ws.Databases(0) On Error GoTo CleanFail ws.BeginTrans db.Execute "UPDATE tblStock SET Qty = Qty - 2 WHERE ItemID = 105", dbFailOnError db.Execute "INSERT INTO tblShipment (ItemID, Qty) VALUES (105, 2)", dbFailOnError ws.CommitTrans Exit Sub CleanFail: ws.Rollback ' 两步一起作废,库存不会凭空少两件 MsgBox "出库失败已回滚:" & Err.Description, vbExclamation End Sub

注意它的边界:事务只能罩住这一个数据库文件里的表。若你的前端链接了 SQL Server 或另一个 accdb,那些远端表的修改不在这张保护伞里,需要自己在业务层设计补偿逻辑。这是很多"迁移了一半"的系统踩过的暗坑。

锁文件与一次真实的排障现场

理解内幕最好的方式是跟着事故走一遍。华彩上线第三个月的一个早晨,仓管电话说打不开库,报错大意是"文件已被占用"。赶到现场的过程:

  1. 到共享目录一看,文件夹里赫然躺着华彩后端.laccdb——但所有人都说没开过库。这文件是锁信息文件,正常情况下最后一个用户关闭后自动消失,赖着不走说明有人异常退出(当晚确实有一台笔记本低电量强关)。
  2. 让在场所有人关闭 Access 后残留依旧,说明持有者已断线,属于死锁残骸。
  3. 请所有用户退出后,删除这个 .laccdb 残留文件,一切恢复正常。删它是安全的吗?只有先确认无人使用才是——里面只是锁登记信息,不是业务数据,删错时机才会真伤人。

顺手解决了第二个隐患:他们此前偶尔出现两人同时改同一段数据时报冲突。检查发现库是以禁用记录级锁定的方式打开的,引擎退化成按整页加锁——同一页上物理相邻的两条无关记录会互相连坐。在选项里勾回"使用记录级锁定打开数据库",冲突频率立刻降下来。页面虽比行大,但相邻记录之间的误伤面小得多;这也是为什么第 2 章建议别把一张大宽表什么都往里塞:行越宽,一页装的行越少,锁的单位反而越粗。

内幕改变哪些日常决策

  • 2GB 上限的本质不是数字游戏,而是单文件架构的天花板——归档历史数据(第 7 章)之所以有效,就是给"页仓库"腾房子;
  • 备份必须独占:热备份抓到的是半提交状态,日志恢复不了旁观的复制文件,所以例行备份安排在无人时段用独占方式或压缩修复替代品;
  • 压缩修复为什么灵:碎片化的页重新排列整齐,空气房间的隔墙被拆掉——它是针对存储构造的直接保养,不是玄学;
  • 链接外部库等于交出事务权:混合架构性能好扩展好,代价是一致性责任转移给了业务代码,这一点要在方案阶段讲清楚。

还有一条隐秘的连锁反应值得一提:因为数据页物理相邻存储,频繁删除再插入会让一页里塞满不同批次的碎片记录——这就是为什么高改动的表比只追加的表更需要定期压缩修复。理解了页的存在,连保养节奏都有了物理解释。

收个尾

本节给出的核心图景三句话:accdb 是分页的复合仓库;多用户的秩序由锁文件与页面锁维系;崩溃一致性靠流水记录兜底。带着这三句话回看前面任何一章的操作建议,你会发现自己已经从"照做"升级到了"知道为什么这么做"。下一节往外看:别的软件怎么进来取数,我们又如何伸手出去。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U