这是一个老问题,但没有人真正回答过。以下是问题:
- MDB 中如何处理并发读取?
- MDB 中如何处理并发更新/删除?
- 是否存在锁的概念以及如何在 .NET 应用程序中利用它?
- 将 MDB 文件放在网络共享上是好主意还是坏主意?
前两个问题基本上可以用一个解释来回答。这里有一个关键警告:我在这里给出的答案特定于 Jet MDB(及其变体),并不完全适用于从 A2007 开始引入的新文件格式,即 ACCDB 格式。我还没有完全探讨从 ACE 中删除 Jet ULS 的影响,下面的一些评论可能假设 Jet ULS 在幕后。不过,对于很多事情,您可以用“LACCDB 文件”替换“LDB 文件”,结果是相同的。
1-2) 并发读取/更新/删除
Jet 数据库引擎通常被称为“文件服务器”数据库,因为没有服务器端恶魔管理服务器上数据文件的 I/O。这意味着所有使用 Jet MDB 的客户端都直接读取该文件。
当然,如果没有内置某种机制来处理对文件的并发访问,这将导致灾难。
Jet 使用记录锁定文件,如果您的 MDB 是“MyFile.MDB”,则记录锁定文件将位于同一文件夹中并称为“MyFile.LDB”。 LDB 文件记录哪些 Jet ULS 用户打开了 MDB 文件、用户从哪个工作站连接以及协商并发问题所需的所有信息。
现在,对于那些刚刚接触客户端/服务器数据库引擎的人来说,这可能看起来原始且危险,但在开发 Jet 数据库引擎时,它的目的是用作小型工作组的桌面数据库引擎,并且正在与 xBase 和 Paradox 等其他桌面数据库引擎竞争,这两个引擎都使用类似的锁定文件来管理来自多个客户端的数据文件的并发使用。
在 Jet 数据库文件中,锁要么应用于数据页(在 Jet 4 中增加到 4K,而在 Jet 3.x 及之前版本中为 2K),要么应用于记录级别(如果数据表最初创建为使用记录级锁定。在 Jet 4 的早期,许多人发现记录级锁定非常慢,特别是在使用悲观锁定时,因此许多 Access 开发人员除了页面级锁定之外从未使用过任何其他锁定(@David Fenton 举手!)。
事实上,使用乐观锁定时,您可以避免悲观锁定带来的大多数并发问题。
一些注意事项:
-
从 DAO 中,记录级锁定不可用,您只能获得页级锁定。
-
从 DAO 中,有许多用于控制乐观/悲观锁定的选项,特别是 OpenRecordset 方法的 LockEdits 参数,但它也会与 OpenRecordset Options 参数中指定的某些设置交互(例如,选项 dbReadOnly 不能与锁定编辑)。除了锁定之外,还有一致/不一致更新的选项,所有这些都可以与事务交互(例如,未提交事务中的更改不会对其他用户可见,因此不会与他们冲突,但它可以在涉及的表上放置只读锁)。
从 ADO/OLEDB 中,这些 Jet 并发控制结构将映射到 ADO/OLEDB 中的相关函数和参数。由于我仅从 Access 使用 Jet,因此仅通过 DAO 与其交互,因此我无法建议您如何使用 ADO/OLEDB 控制这些,但重点是 Jet 数据库引擎在访问它时提供对记录锁定的控制以编程方式(而不是通过 Access UI)——只是更复杂。
3) 锁和.NET
我不能在这里提供任何建议,除了您可能会使用 OLEDB 作为数据接口之外,但关键是锁定功能/控制位于数据库引擎本身中,因此可能有一种方法可以通过OLEDB。不过,这可能不太漂亮,因为在我看来,OLEDB 是围绕客户端/服务器架构设计的,而 Jet 基于文件的锁定可能无法以优雅的方式映射到这一点。
4) 网络共享上的 MDB
Jet 对任何网络连接中最轻微的故障都非常敏感。因此,低带宽网络可能会增加通过慢速连接打开的 Jet 数据库的漏洞。
这是因为数据库文件的主要块必须通过线路拉至本地计算机的 RAM 进行处理。现在,许多人错误地声称整个 MDB 文件被拉过网络,或者整个表被拉过网络。这不是真的。相反,Jet 首先请求索引(并且只请求完成查询所需的内容),然后根据该结果准确确定需要哪些数据页,然后仅提取这些页。这是令人惊讶的高效和快速。
此外,Jet 还进行了一些非常智能的缓存,这可能意味着第一个数据请求可能需要一段时间,但由于缓存,对相同数据的后续请求几乎立即发生。
现在,如果您没有很好地为表建立索引,您最终可能会拉出整个表并进行全表扫描。同样,如果您基于不属于 Jet SQL 方言的客户端函数的标准,您最终可能会拉出完整的表(例如,按 Replace(MyField, "A", "Z") 进行排序可能会导致全表扫描)。但对于客户端/服务器架构来说,这种事情也会效率低下,因此正确索引事物并小心使用 UDF 或不兼容 Jet 的函数只是常识性的模式设计。一般来说,对客户端/服务器有效的同样的事情对 Jet 也将有效(主要的区别是,使用 Jet,您最好使用持久连接,以避免重新创建 LDB 文件的开销,这是显着的)。
另一件要避免的事情是尝试通过 WiFi 连接使用 Jet 数据。我们都知道 WiFi 有多不可靠,尝试通过 WiFi 连接处理 Jet 数据只会带来麻烦。
底线:
如果您使用 MDB 作为数据存储来提供来自 Web 服务器的数据,则应将数据尽可能靠近 Web 服务器的 RAM。这意味着在可能的情况下,在连接到物理 Web 服务器的磁盘卷上。如果无法做到这一点,您需要快速、可靠的 LAN 连接。如今,数据中心中的 GB LAN 非常常见,我非常乐意通过这种连接使用 Jet 数据。
对于共享使用,例如,运行共享单个 Jet MDB 作为数据存储的 VB.NET 桌面应用程序的多个客户端工作站,将数据文件放在可靠的文件服务器上是非常安全的。如果可能,最好将 Jet MDB 文件放在不具有多种用途的计算机上(例如,运行 Exchange、SQL Server 并充当文件服务器和打印服务器的域控制器可能不是最佳位置) 。像 Exchange 这样的应用程序可能会严重干扰文件服务器功能,我通常建议永远不要将 MDB 文件放在像 Exchange 服务器一样进行多任务处理的服务器上,除非它的容量极低。
其他考虑因素:
-
切勿尝试在复制的文件系统上分发 MDB,除非所有用户都使用相同的副本。也就是说,如果您有两台服务器在它们之间复制文件,则无需考虑从两台服务器编辑 MDB 文件。这几乎会立即损坏文件。
-
我建议不要将任何 MDB 存储在除通过本机 Microsoft SMB 网络提供服务的本机 Windows 文件系统之外的任何其他设备上。这意味着没有 Novell、没有 Linux、没有 SAMBA。造成这种情况的关键原因是,Jet 显然对 Windows 文件系统中的某些低级锁定功能进行了低级挂钩,而这些功能并未 100% 复制到其他文件系统上。现在,我对此非常保守,许多有能力的 Access 开发人员都报告了正确配置的 Novell 文件服务器的出色结果(通常需要进行一些记录锁定调整,尽管现在可能不太相关 - 我不知道)甚至不知道 Novell 是否还存在!),以及运行 SAMBA 的基于 Linux 的文件服务器的惊人性能。我对此持谨慎态度,并建议任何客户端不要使用它(这也包括各种 SAN 设备,因为其中不是很多都是基于 Windows 的)。
-
出于同样的原因,我永远不会在任何虚拟化文件系统上运行它们。不过,我有一位客户,她已经在 Mac Air 上的 Parallels 下运行她的单用户 Access 应用程序好几年了,没有出现任何问题。但它是单用户的,因此锁定问题相对较小。
我不知道这是否能回答你的问题。这一切都是基于我作为 Access 开发人员 13 年来定期使用 Jet 以及对有关 Jet 的唯一出版书籍《Jet 数据库引擎程序员指南》(仅适用于 Jet 3.5)的研究。我没有提供任何真实的引文,但如果有人需要我所说的任何细节,我会尽可能进行研究。