mysql:使用SET还是很多列?

时间:2011-06-02 20:50:53

标签: php mysql database database-design

我正在使用PHP和MySQL。我有以下记录:

  • 具有等级的各种“事件类型”的事件(事件可以有多个类别和子类别,但是有固定数量的此类别和子类别)(时间戳)

设置表格的最佳方法是什么?我是否应该有一堆列(30左右)带有枚举的是或否表示该类别的成员资格?或者我应该使用MySQL SET数据类型? http://dev.mysql.com/tech-resources/articles/mysql-set-datatype.html

基本上我有表现,我希望能够检索给定类别的所有事件的ID。只是寻找一些有关最有效的方法的见解。

4 个答案:

答案 0 :(得分:9)

听起来你主要关注的是表现。

有几个人建议拆分成3个表(类别表加上简单的交叉引用表或更复杂的树层次结构建模方式,如嵌套集或物化路径),这是我第一次想到的阅读你的问题。

对于索引,像这样的完全规范化的方法(增加两个JOIN)仍将具有“非常好”的读取性能。一个问题是对事件的INSERT或UPDATE现在也可能包括一个或多个INSERT / UPDATE / DELETE到交叉引用表,在MyISAM上意味着交叉引用表被锁定,在InnoDB上意味着行被锁定,因此,如果您的数据库忙于大量写入,那么与仅仅锁定事件行相比,您将遇到更大的争用问题。

就个人而言,我会在优化之前尝试这种完全规范化的方法。但是,我会假设你知道你正在做什么,你的假设是正确的(类别永远不会改变),你有一个使用模式(大量的写入),需要一个较不规范化的扁平结构。这完全没问题,是NoSQL的一部分。

SET与“很多列”

那么,关于你的实际问题“SET与很多专栏”,我可以说我曾与两家拥有智能工程师的公司合作(其产品是CRM网络应用程序......其中一项实际上是事件管理),他们都使用“大量列”方法来处理这种静态集合数据。

我的建议是考虑您将在此表上进行的所有查询(按频率加权)以及索引的工作方式。

首先,使用“大量列”方法,您将需要在每个列上使用索引,以便您可以执行SELECT FROM events WHERE CategoryX = TRUE。使用索引,这是一个超快查询。

与SET相比,您必须使用按位AND(&),LIKE或FIND_IN_SET()来执行此查询。这意味着查询不能使用索引,必须对所有行进行线性搜索(您可以使用EXPLAIN来验证这一点)。慢查询!

这是SET一个坏主意的主要原因 - 它的索引仅在您选择精确的类别组时才有用。如果您按事件选择类别,SET会很有效,但不是相反。

较少规范化的“大量列”方法(与完全标准化相比)的主要问题是它不能扩展。如果你有5个类别并且它们永远不会改变,那很好,但是如果你有500个并且正在改变它们,这是一个大问题。在您的方案中,大约30个永远不会更改,主要问题是每列都有一个索引,因此如果您正在进行频繁写入,那么由于必须更新的索引数量,这些查询会变慢。如果选择这种方法,您可能需要检查MySQL慢查询日志,以确保在繁忙的一天中由于争用而没有异常缓慢的查询。

在你的情况下,如果你的是一个典型的阅读重量级网络应用程序,我认为采用“大量列”方法(因为两个CRM产品,出于同样的原因)可能是理智的。该SELECT查询的肯定比SET快。

TL; DR 不要使用SET,因为“按类别选择事件”查询会很慢。

答案 1 :(得分:2)

事件和事件类型/类别之间的关系是多对多关系,如echo says,但是一个简单的外部参照表会留下一个问题:如果要查询任何给定节点的所有后代,则必须进行多次递归查询。在一棵深树上,这将是非常低效的。

因此,当您说“检索给定类别的所有ID”时,如果您执行表示所有后代,那么您希望使用嵌套集模型

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

嵌套集模型使写入更新速度稍慢,但使非常容易检索子树:

enter image description here

  • 要获取电视子树,您需要查询所有类别left >= 2right <= 9
  • Leaf节点始终有left = right - 1
  • 您可以在不拉动这些行的情况下找到后代的数量:(right - left - 1)/2
  • 查找继承路径和深度也很容易(单个查询的东西)。有关详细信息,请参阅文章。

答案 2 :(得分:2)

类别数量固定是好的。如果不是,你就不能使用任何一种方法。

检查您链接的页面上的为什么不应该使用SET 。我认为应该给你一个全面的指南。

我认为最重要的是索引。此外,修改SET稍微复杂一些。

答案 3 :(得分:1)

您可以尝试使用交叉引用(外部参照)表,在事件及其类型之间创建多对多关系。

create table event_category_event_xref
(
  event_id int,
  event_category_id int,

  foreign key(event_id) references event(id),
  foreign key (event_category_id) references event_category(id)
);

事件/类别成员资格由此表中的记录定义。因此,如果您有{event_id = 3, event_category_id = 52}的记录,则表示事件#3属于#52类。同样,您可以拥有{event_id = 3, event_category_id = 27}的记录,依此类推。