2016-12-03 90 views
2

对于排名系统(在拥有数万用户的数据库中),我想按用户总数排列三列: Points_A,Points_BPoints_C。我想知道这将是一个更好的选择:总列或新列

  • 每个被访问排名网页时,通过总和所有列和顺序相加计算用户的排名
  • 创建一个新列Points_total与三列的总和(我每次更新其他列之一时更新总数),并按此Points_total排序查询。

排名页面可能每秒要求多次,所以表现非常重要。第二种选择更快还是不推荐?

+1

最好的办法就是试试更简单的方法(不存储总和),看看性能是否可以接受*。不是哪一个更好,因为不管多困难多少,比方说,它的速度要快1000倍,比如它需要从120毫秒到少于一飞秒的延迟,120毫秒对于一个页面来说足够快加载。即只有在显着影响用户体验的情况下,性能才非常重要。 –

回答

3

从MySQL 5.7.6开始,您可以创建生成的列,使您能够根据表中其他列中的其他值存储虚拟值。

CREATE TABLE tbl (
    Points_A INT, 
    Points_B INT, 
    Points_C INT, 
    Points_total INT AS (Points_A + Points_B + Points_C) 
); 

生成列定义具有以下语法:

COL_NAME DATA_TYPE [总是GENERATED] AS(表达式)
    [VIRTUAL | STORED] [UNIQUE [KEY] [注释评论]
    [NOT] NULL] [PRIMARY] KEY]

您可以决定使用VIRTUALSTORED(取决于你的需要)。

从MySQL 5.7.8开始,你也可以添加INDEXes on virtual columns,所以在你的情况下 - 这正是你正在寻找的(这种方式列中的值被索引)。

如果您使用的是比5.7.8更早的MySQL,并且多次访问排名页面 - 我建议您将特定列中的计算值相同并在该列上添加索引。

+0

如果OP有成千上万的用户,那么需求可能需要一个索引。 –

+0

注意:某些时候(数百万用户?)重复更新索引的成本将会过高。成本本质上是“从BTree(索引)中删除一行并将其重新插入到其他地方”。 –

0

这是一个想法。而不是在排名的每一个变化上采取昂贵的行动,看看这个复杂的方法可能会更好:

但首先,这取决于Points只增加,从不减少。它还假定你只需要排在前10名。并且不关心发现“我在10000中排名7654”)

设置一个触发器(或应用程序代码),每当排名增加时都会作出反应。它将点加在一起并根据阈值进行检查。如果低于第10位的值,则不要做任何事情。

如果更高,则执行SELECT ... ORDER BY ... LIMIT 10以获得新的“前10位”并将结果存储在单独的表中。另外,更新阈值。

可能好处:

  • 这个单独的表是什么是访问“多次每一秒”,但主表不需要。
  • 做最少的工作(?)的时间少。 (但是对于投入很多的东西需要做更多的工作。)
  • 从主表(10K行)中分离出等级显示表(10行),从而减少锁争用。
  • 可以是用例的查询缓存:
    • 打开它,
    • 设置query_cache_size = 20M(不要太大),
    • query_cache_type = DEMAND
    • 添加SQL_CACHE到基层SELECT;将SQL_NO_CACHE添加到其他大多数SELECTs

如果你需要的不仅仅是一个的“前10名”页多,考虑到节约“前50名”。对于前5页,新表就足够了。对于第6页及更高版本,请以硬方式进行(扫描10K行表格)。希望这将会是非常罕见的,仍然是一个优势。