存储实体变更:MySQL是否是合适的解决方案?

11

我想要将对"entity"表所做的更改保存下来,类似于日志记录。当前在MySQL中实现的方式是使用以下表:

CREATE TABLE `entitychange` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `entity_id` int(10) unsigned NOT NULL,
  `entitytype` enum('STRING_1','STRING_2','SOMEBOOL','SOMEDOUBLE','SOMETIMESTAMP') NOT NULL DEFAULT 'STRING_1',
  `when` TIMESTAMP NOT NULL,
  `value` TEXT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  • entity_id是我的entity表的主键。
  • entitytype是在entity表中更改的字段。有时只更改一个字段,有时更改多个。一次更改=一行。
  • value是字段“新值”的字符串表示形式。

例如将字段entity.somedouble从3更改为2时,运行以下查询:

UPDATE entity SET somedouble = 2 WHERE entity_id = 123;
INSERT INTO entitychange (entity_id,entitytype,value) VALUES (123,'SOMEDOUBLE',2);

我需要选择特定实体和实体类型最近15天的更改。例如:在最近15天内,具有SOMEDOUBLE的实体ID 123的最新更改。

现在,有两件事我不喜欢:

  1. 所有数据都存储为TEXT——尽管大多数(少于1%)实际上并不是文本,在我的情况下,大多数值是DOUBLE。这是个大问题吗?
  2. 由于表已经有2亿行,因此插入速度变得非常缓慢。目前我的服务器负载已经达到10-15。

我的问题:如何解决这两个"瓶颈"?我需要扩展。

我的方案如下:

  1. 像这样存储:http://sqlfiddle.com/#!2/df9d0 (点击浏览)——将更改存储在entitychange表中,然后按其数据类型在entitychange_[bool|timestamp|double|string]中存储值
  2. 使用HASH(entity_id)进行分区——我考虑了大约50个分区。
  3. 我应该使用另一个数据库系统,比如MongoDB吗?

1
我会说同样的话,不知道你为什么要使用枚举,你应该使用至少两个表,一个是日志表,另一个是实体类型表。在你的主日志表中,有一个指向实体类型表的外键。 - xtrm
1
由于您有一个包含5个值的枚举,因此您将拥有一个entitytype表,共计32条记录(2^5=32)。entitytype表中的每条记录都将有自己的id,在主日志表中只有一个简单的外键。这样可以节省大量空间,并且JOIN操作将会很快。另一种方法是,如果我们正确使用规范化,可以在entitytype表中设置“实体外键”,并从主日志表中删除entitytype。最后建议进行性能测试。 - xtrm
@xtrm:实体类型表的表模式是什么样的?你能在sqlfiddle.com上做一个吗? - Stefan
@Stoleg,那不是规范化的意思。 - Bill Karwin
@BillKarwin 我确实想知道我错过了什么。我大致理解规范化是将实体属性从事实表中取出,并引用存储在单独表中的这些属性。理想情况下,您应该最终拥有两种类型的表:具有指向属性的键的数据表和属性表。 - Stoleg
显示剩余7条评论
8个回答

5
如果我遇到您提到的问题,我会设计如下的日志表:
  1. EntityName: (字符串)正在操作的实体(必填)。
  2. ObjectId: 正在操作的实体,主键。
  3. FieldName: (字符串)实体字段名称。
  4. OldValue: (字符串)实体字段旧值。
  5. NewValue: (字符串)实体字段新值。
  6. UserCode: 应用程序用户唯一标识符。(必填)
  7. TransactionCode: 任何更改实体的操作都需要具有唯一的事务代码(例如GUID)(必填), 在更新多个字段更改实体的情况下,这些列将是跟踪更新(事务)中所有更改的关键点。
  8. ChangeDate: 交易日期。(必填)
  9. FieldType: 枚举或文本,显示字段类型,如TEXT或Double。(必填)
拥有这种方法
任何实体(表)都可以被追踪
报告将易于阅读
只记录更改。
事务代码将是检测单个操作更改的关键点。

顺便说一下。
Store the changes in the entitychange table and then store the value 
according to its datatype in entitychange_[bool|timestamp|double|string]

在单个表中,您将不需要更改和数据类型。

Use partitioning by HASH(entity_id)

我建议按照ChangeDate对数据进行分区,或者为旧的ChangeDate创建备份表并从主日志表中移除。

Should I use another database system, maybe MongoDB?

任何数据库都有其优缺点,你可以在任何关系型数据库中使用此设计。 有用的比较文档式数据库(如MongoDB)的链接可在此处找到。 希望能对您有所帮助。

谢谢您的回答。为什么需要FieldType - Desprit
1
这可能对未来的解释使用很方便。想想如果更改已经被应用到一个数组或者...。 - Mohsen Heydari

3
现在我认为我理解了您的需求,您需要一个具有记录更改历史的可版本化表格。这可能是实现相同目标的另一种方式,您可以轻松进行一些快速测试,以查看它是否比当前解决方案具有更好的性能。Symfony PHP框架使用Doctrine和Versionable插件实现此功能。 请注意,有两个键的主键唯一索引,即版本和fk_entity。 同时,请查看保存的值。您将在未更改的字段中保存0值,在更改的字段中保存更改后的值。
CREATE TABLE `entity_versionable` (
  `version` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `fk_entity` INT(10) UNSIGNED NOT NULL,
  `str1` VARCHAR(255),
  `str2` VARCHAR(255),
  `bool1` BOOLEAN,
  `double1` DOUBLE,
  `date` TIMESTAMP NOT NULL,
  PRIMARY KEY (`version`,`fk_entity`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;


INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a1", "0", "0", "0", "2013-06-02 17:13:16");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a2", "0", "0", "0", "2013-06-11 17:13:12");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "b1", "0", "0", "2013-06-11 17:13:21");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "b2", "0", "0", "2013-06-11 17:13:42");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "0", "1", "0", "2013-06-16 17:19:31");

/*Another example*/
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a1", "b1", "0", "0", CURRENT_TIMESTAMP);


SELECT * FROM `entity_versionable` t WHERE 
(
    (t.`fk_entity`="1") AND 
    (t.`date` >= (CURDATE() - INTERVAL 15 DAY))
);


可能还有一步可以提高性能,就是将所有历史日志记录保存在单独的表中,每月保存一次。这样,每个表中都不会有太多记录,并且按日期搜索将变得非常快。


感谢您的回答,但我不确定存储整行会提高性能。比方说,在我的生产环境中,我有大约30个列,而且大多数情况下只有一个列发生了变化,我认为存储所有内容(包括大型 TEXT 字段)都不是很好。或者我是错误的假设了吗? - Stefan

2
这里有两个主要的挑战:
  1. 如何高效地存储数据,即占用更少的空间并采用易于使用的格式
  2. 管理大型表格:存档、备份和恢复的便利性
  3. 性能优化:更快的插入和查询速度
高效存储数据
  1. value字段。我建议将其设为VARCHAR (N)。原因:

    • 使用N<255将每行节省1个字节,仅因为数据类型。
    • 对于此字段使用其他数据类型:固定类型无论值是什么都会占用空间,通常每行将占用8个字节(datetime、长整数、char(8)),其他可变数据类型对于此字段来说过于庞大。
    • TEXT数据类型还会导致性能损失:(来自BLOB和Text数据类型手册)
查询结果中使用临时表处理的TEXT列实例会导致服务器使用磁盘上的表而不是内存中的表,因为内存存储引擎不支持这些数据类型。使用磁盘会产生性能损失,因此只有在真正需要时才将BLOB或TEXT列包含在查询结果中。例如,避免使用SELECT *选择所有列。
每个BLOB或TEXT值在内部都由单独分配的对象表示。这与所有其他数据类型形成对比,后者在打开表时为每个列分配存储空间。
基本上,TEXT用于存储大字符串和文本片段,而VARCHAR()则专门用于相对较短的字符串。
  1. id字段。 (感谢@steve的更新)我同意该字段不包含任何有用信息。为主键使用3个列:entity_identitypewhenTIMESTAMP将很好地保证不会出现重复项。同样的列将用于分区/子分区。
表格管理 有两个主要的选项:MERGE表格和分区。MERGE存储引擎基于My_ISAM,据我所知,它正在逐步淘汰。这里有一些关于[MERGE存储引擎]的阅读材料。2 主要工具是分区,它提供了两个主要的好处: 1. 分区切换(通常是在大量数据上的即时操作)和滚动窗口场景:将新数据插入一个表中,然后立即将所有数据切换到归档表中。 2. 按排序顺序存储数据,这使得可以进行分区修剪-仅查询包含所需数据的那些分区。MySQL允许子分区将数据进一步分组。
entity_id进行分区是有意义的。如果您需要查询较长时间的数据或者在查询表格时有其他模式-请使用该列进行子分区。除非分区将在该级别进行切换,否则无需对主键的所有列进行子分区。
分区的数量取决于想要为该分区设置多大的数据库文件。子分区的数量取决于核心数,这样每个核心可以搜索自己的分区,N-1个子分区应该足够,这样1个核心可以完成整体协调工作。
优化:
插入:
插入速度快的表没有索引,所以先插入大块数据(执行更新),然后创建索引(如果可能)。
将Text改为Varchar可以减轻数据库引擎的负担。
最小化日志记录和表锁可能有帮助,但往往难以使用。
查询:
将Text改为Varchar肯定会改善查询效率。
有一个包含最近数据的当前表——最近15天,然后通过分区切换将其移至归档表。在此过程中,您可以选择按日期首先分区表,然后按实体ID分区归档表,并通过将少量(1天)数据移动到临时表并更改其分区方式来更改分区方式。
另外,您可以考虑按日期进行分区,因为您有许多基于日期范围的查询。首先考虑数据及其部分的使用情况,然后再决定哪种模式最适合支持它。
至于您的第三个问题,我不认为使用MongoDB会特别有益于这种情况。

"id"字段:使用(entity_id,when)作为PRIMARY KEY并不实用,因为一个实体可以在同一时间在此表中有两个更改(例如每行的SOMEBOOLSOMEDOUBLE具有相同的时间戳)- 是否改用(entity_id,when,entitytype) - Stefan
@steve,如果这里有多个字段可以同时更改-那么是的。聚集索引是表本身,因此使键变长不会占用任何额外空间。 - Stoleg
你能否对类似于这种方式 http://sqlfiddle.com/#!2/df9d0 的更改存储方法(如我的第一种方法)的优缺点做出评价? - Stefan
@steve 这对于这个任务来说有些过度了。规范化的结构对于实体管理很好,但对于归档来说过度了,并且会减慢带有连接查询的查询速度。你需要考虑数据的使用情况,然后应用最佳模型来支持它,而不是将所有内容都规范化到3或5的程度。评论太短了,无法提供详细的分析。 - Stoleg
1
@steve 例如:Varchars即使为NULL也始终使用至少1-2个字节,somedouble始终使用8个字节(NOT NULL),而somebool则使用1个字节,枚举类型已在数据库引擎中进行了规范化,其他表仅用于JOIN。由于该表的使用方式,这种结构的好处将无法得到利用。 - Stoleg

2
这被称为时间数据库,研究人员已经努力寻找存储和查询时间数据的最佳方法超过20年了。
试图像您所做的那样将EAV数据存储起来是低效的,因为在TEXT列中存储数字数据会使用大量空间,并且正如您发现的那样,您的表越来越长。
另一个选项有时被称为第六范式(尽管有多个无关的6NF定义),是存储额外的表以存储每个要在时间上跟踪的列的修订版。 这类似于@xtrm答案提出的解决方案,但它不需要存储未更改的列的冗余副本。 但它确实导致表数量激增。
我已经开始阅读Anchor建模,它承诺处理结构和内容的时间变化。 但我还不理解它足够好,无法解释它。 我只会链接到它,也许对您有意义。
以下是一些包含时间数据库讨论的书籍:

1
在一个TEXT列中存储整数是不可行的!TEXT是最昂贵的类型。
我会为您想要监视的每个字段创建一个日志表。
CREATE TABLE entitychange_somestring (
    entity_id INT NOT NULL PRIMARY KEY,
    ts TIMESTAMP NOT NULL,
    newvalue VARCHAR(50) NOT NULL, -- same type as entity.somestring
    KEY(entity_id, ts)
) ENGINE=MyISAM;

对它们进行分区。

请注意,我建议使用MyISAM引擎。对于这些无约束、仅插入的表,您不需要事务处理。


1
I would advise you to conduct thorough testing, but based on my own tests, I have achieved good results with both INSERT and SELECT using the previously posted table definition. I will provide detailed testing procedures so that anyone can easily replicate and verify the results. Be sure to back up your data before conducting any tests.
Please note that these are only tests and may not necessarily reflect or improve your specific case. However, they are a useful way to learn and potentially discover valuable information and results.

The suggestions provided here are very helpful, and using a predefined VARCHAR type with size instead of TEXT will certainly improve speed. However, for the sake of data integrity, I recommend using InnoDB instead of MyISAM.

TESTING:

1. Set up the table and insert 200 million rows of data:

CREATE TABLE `entity_versionable` (
  `version` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `fk_entity` INT(10) UNSIGNED NOT NULL,
  `str1` VARCHAR(255) DEFAULT NULL,
  `str2` VARCHAR(255) DEFAULT NULL,
  `bool1` TINYINT(1) DEFAULT NULL,
  `double1` DOUBLE DEFAULT NULL,
  `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`version`,`fk_entity`)
) ENGINE=INNODB AUTO_INCREMENT=230297534 DEFAULT CHARSET=latin1

为了在大约35分钟内向表中插入2亿行数据,请查看我的其他问题,peterm已经回答了其中最佳的填充表格方法之一。它完美地工作。

执行以下查询2次以插入2亿行非随机数据(每次更改数据以插入随机数据):
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
SELECT 1, 'a1', 238, 2, 524627, '2013-06-16 14:42:25'
FROM
(
    SELECT a.N + b.N * 10 + c.N * 100 + d.N * 1000 + e.N * 10000 + f.N * 100000 + g.N * 1000000 + h.N * 10000000 + 1 N FROM 
     (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) f
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) g
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) h
) t;


*由于您已经拥有200万行真实随机数据的原始表格,您可能不需要填充它,只需导出表格数据和架构并将其导入具有相同架构的新测试表格中。这样,您就可以在新表中使用真实数据进行测试,并且获得的改进也将适用于原始表。

2. 为性能更改新的测试表格(或者按照步骤1中我的示例获取更好的结果)。 一旦我们设置好了新的测试表并填充了随机数据,我们应该检查上述建议,并更改表以加快速度:

  • 将TEXT更改为VARCHAR(255)。
  • 选择两个或三个列作为好的主键唯一索引。在第一个测试中尝试使用自动增量版本和fk_entity。
  • 如果需要,对表进行分区,并检查是否提高了速度。建议在第一次测试中不要对其进行分区,以便通过更改数据类型和mysql配置来检查实际性能提升。请查看以下链接获取一些分区和优化技巧。
  • 优化和修复表格。索引将会重新创建,搜索速度将大大加快:

OPTIMIZE TABLE test.entity_versionable;
REPAIR TABLE test.entity_versionable;
*制作一个脚本,每晚运行它以执行优化和维护您的索引最新状态。


3. 通过仔细阅读以下线程来改进您的MySQL和硬件配置。这些值得一读,我相信您会获得更好的结果。

  • 花费一点钱轻松改善数据库硬盘配置:如果可能的话,使用SSD作为主MySQL数据库,使用单独的机械硬盘进行备份。将MySQL日志保存在第三个硬盘上以提高INSERT速度。(记得在几周后对机械硬盘进行碎片整理)。
  • 性能链接: 常规&多核, 配置, 优化IO, Debian核心, 最佳配置, 48GB RAM配置..
  • 分析SQL查询:如何分析查询, 检查查询中可能存在的瓶颈
  • MySQL非常占用内存,如果可能,使用低延迟CL7 DDR3内存。有点离题,但如果您的系统数据很重要,可以考虑使用ECC内存,但它很昂贵。


4.最后,在测试表中测试您的INSERT和SEARCH操作。在使用上述表模式的2亿多个随机数据进行测试时,插入新行花费0.001秒,搜索和选择1亿行需要大约2分钟。虽然这只是一个测试,但结果似乎很好 :)

5.我的系统配置:
  • 数据库:MySQL 5.6.10 InnoDB数据库(测试)。
  • 处理器:AMD Phenom II 1090T X6核心,每个核心3910兆赫。
  • 内存:16GB DDR3 1600兆赫CL8。
  • 硬盘:Windows 7 64位SP1在SSD中,MySQL安装在SSD中,日志记录在机械硬盘中。
    可能使用最新的Intel i5或i7轻松超频到4500Mhz+,因为MySQL只使用一个核心执行一条SQL语句。核心速度越高,执行越快。

6. 阅读更多关于MySQL的信息:
O'Reilly高性能MySQL
MySQL优化SQL语句


7. 使用另一个数据库: MongoDB 或 Redis 将非常适合这种情况,而且可能比 MySQL 快得多。两者都非常容易学习,并且都有各自的优点:
- MongoDB:MongoDB 日志文件增长

Redis

我肯定会选择 Redis。如果你学会了如何在 Redis 中保存日志,那么它将是管理日志的最佳方式,速度极快: Redis 用于记录日志
如果你使用 Redis,请记住以下建议:

  • Redis是用C编译的,存储在内存中,并有一些不同的方法将信息自动保存到磁盘(持久性),您可能不需要担心它。(在灾难情况下,您会失去约1秒钟的日志记录)。

  • Redis被用于管理数千兆字节数据的许多网站,有很多处理这种海量信息的方法,这意味着它很安全(在stackoverflow、暴雪、twitter、youporn等地方使用)。

  • 由于您的日志非常大,为了获得速度而不必访问硬盘,它需要适合内存。您可以为不同的日期保存不同的日志,并仅将其中一些设置为内存中的日志。在达到内存限制的情况下,您不会遇到任何错误,并且一切仍将完美运行,但请查看Redis Faqs以获取更多信息。

  • 我非常确定Redis在这个目的上比MySQL快得多。您需要学习如何使用listssets来更新数据和查询/搜索数据。如果您需要真正高级的查询搜索,应该选择MongoDB,但在这种简单日期搜索的情况下,Redis将是完美的选择。

Instagram博客上发现了一篇不错的Redis文章。


1
为什么INSERT的速度如此缓慢,如何使其更快。以下是我会考虑的事项(并且大致按照我会逐步解决它们的顺序):
  1. 创建一个新的AUTO_INCREMENT-id并将其插入到主键中需要一个锁(在InnoDB中有一个特殊的AUTO-INC锁,它会一直保持直到语句完成,实际上在您的情况下充当表锁)。通常这不是问题,因为这是一个相对快速的操作,但另一方面,如果负载值为10到15(Unix),则可能会有进程等待该锁被释放。从您提供的信息中,我没有看到您的代理键'id'有任何用处。请尝试删除该列是否会显著改变性能。(顺便说一下,没有规定表需要有主键。如果没有,那也没关系)

  2. InnoDB对于INSERT来说可能相对昂贵。这是权衡所做的,以允许其他功能,如事务,可能会影响您。由于您的所有操作都是原子操作,因此我认为不需要事务。话虽如此,请尝试使用MyISAM。注意:MyISAM通常不适用于大型表,因为它只支持表级别锁定而不支持记录级别锁定,但它确实支持concurrent inserts,因此在这里可能是一个选择(特别是如果您放弃了主键,参见上文)

  3. 您可以尝试使用数据库存储引擎参数。InnoDB和MyISAM都有您可以更改的选项。其中一些对实际存储TEXT数据的方式有影响,而其他一些具有更广泛的功能。您应该特别注意innodb_flush_log_at_trx_commit

  4. 如果TEXT列具有非NULL值,则相对昂贵。您当前将所有值存储在该TEXT列中。值得尝试以下操作:向表中添加额外的字段value_intvalue_double并将这些值存储在相应的列中。是的,这将浪费一些额外的空间,但可能会更快 - 但这在很大程度上取决于数据库存储引擎及其设置。请注意,人们认为TEXT列性能的许多观点都不正确。(请参见我对VARCHAR vs TEXT相关问题的回答

  5. 您建议将信息分散在多个表中。只有当您的表完全独立时,这才是一个好主意。否则,任何更改都将导致不止一个INSERT操作,并且很可能会使情况变得更糟。虽然规范化数据通常是好的(tm),但这可能会损害性能。

如何使SELECT查询运行更快

  1. 适当的键。再强调一遍,适当的键。你没有详细说明你的SELECT查询是什么样子的,但我假设它们类似于“SELECT * FROM entitychange WHERE entity_id=123 AND ts>...”。单个复合索引在entity_id和ts上应该足以使此操作快速执行。由于索引必须随每次插入更新,因此可能值得尝试使用entity_id, tsts, entity_id两种性能:这可能会有所不同。

  2. 分区。如果你没有在问题中提到,我甚至不会提起这个话题。你没有说为什么要对表进行分区。从性能上来说,通常没有任何区别,只要你有适当的键。有一些特定的设置可以提高性能,但你需要相应的硬件设置。如果你决定对表进行分区,请考虑按照entity_id或TIMESTAMP列进行分区。使用时间戳,你可以将旧数据归档到存档驱动器上。这样的分区系统需要一些维护(随着时间的推移添加分区)。

我觉得你更关心插入速度而不是查询性能,所以我不会详细介绍SELECT的性能。如果你对此感兴趣,请提供更多细节。


我需要选择关于特定 entity_identitytype 组合的统计数据,因此也许 (entity_id, entitytype, when) 是一个不错的主键。我还需要仅选择最近两周内的条目,因此归档可能是个好主意,对吧?我需要所有数据,但是超过两周的数据并不经常被查询。 - Stefan
你的复合主键听起来很不错。请记住,维护索引会增加插入操作的工作量。尝试将它作为次要(非唯一)键和主键,并查看是否有重大影响。如果您希望通过“when”进行分区,请注意,“when”列需要是您的主键的一部分,最好是它的第一个列,“(when,entity_id,entitytype)”。 - Hazzit

0

在工作中,由于客户条件(金融行业),我们几乎在每个表上都有日志表。

我们是这样做的:两个表(“普通”表和日志表),然后在普通表的插入/更新/删除触发器上存储一个关键字(I、U、D)和旧记录(在更新、删除时)或新记录(在插入时)到日志表中。

我们将这两个表放在同一个数据库模式中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接