如何通知在不同进程中完成的SQLite数据库更改过程?

时间:2009-03-24 11:34:34

标签: python sqlite notifications

假设我有两个或多个处理SQLite数据库的进程 - 一个“播放器”进程和许多“编辑器”进程。

“播放器”进程读取数据库并更新视图 - 在我的情况下,根据存储在数据库中的事件,它将是混合到声卡的波形。

“编辑器”进程是该数据库的任何编辑器:它会不断更改数据库。

现在我希望播放器快速反映编辑更改。

我知道SQLite提供了在同一进程中跟踪数据库更改的钩子,但似乎没有关于如何使用多个进程执行此操作的信息。

我可以不断地轮询数据库,比较记录和触发事件,但这似乎效率很低,特别是当数据库增长到很大的时候。

我正在考虑使用日志表和触发器,但我想知道是否有更简单的方法。

7 个答案:

答案 0 :(得分:5)

关系数据库不是您最好的首选。

为什么?

您希望所有编辑都将更改传递给您的播放器。

您的播放器 - 实际上 - 是所有这些编辑器的服务器。您的播放器需要多个开放连接。它必须听取所有这些连接的变化。它必须显示这些变化。

如果更改非常大,您可以转到混合解决方案,其中编辑器会持续进行更改通知播放器。

无论哪种方式,编辑都必须通知他们玩家他们有变化。它比试图发现数据库变化的玩家简单得多。


更好的设计是接受来自编辑者的消息,持久化并通知玩家的服务器。此服务器既不是编辑器也不是播放器,而只是一个确保处理所有消息的代理。它接受来自编辑和玩家的连接。它管理数据库。

有两种实现方式。服务器是播放器。服务器与播放器分开。服务器的设计不会改变 - 只有协议。当服务器是播放器时,服务器直接调用播放器对象。当服务器与播放器分开时,服务器会写入播放器的套接字。

当播放器是服务器的一部分时,当从编辑器收到消息时,将直接调用播放器对象。当播放器是独立的时,小型阅读器从套接字收集消息并调用播放器对象。

播放器连接到服务器,然后等待信息流。这可以从编辑器输入,也可以是服务器在数据库中持久存储的数据的引用。

如果您的消息流量足够小,以便网络延迟不成问题,编辑器会将所有数据发送到服务器/播放器。如果消息流量太大,则编辑器会写入数据库并向服务器/播放器发送仅带有数据库FK的消息。


请澄清“如果编辑在通知时崩溃,则播放器会在你的问题中永久搞乱”。

这听起来像是播放器服务的糟糕设计。它不能“永久搞砸”,除非它没有得到各个编辑的状态。如果它从编辑器获得状态(但是试图镜像那个状态,那么)你应该考虑一种设计,其中玩家只是从编辑器获得状态而不能“永久搞砸”。

答案 1 :(得分:3)

SQLite有一个update_hook函数可以满足您的需求。

  

SQLite C接口

     

数据更改通知回调

void *sqlite3_update_hook(
    sqlite3*,
    void(*)(void *,int ,char const *,char const *,sqlite3_int64),
    void*
);
  

sqlite3_update_hook()接口注册回调函数   由要调用的第一个参数标识的数据库连接   每当在rowid表中更新,插入或删除行时。任何   由先前调用此函数为同一数据库设置的回调   连接被覆盖。

不幸的是,Python sqlite模块没有公开它......

这是一个有点hacky的解决方法,深入研究C api(来自Python代码)以利用它: https://stackoverflow.com/a/16920926

答案 2 :(得分:2)

只需在两个进程之间打开一个套接字,让编辑器告诉所有玩家有关更新的信息。

答案 3 :(得分:2)

我认为在这种情况下,我会创建一个管理数据库读/写的过程。

每个想要对数据库进行一些修改的编辑都会通过IPC或网络或其他任何方法调用此过程。

然后,此过程可以通知玩家数据库中的更改。当玩家想要检索某些数据时,它应该向管理数据库的进程请求它想要的数据。 (或者db进程告诉它需要什么,当它通知更改时,所以不需要来自播放器的请求)

这样做的好处是只有一个进程访问SQLite数据库,因此数据库上没有锁定或并发问题。

答案 4 :(得分:1)

编辑:我在同一台计算机上处​​理进程。

在我看来,有两种方式:

  • 轮询(如您所述),但将其保留为单个值(就像保留其他表的LastUpdateTime的表一样)

  • 使用目标平台上可用的进程间通信。这可能是Windows中的事件(例如,在C#中(我不知道在Python中)ManualResetEvent,AutoResetEvent,或者如果你想在每个进程中牺牲一个服务器线程,则可以是Mutex),或者在Linux中是Signals

答案 5 :(得分:1)

如果它在同一台机器上,最简单的方法是使用命名管道,“播放器”使用阻塞read()和“编辑器”在修改数据库时将令牌放入管道。

答案 6 :(得分:1)

有多少编辑器进程(为什么要处理?),以及您希望更新的频率如何?这听起来不是一个好的设计,特别是考虑到sqlite对于多次并发访问数据库感到非常高兴

如果多个进程有意义您需要持久性,那么让编辑者通过套接字,管道,共享内存等通知您的播放器可能更为明智。然后让玩家(也就是服务器进程)做持久化。