背景:
我有一个具有以下结构的Mysql模式
@Data
public class DBQueryEvaluation {
//primary key
private final Long id;
//The combination of a 'query' and an 'evaluationId' will always be unique.
private final Long evaluationId;
private final String query;
private final Date createdAt;
private final Date updatedAt;
}
约束 :
'查询'和' evaluationId '的组合始终是唯一的。
对于给定的EvaluationId,可能会有很多查询。该表中共有500万条记录。 (每个评价ID约50,000个查询,其中100个这样的评价= 500万条记录)。
目标:
想要对给定的EvaluationId进行计数(记录)。
问题:
鉴于评估id的基数非常低 (对于大约50k条记录重复相同的评估id):
=== 更新 ===
答案 0 :(得分:2)
- 建议在此处为“ evaluationId”建立索引。期望BTree实现应该能够以毫秒为单位提供计数。 (<10毫秒)
是的,没有索引,引擎必须进行全表扫描。但是,使用索引,它不必访问数据记录,而可以仅从索引中获取计数。为此,它需要读取的索引记录要比数据记录的数目少得多,因为:
例如,如果块大小为10,并且50,000条记录具有评估ID,则需要读取约5,555个块。将其与至少500,000个需要在表扫描中读取的块进行比较。显然,数据库具有优化方法,这会使公平的比较复杂化,因此尝试一下是有意义的。
- 索引如此低的基数属性可能有什么弊端?
基数的作用取决于一个块中可容纳多少数据记录(即<= recordsize / blocksize)。如果该数字接近基数,那么索引的好处就会消失。
- 获取计数(*)的其他最佳方法是什么。
当计数在50,000左右时,您可以重新评估具有 exact 计数的重要性,并且知道获得计数后的第二秒,可能已经有新记录了插入/删除。实际上是49,756还是49,695无关紧要?
如果近似值正确,则运行计划的批处理作业,对所有评估id进行计数,并将其存储在单独的“ count”表中(该表将包含约100条记录)。根据您的需求,您可以安排它每天,每小时,……运行一次,具体取决于营业额和所需的准确性。这样一来,闪电般的速度便会带来轻微的误差。
为提高准确性,您可以将上面的“ count”表与数据表上的插入/删除触发器结合使用,该触发器会将更改的结果(作为+1或-1值)插入到日志表中。然后,查询记录数将以“ count”表为起点,并基于日志表中的+ 1 / -1修改结果。以上作业将在运行时清除日志。