Access数据库凭借其零部署成本和熟悉的图形化操作,成为不少中小型系统的首选。但当数据规模来到百万级、且需要多人在局域网内长期高频使用时,许多起初未暴露的问题会逐渐显现。本文基于一个持续运行四年、后端mdb文件达六百兆的业务系统实例,剖析长期使用中的典型经验与结构性缺点。

一、文件型架构带来的并发硬伤
Access本质上是一个基于文件共享的数据库引擎,它不像MySQL或SQL Server那样有独立的服务进程在内存中调度锁与事务。每一个客户端直接通过SMB协议读写同一个mdb或accdb文件,依靠同目录下的ldb锁文件来协调。当并发用户超过十人且存在持续写入时,锁冲突的概率显著上升。
我们曾遇到这样的现象:某用户执行大批量导入时,其他用户的更新操作会随机抛出“无法更新,目前被其他用户锁定”的错误,即便从任务管理器看并没有人正在操作。根本原因在于Access的页级锁在文件型共享下不够健壮,网络抖动就可能让锁状态残留。以下VBA片段展示了如何用错误处理来规避部分临时锁异常:
On Error GoTo RetryWrite
Dim db As DAO.Database
Set db = OpenDatabase("\\192.168.0.1\share\app.accdb")
db.Execute "INSERT INTO LogTable (msg) VALUES ('test')"
Exit Sub
RetryWrite:
If Err.Number = 3260 Then ' 3260为锁冲突
Application.Wait Now + TimeValue("00:00:02")
Resume
End If
这种重试机制只是缓解,并不能根治。随着数据量增长,锁文件自身的维护开销也变大,最终我们不得不将高频写入模块迁移到独立服务,只保留Access做报表查询。
二、单文件膨胀与性能退化
中型Access数据库最直观的缺点是文件体积失控。删除记录并不会立即释放空间,日志和索引碎片会留在文件内。系统运行两年后,即便有效数据只有三百兆,mdb文件也已虚胖到六百兆。定期“压缩和修复”成了运维必修课,但该操作要求所有用户退出,且六百兆文件在机械盘上往往要花二十分钟。
更为隐蔽的是查询计划的退化。Access的查询优化器依赖于统计信息,而文件型库不会自动更新这些统计。以下SQL在初期响应飞快,半年后却全表扫描:
SELECT * FROM Orders WHERE OrderDate > #2023-01-01# AND Status = 'SHIPPED';
我们后来发现,必须在业务低峰期手动执行ANALYZE类操作(通过CompactRepair间接触发)来重建索引统计。下表对比了修复前后的关键指标:
| 阶段 | 文件大小 | 典型查询耗时 | 日维护窗口 |
|---|---|---|---|
| 运行6个月 | 320MB | 120ms | 无 |
| 运行24个月 | 610MB | 2400ms | 每周2小时 |
| 压缩修复后 | 330MB | 140ms | 无 |
可以看出,若不干预,性能会以肉眼可见的速度劣化。对于需要全天候可用的系统,这种停机维护本身就是一种严重缺点。
三、前后端拆分的经验与局限
官方推荐的做法是将表放在一个后端文件,窗体和查询放在各用户的前端文件,以此减少网络传输锁文件的概率。我们确实采用了这种架构,前端accde分发给二十个工位,后端mdb置于192.168.0.1的共享目录。
拆分后,前端崩溃不会影响他人,补丁升级也只需替换前端。但后端依然是单点文件,所有写压力集中于一处。当某部门开始用Access做跨表聚合分析时,后端CPU虽空闲,磁盘IO却成为瓶颈。以下Python示例说明如何用pyodbc绕过前端,直接读后端做轻量抽取,减轻Access自身查询负担:
import pyodbc
conn = pyodbc.connect(
r"Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
r"DBQ=\\192.168.0.1\share\backend.accdb;"
)
cur = conn.cursor()
cur.execute("SELECT dept, COUNT(*) FROM Orders GROUP BY dept")
for row in cur.fetchall():
print(row)
即便如此,后端文件若损坏,整个公司业务即停摆。我们因此额外写了定时复制后端到ipipp.com备份服务器的脚本,但还原时仍需停写,体验远不如数据库主从复制。
四、迁移成本与选型建议
当数据表行数超过五百万或并发写超过十五人,继续修补Access不如迁移。我们最终将核心交易表迁至PostgreSQL,仅保留Access作为老员工熟悉的录入前端,通过ODBC链接表指向新库。迁移脚本需处理Access特有的是/否类型、 ole字段等,工作量被低估过。
判断是否需要换库的几个信号:压缩修复频率变高、ldb文件常驻不消失、多用户频繁报3197错误、单表超过百万行且联合查询超三秒。出现两条以上,就该认真评估迁移。Access并非不好,只是在中型且长期运行的场景下,其文件型基因决定了缺点会随时间放大。
总结来看,长期使用中型Access数据库积累的经验是:务必前后端拆分、定时压缩、监控文件增长;而其缺点集中在并发弱、易损坏、维护停机长。理解这些,才能在合适阶段做出合理的技术决策。