喜欢和'='等于sql - mysql集群中的运算符性能

时间:2016-10-03 05:36:26

标签: mysql sql performance query-optimization mysql-cluster

的MySQL群集-GPL-7.4.12-1.el6.x86_64

查询1

    select
    userinfo.username,
    userinfo.firstname,
    userinfo.lastname,
    userinfo.email,
    radcheck.attribute,
    radcheck.`value`,
    radusergroup.groupname,
    userinfo.id,
    userinfo.account_type,
    userinfo.workphone,
    userinfo.homephone,
    userinfo.mobilephone,
    userinfo.address,
    userinfo.zone,
    userinfo.account_state,
    userinfo.link_type,
    userinfo.device_owner,
    userinfo.email
    FROM
    userinfo
    INNER JOIN radcheck ON userinfo.username = radcheck.username
    INNER JOIN radusergroup ON userinfo.username = radusergroup.username
    WHERE
    radcheck.attribute='Expiration'  AND userinfo.mobilephone LIKE '9876543210%'

此查询需要 8s 才能完成 以下是查询的explain输出

    +----+-------------+--------------+------+----------------------+-----------+---------+----------------------------+------+-------------+
    | id | select_type | table        | type | possible_keys        | key       | key_len | ref                        | rows | Extra       |
    +----+-------------+--------------+------+----------------------+-----------+---------+----------------------------+------+-------------+
    |  1 | SIMPLE      | radcheck     | ref  | username,attribute   | attribute | 34      | const                      |    9 | Using where |
    |  1 | SIMPLE      | userinfo     | ref  | username,mobilephone | username  | 131     | ctradius.radcheck.username |   13 | Using where |
    |  1 | SIMPLE      | radusergroup | ref  | username             | username  | 66      | ctradius.userinfo.username |   10 | Using where |
    +----+-------------+--------------+------+----------------------+-----------+---------+----------------------------+------+-------------+

查询2

以下是在<中完成的查询0.005s

    select
    userinfo.username,
    userinfo.firstname,
    userinfo.lastname,
    userinfo.email,
    radcheck.attribute,
    radcheck.`value`,
    radusergroup.groupname,
    userinfo.id,
    userinfo.account_type,
    userinfo.workphone,
    userinfo.homephone,
    userinfo.mobilephone,
    userinfo.address,
    userinfo.zone,
    userinfo.account_state,
    userinfo.link_type,
    userinfo.device_owner,
    userinfo.email
    FROM
    userinfo
    INNER JOIN radcheck ON userinfo.username = radcheck.username
    INNER JOIN radusergroup ON userinfo.username = radusergroup.username
    WHERE
    radcheck.attribute like 'Expiration%'  AND userinfo.mobilephone LIKE '9876543210%'

以下是查询的解释

 +----+-------------+--------------+-------+----------------------+-------------+---------+----------------------------+------+------------------------+
    | id | select_type | table        | type  | possible_keys        | key         | key_len | ref                        | rows | Extra                  |
    +----+-------------+--------------+-------+----------------------+-------------+---------+----------------------------+------+------------------------+
    |  1 | SIMPLE      | userinfo     | range | username,mobilephone | mobilephone | 203     | NULL                       |  585 | Using where; Using MRR |
    |  1 | SIMPLE      | radusergroup | ref   | username             | username    | 66      | ctradius.userinfo.username |   10 | Using where            |
    |  1 | SIMPLE      | radcheck     | ref   | username,attribute   | username    | 66      | ctradius.userinfo.username |   17 | Using where            |
    +----+-------------+--------------+-------+----------------------+-------------+---------+----------------------------+------+------------------------+

问题

为什么在使用like运算符(第二个查询)而不是=(第一个查询)时查询执行速度更快?

1 个答案:

答案 0 :(得分:2)

您有两个完全不同的执行计划。

对于您的第一个查询,MySQL通过radcheck.attribute='Expiration'上的索引查找radcheck.attribute的所有条目(并假设有9行适合)。然后,它将使用username(以及username上的索引)为每个pssible用户名加入其他表,然后从表中读取userinfo.mobilephone的值,看它是否适合。< / p>

对于第二个查询,它会检查userinfo.mobilephone上的索引,查找以9876543210开头的任何内容(假设它会找到585行)。然后,它将使用username(以及username上的索引)连接其他表,以使用正确的移动电话获取所有用户名,然后从表中读取radcheck.attribute的值并查看是否适合。

当您实际上只有一些行以您的手机号码开头,但很多行包含radcheck.attribute='Expiration'时,您的第二个查询当然要快得多,因为它必须完成其余的执行(加入,特别是从表中读取)的行数少得多(尽管你需要比你的解释中显示的行多得多,以证明8秒的合理性。)

MySQL必须猜测哪种方式更快,并根据您的查询和一些关于表的统计数据选择一种方法。它选择了完全错误。

在你的第一个查询中,MySQL假设在索引中查找=比在索引中查找like更好(并且只产生9行,这显然是不正确的)。在第二个查询中,只需选择在第一个或第二个索引中查找like是否更好 - 并且猜对了。但是,如果你愿意,已经找了userinfo.mobilephone LIKE '0%',它本来可能比较慢(取决于你的数据)。

您可以尝试一些事项:

  • 使用optimize table userinfo, radcheck, radusergroup。这将重新创建索引和统计信息。根据您的数据,它可能不再仅为您的第一个查询假设9行。
  • 强制mysql始终按照STRAIGHT_JOIN的第二个查询的顺序加入:

    ...
    from radcheck
    straight_join userinfo ON userinfo.username = radcheck.username
    straight_join radusergroup ON userinfo.username = radusergroup.username
    where ...
    

    但是,如果您正在查找例如,这可能会导致查询速度变慢userinfo.mobilephone LIKE '0%',或userinfo.mobilephone根本没有条件,所以请在不同情况下对其进行测试。

  • 继续使用like 'Expiration%',您甚至可以将其切换为=,如果您没有或只需要一部简短的手机即可查找,但不能保证始终使用第二部然后(可能会改变其他mysql版本)。

顺便说一下,如果你用{integer} username替换你的id - 列,你可能会得到一些额外的性能,让它(你的第一部分)成为你的三个主键表,并使用此id来加入您的表,因为您的索引会变得更小,例如找到Expiration的值后,不必查找用户名。这应该减少你第一次查询的执行时间(根据你对表格和数据的一些假设,我估计超过50% - 但这只是一个猜测,它也可能只有5%)。但是,由于它不能证明为此更改整个应用程序是合理的(尽管其他查询也会从中获益),但是当您必须对代码进行一些重大更改时,您可能会考虑它。 (您可以模拟其中的某些部分并尝试添加索引radcheck(attribute, username)是否值得付出努力,这应该为您的第一个查询提供30%-50% - 假设username不是您的已经是主键)