优化SQL以连接随机单词

时间:2015-02-22 07:39:11

标签: mysql sql

我有一个表(“j_un2”),其中有6,318个随机单词,总计数永远不会改变。表中的ID是无间隙的。

我需要生成一个由2个单词组成的5个随机连接字符串的列表,其中字符串的总长度为8个字符。

几天前我已经通过以下方式获得了一些非常有用的帮助: Selecting random words from table

我的基本方法是从表中选择两次,并连接随机选择的单词。

我有一个“fld_len”列,它是单词的长度。

表格结构:

CREATE TABLE `j_un2` (
  `fld_id` int(11) NOT NULL AUTO_INCREMENT,
  `fld_un` varchar(255) DEFAULT NULL,
  `fld_cat_id` int(11) DEFAULT NULL,
  `fld_len` int(2) NOT NULL,
  PRIMARY KEY (`fld_id`),
  KEY `cat` (`fld_cat_id`),
  KEY `bob` (`fld_len`,`fld_un`)
);

该表包含以下索引:

Keyname     Type    Field
PRIMARY     PRIMARY fld_id
bob         INDEX   fld_len, fld_un

如果我做了ORDER BY RAND(),我发现了主要的性能问题。阅读StackOverflow并在此处:http://www.warpconduit.net/2011/03/23/selecting-a-random-record-using-mysql-benchmark-results/

我的查询时间大约为3.7秒:

   SELECT CONCAT(w1.fld_un, w2.fld_un) bbb
        , FLOOR(1 + RAND() * 6318) 'rand_ind'
     FROM j_un2 w1
        , j_un2 w2 
    WHERE w1.fld_len = 8 - w2.fld_len
      AND w2.fld_len < 8
      AND RAND()<(((1/6318)*10)) 
 ORDER BY rand_ind
    LIMIT 20;

这是查询的解释计划:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          w2      range   bob             bob     4           NULL    5886    Using where; Using index; Using temporary; Using filesort
1   SIMPLE          w1      ref     bob             bob     4           func    63      Using where; Using index

我想要使用此查询的页面每月获得大约500k页面查看量,所以非常繁忙(无论如何对我来说),如果用户每次刷新页面需要等待大约4秒钟,他们可能会对它感到恼火。

我在选择单词后也尝试做了CONCAT,但这需要10秒才能运行:

SELECT CONCAT(word1, word2) joined
FROM
   (SELECT w1.fld_un word1, w2.fld_un word2
        , FLOOR(1 + RAND() * 6318) 'rand_ind'
     FROM j_un2 w1
        , j_un2 w2 
    WHERE w1.fld_len = 10 - w2.fld_len
      AND w2.fld_len < 10
      AND RAND()<(((1/6318)*10)) 
 ORDER BY rand_ind
    LIMIT 20) bob;

鉴于我正在尝试通过一个相当简单的连接方法连接两个表,我想知道这个查询是否以前所未有的速度运行,或者是否有任何可以加速的范围?


更新1

实际上,我认为性能影响力取决于表连接机制,因为:

   SELECT CONCAT(w1.fld_un, w2.fld_un) bbb
     FROM j_un2 w1
        , j_un2 w2 
    WHERE w1.fld_len = 8 - w2.fld_len
      AND w2.fld_len < 8
      AND RAND()<(((1/6318)*10)) 
 ORDER BY rand()
    LIMIT 20;

同时运行 - 例如对rand()

的订单没有任何影响

2 个答案:

答案 0 :(得分:0)

您在什么情况下执行该查询?如果在sql之外生成随机值,效率会更高。

如果是在PHP中:

function get_word() {
    $found = false;
    while(!$found) {
        $rand1 = rand(1, 6138); 
        $rand2 = rand(1, 6138);
        $query = " SELECT CONCAT(w1.fld_un, w2.fld_un) word FROM j_un2 w1 , j_un2 w2 WHERE w1.fld_id = '$rand1' AND w2.fld_id = '$rand2';
        ... Execute query and save result in $word
        if(strlen($word) == 8) $found = true;
    } 
    return $word;
} 

这会产生几个但非常有效的查询。

另一种方法:

  • 读一个随机词
  • 确定剩余长度
  • 读取一个长度为
  • 的随机单词

第二种方式的缺点:

  • 它不那么随意
  • 对数据库中单词长度的分布有要求。

答案 1 :(得分:0)

此处可找到仅限SQL的解决方案:http://mysql.rjweb.org/doc.php/random#case_consecutive_auto_increment。总之,假设id中没有间隙,它基于随机取一行。做两次以获得两行。不需要表扫描。如果需要避免两次使用相同的单词,仍然需要再次尝试代码。

如何使两行的总长度等于8个字符?这是一个有趣的转折。对于第二个查询:添加INDEX(len, id),然后在剩余长度内使用MIN和MAX id。然后调整下一个案例&#39;在那个博客(带间隙的AUTO_INCREMENT)中找到第二行。 (好吧,这会变得混乱,但希望我已经给你一些事情要考虑。)

该链接有8&#39;效率&#39; ORDER BY RAND() LIMIT n的替代品。 (没有&#39;完美&#39;。)