为什么 QuickBooks 不能存储在 Google Drive 上供多用户使用
在深入探讨具体细节之前,重要的是要理解这是一个普遍性的概念,我们实际上可以将其归结为“为什么客户端/服务器或共享数据库文件类应用程序在访问未被限制为单一用户的情况下,不能存储在同步存储(例如 Google Drive、DropBox、NextCloud 等)上?”QuickBooks 使用一种共享数据库文件机制,这种机制在 20 世纪 80 年代风格的应用程序中很常见,即单个文件或一组文件通过文件共享机制进行共享,各个应用程序副本分别访问该文件以对其进行修改。Google Drive 是一种同步存储机制:意味着它将数据从一个位置复制到另一个位置。同时处理同一文件的人员可能会、而且经常会覆盖彼此的更改,其预期是这些更改稍后将被手动协调、被忽略,或者用户将受到约束以避免他们同时进行操作。
对于某些类型的多用户应用程序,同步存储是可以加以利用的,但仅限于应用程序能够被存储系统锁定、从而只在不存在其他更改时才允许更改的情形。这需要一种与通用文件同步不切实际的集成水平。大多数实现这一功能的系统所使用的同步机制内建于数据库层或应用程序层,而非那种必须盲目运作的通用机制。为了在使用同步存储时维护数据完整性,有必要做到一次只有一个人编辑文件,等待所有潜在用户都接收到在该文件“保存”之后所做的更新,然后另一位用户才能编辑该文件并重复此过程。但一次只能有一位用户处理该文件,且必须在自己打开文件进行编辑之前接收到其他用户的更新。否则系统就必须在每一种情况下询问用户保留哪些更改、丢弃哪些更改。
这种完整性流程无法以任何现实可行的方式应用于数据库文件。该文件被设计为始终保持打开和被访问的状态,而不仅仅是快速打开、编辑和保存。保存也并非手动操作,而且并不总是可预测的。我们通常假定保存是在使用过程中持续发生的,但缓存甚至会使那些保存操作都无法被手动控制。然而出于性能方面的原因,这又是必需的。
困惑往往源于这样一个事实:单一用户在不必担心另一位用户同时访问系统的情况下,可以使用同步存储(如 Google Drive 或 Apple iCloud)来充当备份机制(它只是自动地制作一份远程副本),和/或作为一种复制文件的手段,以便该单一用户能够先在一个位置使用该文件,再在另一个位置使用,而无需手动将文件从一个位置移动到另一个位置。只要该单一用户在不同位置之间转移时留出足够的时间,以确保任何缓存都已刷新、同步与锁定都已完成,或确保自己没有让该应用程序的第一个实例保持打开状态,他们就可以合理地认为这是一个安全的系统(但无法完全保证 – 即便如此,该机制仍带有发生竞态条件的微小风险)。正因为存在“一种方法”可以在单用户模式下安全地将同步存储与该应用程序配合使用,许多非技术背景的会计或财务人员便会错误地以为多用户同时访问(这是完全不同的另一回事)也同样可行。然而,这是不可能的。
实际发生的情况是,你会在多个用户之间陷入竞态条件,而且你永远无法完全确定它没有发生。有时数据会直接出错,毫无疑问竞态条件已经发生,因为数字会出现严重的失准。但更常见的情况是,某些交易会直接丢失,即便它们已经过审核。
我们来举个例子。用户 1 在家中向 QuickBooks 录入了一张新的收据。这一更改开始保存到本地计算机,待其完成后,便开始将新文件转发到云端(在线)的 Google Drive。与此同时,用户 2 在办公室开始在一张发票上录入一笔客户付款。用户 2 的 QuickBooks 数据文件副本处于打开状态,在使用期间无法被覆盖,因此正在发送到 Google Drive 的副本无法到达用户 2。一旦用户 2 保存了他的更改,他的副本也想要发送到 Google Drive。现在 Google Drive 不得不处理两份文档,它们起初是相同的,但如今各自带有截然不同的更改,而任何一份副本都不同时包含两者的更改。它没有任何可能的手段将二者合并,因此它要么接受用户 1 作为主版本并丢弃用户 2 的更改(例如“先到优先”);要么接受用户 2 的更改并丢弃用户 1 的更改(例如“最新优先”);当然,它也可以丢弃所有更改、一概不予接受。在任何一种情况下,都不可能在所有用户已在本地保存其财务交易之后仍将这些交易全部保留下来。用户 1 或用户 2(或可能两者)都将会发现,他们以为已经保存的数据突然之间凭空消失。再加入更多用户,问题就会变得更加严重。
问题的一部分在于,当在文件访问层级进行操作、并在完整文件层级进行数据同步和共享时,无法只锁定某一条记录或某一行,也无法将交易彼此隔离或合并更改。该文件是单一实体,而它已经发生了变化。要么全部、要么全无。各个 QuickBooks 应用程序无法彼此直接通信,也无法通过数据库文件进行通信,来协调写入、保存、读取等操作以绕开这一问题。它们是盲目的,无法知道其他应用程序正在试图同时处理该文件,因为每一个都拥有自己独有的文件副本,它们之间没有任何“共享”的东西来允许通信。这些副本之间互不关联,这里并不涉及什么量子态。然后我们还可以加入这样一些潜在问题:在 Internet 连接缓慢或有故障时,或者更糟糕地在某个实例离线时,有一个或多个应用程序实例正在被使用。当同步最终发生时,可能会有数小时甚至数天的更改不得不去覆盖、或被覆盖。我们很少谈论的是毫秒级别,而往往是数天的数据。
对于本地共享的文件,当这个问题得到处理时,其处理方式是多方面的。首先,只有一个文件,而非该文件的多个副本。因此所有更改都能同时且即时地提供给该应用程序的所有副本。当应用程序的某一个实例要向文件写入数据时,它会使用一种锁定与告警机制,类似于集群文件系统使 SAN 得以运作的方式:它可以向其他应用程序实例发出信号,表明正在进行某项更改,它们必须等待该更改完成,然后刷新自己那份数据副本,之后才能继续。只能有一个实例进行写入,所有其他实例都必须等待。不存在竞态条件,因为锁是在开始之前获取的,并在完成时释放。而且所有实例只有在数据当前可访问时才能运作,如果连接丢失,它们就无法继续。这是一种至关重要的数据完整性保护机制。某些应用程序会把这一机制提升到更高的层次,增设直接的(而非通过共享文件的)通信信道,以使这一过程更迅速、从而获得更好的性能。但很少有应用程序会做到这个地步,因为一旦深入到那个层级,通常远比试图将现代多用户系统硬塞进数十年前的设计要容易得多——直接迁移到现代应用程序反而更简单。
希望这澄清了为何会计人员通常会以为同步文件能够奏效,以及为何他们常常声称它“对我有效”——而其实他们应该说的是“我运气好”或“我是在一个完全不同、并不适用于多用户环境的场景中使用它的”;也澄清了为何你完全可以将 Google Drive、NextCloud、iCloud、DropBox 等与 QuickBooks 及其他遗留风格的应用程序配合使用来进行备份和数据传输,却不能考虑试图将其用作获取多用户访问的手段,因为它根本无法保持数据的完整。