您的应用程序似乎与我的应用程序具有相同的特征。我写了一个 MySQL 自定义存储引擎来有效地解决这个问题。描述here
想象一下,您的数据以 2M 固定长度条目(每个实体一个)的数组形式排列在磁盘上,每个条目包含 3650 行(每天一个)20 字节(每天一个实体的行)。
您的阅读模式读取一个实体。它在磁盘上是连续的,因此需要 1 次搜索(大约 8 毫升秒)并以 100MB/秒的速度读取 3650x20 = 大约 80K ......所以它在几分之一秒内完成,轻松满足您每秒 1 次查询的读取图案。
更新必须在磁盘上 2M 个不同的位置写入 20 个字节。在最简单的情况下,这需要 2M 次查找,每次查找大约需要 8 毫秒,因此需要 2M*8ms = 4.5 小时。如果将数据分布在 4 个“raid0”磁盘上,则可能需要 1.125 小时。
然而,这些地方相距仅 80K。这意味着在 16MB 块(典型的磁盘缓存大小)内有 200 个这样的位置,因此它可以以高达 200 倍的速度运行。(1 分钟)现实介于两者之间。
我的存储引擎就是按照这种理念运行的,尽管它比固定长度的数组更通用一点。
您可以完全按照我的描述编写代码。将代码放入 MySQL 可插拔存储引擎意味着您可以使用 MySQL 通过各种报告生成器等查询数据。
顺便说一句,您可以从存储的行中删除日期和实体 id(因为它们是数组索引)并且可能是唯一的 id - 如果您真的不需要它,因为 (entity id, date) 是唯一的,并且将 2 个值存储为 3 字节 int。那么您存储的行是 6 个字节,每 16M 有 700 次更新,因此插入速度更快,文件更小。
编辑与平面文件比较
我注意到评论普遍偏爱平面文件。不要忘记目录只是文件系统实现的索引,它们通常针对相对较少的相对较大的项目进行优化。对文件的访问通常经过优化,因此它期望打开的文件数量相对较少,并且打开和关闭以及每个打开的文件的开销都相对较高。所有这些“相对”都与数据库的典型使用有关。
使用文件系统名称作为我认为是 1 到 2Million 的非稀疏整数的实体 ID 的索引是违反直觉的。例如,在编程中,您将使用数组,而不是哈希表,并且您不可避免地会为昂贵的访问路径带来大量开销,而访问路径可能只是数组 indeing 操作。
因此,如果您使用平面文件,为什么不只使用一个平面文件并对其进行索引呢?
性能编辑
该应用程序的性能将取决于磁盘寻道时间。我在上面所做的计算确定了你能做的最好的事情(尽管你可以通过减慢 SELECT 来加快 INSERT - 你不能让它们都变得更好)。无论您使用数据库、平面文件还是一个平面文件都没有关系,只是您可以添加更多您并不真正需要的搜索并进一步减慢速度。例如,与“查找数组”相比,索引(无论是文件系统索引还是数据库索引)会导致额外的 I/O,这些会减慢您的速度。
编辑基准测量
我有一张看起来很像你的桌子(或者几乎完全像你的一个分区)。它是 64K 实体而不是 2M(你的 1/32)和 2788 个“天”。该表的创建顺序与您的插入顺序相同,并且具有相同的索引 (entity_id,day)。一个实体上的 SELECT 需要 20.3 秒来检查 2788 天,这大约是预期的每秒 130 次寻道(在 8 毫秒平均寻道时间的磁盘上)。SELECT 时间将与天数成正比,而不太依赖于实体的数量。(它在具有更快寻道时间的磁盘上会更快。我在 RAID0 中使用了一对 SATA2,但这并没有太大区别)。
如果您将表重新排序为实体顺序 ALTER TABLE x ORDER BY (ENTITY,DAY) 则相同的 SELECT 需要 198 毫秒(因为它是在单个磁盘访问中读取订单实体)。但是,ALTER TABLE 操作需要 13.98 天才能完成(对于 182M 行)。
测量结果告诉您其他一些事情 1. 您的索引文件将与您的数据文件一样大。此示例表为 3GB。这意味着(在我的系统上)所有索引都以磁盘速度而不是内存速度。
2.您的 INSERT 率将呈对数下降。插入数据文件是线性的,但插入索引的键是 log。在 180M 记录时,我每秒获得 153 次 INSERT,这也非常接近搜索速率。它表明 MySQL 正在为几乎每个 INSERT 更新一个叶索引块(正如您所期望的,因为它是在实体上编制索引但按天顺序插入的。)。因此,您正在查看 2M/153 秒 = 3.6 小时来完成 2M 行的每日插入。(除以您可以通过跨系统或磁盘分区获得的任何效果)。