在实体上存储更改:MySQL是否是正确的解决方案?

Ste*_*fan 11 mysql database-design temporal-database database-partitioning entity-attribute-value

我想存储我在"实体"表上所做的更改.这应该像一个日志.目前它在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;
Run Code Online (Sandbox Code Playgroud)
  • entity_id=我entity桌子的主键.
  • entitytype= entity表中更改的字段.有时只改变一个字段,有时多个.一个变化=一排.
  • value =字段"新值"的字符串表示形式.

将Field entity.somedouble从3 更改为2 时的示例,我运行这些查询:

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

我需要select更改过去15天的特定实体和实体类型.例如:最后15天内最后一次更改SOMEDOUBLE为entity_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?

Moh*_*ari 5

如果我遇到你提到的问题,我会像下面那样设计LOG表:

  1. EntityName:(String)正在被操纵的实体.(必填)
  2. ObjectId:被操纵的实体,主键.
  3. FieldName:(String)实体字段名称.
  4. OldValue:(字符串)实体字段旧值.
  5. NewValue:(字符串)实体字段新值.
  6. UserCode:应用程序用户唯一标识符 (强制)
  7. TransactionCode:任何更改实体的操作都需要具有唯一的事务代码(如GUID)(必需),
    如果实体更新多个字段,这些列将是跟踪更新中所有更改的关键点(transcation) )
  8. ChangeDate: 交易日期.(强制)
  9. FieldType:枚举或显示字段类型的文本,如TEXT或Double.(强制)

使用此方法
可以跟踪任何实体(表)
报告将是可读的
仅记录更改.
事务代码将是通过单个操作检测更改的关键点.

BTW

Store the changes in the entitychange table and then store the value 
according to its datatype in entitychange_[bool|timestamp|double|string]
Run Code Online (Sandbox Code Playgroud)

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

Use partitioning by HASH(entity_id)
Run Code Online (Sandbox Code Playgroud)

我更喜欢通过ChangeDate进行分区或为changeDate创建备份表,这些表已经足够大,可以从主LOG表中备份和卸载

Should I use another database system, maybe MongoDB?
Run Code Online (Sandbox Code Playgroud)

任何数据库都有自己的概率和缺点,您可以在任何RDBMS上使用该设计.可以在此处找到基于文档的数据库(如MongoDB)的有用比较

希望对你有帮助.


xtr*_*trm 3

现在我想我明白你需要什么,一个带有更改记录历史的可版本化表。这可能是实现相同目标的另一种方法,您可以轻松地进行一些快速测试,以查看它是否为您提供比当前解决方案更好的性能。这是 Symfony PHP 框架在 Doctrine 中使用 Versionable 插件实现的方式。
请记住,有两个键(version 和 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))
);
Run Code Online (Sandbox Code Playgroud)


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