我正在努力改进我为工作编写的这个程序。最初我很着急,他们不关心性能或其他任何事情。因此,我做出了一个可怕的决定:查询整个数据库(一个 SQLite 数据库),然后将结果存储在列表中以供我的函数使用。但是,我现在正在考虑让我的每个函数都线程化,并且让函数只查询它需要的数据库部分。有大约 25 个函数。我的问题是,这样做安全吗?另外,是否有可能有那么多并发连接?我只会从数据库中提取信息,从不插入或更新。
3 回答
SQLite 没有写并发,但它支持任意多个同时读的连接。只要确保每个线程都有自己的连接。
25 个同时连接不是一个聪明的主意。这是一个巨大的数字。
我通常为这个问题创建一个多层设计。我通过一种具有内部缓存的 ObjectFactory 类将所有请求发送到数据库。ObjectFactory 会将请求转发到 ConnectionPoolHandler 并将结果存储在其缓存中。此连接池处理程序使用 X 个同时连接,但将它们分派给多个线程。
但是,在应用此设计之前必须做一些说明。你首先要问自己以下两个问题:
- 您的应用程序是唯一可以访问此数据库的应用程序吗?
- 您的应用程序是唯一修改此数据库中数据的应用程序吗?
如果第一个问题是否定的,那么您可能会遇到锁定问题。如果您的第二个问题的回答是否定的,那么应用缓存将非常困难。您甚至可能不希望全部实现任何缓存。
如果您经常根据唯一引用(例如主键)请求对象,缓存尤其有趣。在这种情况下,您可以将最常用的对象存储在 Map 中。一个流行的缓存集合是“LRUMap”(“最近最少使用”地图)。这个集合的好处是它会自动将最常用的对象排列到顶部。同时,它具有最大尺寸,并自动从地图中删除很少使用的项目。
缓存的第二个优点是每个对象只存在一次。例如:
- 从数据库中获取一个员工。
- ObjectFactory 将结果集转换为实际的对象实例
- ObjectFactory 立即将其存储在缓存中。
- 稍后,使用 SQL "... where name like "John%" 语句获取了一堆员工。
- 在将结果集转换为对象之前,ObjectFactory 首先检查这些记录的 ID 是否可能已经存储在缓存中。
- 找到了匹配!啊哈,这个对象不需要重新创建。
将某个对象仅在内存中保存一次有几个优点。
最后但并非最不重要的一点是,在 Java 中有类似“弱引用”的东西。这些是实际上可以被垃圾收集器清理的引用。我不确定它是否存在于 C# 中以及它是如何被调用的。通过实现这一点,您甚至不必关心缓存对象的最大数量,您的垃圾收集器会处理它。
我向我描述的方式[*]是让每个并发线程打开自己的数据库连接,因为每个连接一次只能处理一个查询或修改。然后,具有连接的线程组可以轻松地执行并发读取。如果您遇到许多并发写入导致过度阻塞或无法获取锁的严重问题,那么您将超过 SQLite 为您做的事情(并且应该考虑像 PostgreSQL 这样的基于服务器的数据库) .
请注意,如果这样更方便,您也可以让主线程打开工作线程的连接,但建议(为了您的理智,如果没有别的!)只实际使用来自一个线程的每个连接。
[* 对于 SQLite 的正常构建。当然,可以在构建时将其关闭。]