我正在设计一个测验应用程序,但我仍然坚持如何设计答案表。
假设我有以下表格:
User(user_id,...other columns)
Question(question_id,user_id,...other columns)
QuestionAnswers(question_id,answer_id... other columns)
现在该如何处理UserAnswers表?我想到的结构是:
UserAnswers(user_id,question_id,answer_id,.. other columns)
我所做的结构在开始时效果很好,但是一旦达到1000万行,性能开始降低。考虑到我的应用程序,如果存在10,000个问题,并且系统中有1000个用户,并且每个用户回答每个问题。我将很容易达到1000万行,随着用户和问题的增长,表的大小将会急剧增长。
存储这些答案的更好方法是什么?
此外,我在MySQL中设计了系统。您认为相同的表结构在其他DBMS中会更好吗?
mysql> explain select count(*) from user_answer where question_id = 9845;
+----+-------------+-------------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | user_answer | NULL | ref | question_id | question_id | 4 | const | 645 | 100.00 | Using index |
+----+-------------+-------------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select count(*) from user_answer;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select count(*) from user_answer;
+----------+
| count(*) |
+----------+
| 20042126 |
+----------+
1 row in set (11 min 30.33 sec)
答案 0 :(得分:2)
一般的索引概念是密钥中最左边的概念。让我们以下面的密钥为例(无论是否主要不是这里的重点)
key(a,b,c)
适用于
等查询select region from myTable where c='Turkey'
不使用上述密钥。你可以忍受一桌扫描。
适用于
等查询select region from myTable where a=17 and c='Turkey'
该密钥最多用于最最左侧部分,即a
,因为b
不在查询中。所以关键是有用的,但并不完全有用。这意味着,至少它可以快速将您带到已分段的a
行,但从那里执行where
。
让我说上面的另一种方式:在该查询中,它没有完全使用索引来到c
。它知道b
不在查询的混合中,并且在完全使用索引时,不会神奇地跳过b
以获得c
。但至少部分使用了索引。
这就是为什么在精简索引宽度(如整数)和复合材料上,我经常创建第二个复合索引“走另一条路”,如此answer所示为连接表:
unique key(studentId,courseId,term), -- no duplicates allowed for the combo (note student can re-take it next term)
key (courseId,studentId),
忽略term
进行此讨论。重点是,那些是薄的(开销相对较低)。第二个密钥需要开销。所以它是有代价的,我愿意付出代价。但是对于向另一个方向发出的询问,我被覆盖了。含义,涉及courseId
而不包含studentId
的查询。
注意,我在上面的复合翻转不是一个明星。经常向我指出,如图所示,它会导致不必要的开销。特别是,对于第二个键,它应该只在courseId
上(非复合键)。如果在第一把钥匙上,无论出于何种原因term
楔入第二位,那么这将是一个有效的例子。
更好的例子是
key (a,b,c),
key (c,b)
除其他外,上述内容对于仅针对c
以及b
和c
的查询非常有用。但不仅仅是b
。
The Takeaway:
拒绝将新索引分散到您的架构中的冲动,愚蠢地认为它们将被使用。特别是对于在实际和频繁查询中未获取的非最左侧列。当然不是那些刚才提到的和更广泛的列,如varchar(100)乘以多个索引排序的几个翻转。它们所做的一切都可能会减慢插入和更新速度,并且在实际查询中提供很多次性能提升。所以仔细审查这一切。
所有索引选择都需要付出代价。只有你应该确定什么适合你的系统。