【问题标题】:Table schema design while using innodb使用 innodb 时的表架构设计
【发布时间】:2023-03-25 03:04:01
【问题描述】:

我在为我们的系统设计表架构时遇到了一个问题。

情况如下:

  1. 我们的系统有很多项目(超过 2000 万个),每个项目都有一个唯一的 id,但每个项目可以有很多记录。例如,对于 id 为 1 的项目,大约有 5000 条记录,每条记录有 20 多个属性。需要通过其 id 和一个或多个属性的状态来标识,以便在selectupdatedelete 中使用。

  2. 我要使用innodb

但是问题是在使用innodb的时候,必须要有cluster index。 由于上述情况,它似乎必须找到一个集群索引,所以我只能使用 auto_increment int 作为键

目前的设计如下:

create table record (
item_key int(10) unsigned NOT NULL AUTO_INCREMENT,
item_id int(10) unsigned NOT NULL,
attribute_1 char(32) NOT NULL,
attribute_2 int(10) unsigned NOT NULL,
.
.
.
.
.
attribute_20 int(10) unsigned NOT NULL,
PRIMARY KEY (`item_key`),
KEY `iattribute_1` (`item_id`,`attribute_1`),
KEY `iattribute_2` (`item_id`,`attribute_2`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=latin1

sql语句:

select * from records 
  where item_id=1 and attribute_1='a1' and attribute_2 between 10 and 1000;

updatedelete 语句类似。

我不认为这是一个好的设计,但我想不出别的;欢迎所有建议。


对不起,如果我没有把问题说清楚。

  1. 我想要访问(选择、更新、删除、插入)的是记录,而不是项目。 项目有自己的属性,但在上面的描述中,我提到的属性是属于记录的。

  2. 每个项目可以有很多记录,比如项目 1 有大约 5000 条记录。

  3. 每条记录有42个属性,其中一些可以为NULL,每条记录都有一个唯一的id,这个id在不同的item中是唯一的,但是这个id是字符串而不是数字

  4. 我想以这种方式访问​​记录:

    A.我只会在一次或一次查询中获取(或更新或删除)属于一个特定项目的记录

    B.我将获取或更新查询中所有属性或某些特定属性的值

    C.查询条件中的属性可能和我想要的属性不一样。

所以可能会有一些 SQL 语句,例如:

Select attribute_1, attribute_N from record_table_1 where item_id=1 and attribute_K='some value' and attribute_M between 10 and 100

而我认为原设计不好的原因有:

  1. 我不能选择一个属性或记录id作为主键,因为它没有用,在每个查询中,我必须分配项目id和一些属性作为查询条件(比如“哪里item_id=1 and attribute_1='value1' and attribte_2 between 2 and 3),所以我只能用一个auto_increment int number作为主键。这样的结果就是每次查询都要扫描两棵b-tree,看起来比如二级索引的扫描是无效的。

  2. 复合键似乎也没什么用,因为查询的条件可能因许多属性而异。

  3. 按照原来的设计,我好像加了很多索引来满足不同的查询,不然就得处理全表扫描的问题,但是显然索引太多不利于更新、删除、插入操作。

【问题讨论】:

    标签: mysql schema innodb


    【解决方案1】:

    你是对的,架构是错误的。将属性 1..20 作为表中的字段不是这样做的方法,您需要一个单独的表来存储此信息。该表将包含来自该记录的item_key 以及它自己的keyvalue,因此第二个表将具有允许更好搜索的索引。

    类似于以下内容:

    查看图表很明显有问题,因为record 表太空了,对我来说看起来不正确,所以也许我在原始问题中遗漏了一些东西......

    复合键

    我想也许你正在寻找复合键而不是聚集索引,这是另一回事。您可以通过以下方式实现:

    create table record (
    item_id int(10) unsigned NOT NULL,
    attribute_1 char(32) NOT NULL,
    attribute_2 int(10) unsigned NOT NULL,
    .
    .
    .
    .
    .
    attribute_20 int(10) unsigned NOT NULL,
    PRIMARY KEY (`item_id`,`attribute_1`,`attribute_2`),
    KEY `iattribute_1` (`item_id`,`attribute_1`),
    KEY `iattribute_2` (`item_id`,`attribute_2`)
    ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=latin1
    

    【讨论】:

    • 小心。实体属性值模型是 Bill Karwin 即将出版的书中描述的反模式之一:slideshare.net/billkarwin/…
    • 我知道 EAV 的局限性——你说得对,它不是一个非常好的通用模式,并且可能会破坏许多应该设计成一个好的模式的特性。但是我仍然认为它有一个没有预定义属性的地方。在这种情况下,我不确定,因为 OP 含糊不清,所以我会修改以更好地解释我的意思。
    • 嗨,理查德,感谢您的建议。对不起,如果我没有说清楚。实际上我想要访问的是记录,而不是项目。每条记录都有一个唯一的id,属于一个项目,每条记录有20多个属性。
    • 而且一项可以有很多条记录。我想获取属于具有其他属性条件的一个特定项目的记录的某些属性的值。如:select attribute_1,attribute_2,attribute_3 from records where item_id=1 and attribute_4='hello' and attribute_5在3到6之间。EAV模型适合这种情况吗?看来我必须访问许多表
    • 而且问题是项太多(超过2000万),所以即使我把记录表拆分成几个结构相同的小表(比如id在1到10000之间) table1,table2中id在10001到20000之间),看来还是原来设计的不好,因为一个表中可能有太多的记录属于同一个项目。
    【解决方案2】:

    如果你想要一个集群索引并且不想使用 myisam 引擎,听起来你应该使用两个表:一个用于项目的唯一属性,另一个用于项目的每个实例(具有指定的属性)。

    【讨论】:

      猜你喜欢
      • 2015-05-23
      • 2010-09-28
      • 2021-02-19
      • 2013-08-29
      • 1970-01-01
      • 1970-01-01
      • 2011-06-06
      • 2015-12-31
      • 2016-03-26
      相关资源
      最近更新 更多