针对大型myisam表的MySQL优化

sat*_*gie 6 mysql indexing myisam query-optimization freeradius

OS=centos 6.7  [Dedicated server]
memory=15G      
cpu=Intel(R) Xeon(R) CPU E5-2403
mysql= V 5.1.73    
Run Code Online (Sandbox Code Playgroud)

这是一个MyISAM表,包含大约500万行数据.在每5-6分钟插入大约3000个用户的数据(例如上传和下载速率,会话状态等).

表信息:描述"radacct"

在此输入图像描述

my.cnf中

在此输入图像描述

在此输入图像描述

从mysql慢查询日志中获取大部分时间的查询之一如下

Query_time: 7.941773  Lock_time: 0.155912 Rows_sent: 1 Rows_examined: 5377
use freeradius;
SET timestamp=1461582118;
SELECT sum(acctinputoctets) as upload,
       sum(acctoutputoctets) as     download
    FROM radacct a 
    INNER JOIN (SELECT acctuniqueid, MIN( radacctid )     radacctid 
                   FROM radacct 
                   WHERE username='batman215'
                     and acctstarttime  between '2016-02-03 12:10:47'
                                            and '2016-04-25 16:46:01' 
                   GROUP BY   acctuniqueid) b 
        ON a.acctuniqueid = b.acctuniqueid 
       AND a.radacctid =    b.radacctid;
Run Code Online (Sandbox Code Playgroud)

解释查询输出

在此输入图像描述

当有许多用户试图查看其消耗的带宽时,由于高负载和IO,服务器无法满足请求.我有什么办法可以进一步优化数据库吗?

表"radacct"的索引

在此输入图像描述

不使用\ G解释查询

在此输入图像描述

谢谢

O. *_*nes 7

让我们从你的内部查询开始,这是:

        SELECT acctuniqueid, 
               MIN( radacctid ) radacctid 
          FROM radacct 
         WHERE username='batman215'
           and acctstarttime between '2016-02-03 12:10:47'
                                 and '2016-04-25 16:46:01' 
      GROUP BY   acctuniqueid
Run Code Online (Sandbox Code Playgroud)

您正在寻找相等匹配username和范围匹配acctstarttime.然后你acctuniqueid用来分组和拉出一个极值(MIN())radacctid.

因此,要加速此子查询,需要以下复合索引.

(username, acctstarttime, acctuniqueid, radacctid)
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?将索引(这些是BTREE索引)视为其中值的排序列表.

  1. 查询引擎随机访问列表 - 快速,O(log(n)) - 以查找匹配的第一个条目usernameBETWEEN范围的低端.
  2. 然后按顺序逐个扫描列表,直到达到BETWEEN范围的高端.这称为索引范围扫描.
  3. 在扫描时,它会acctuniqueid,按顺序查找每个新值,然后取最低值 - 按顺序排列的第一个值 - radacctid然后向前跳到下一个值accuniqueid.这被称为松散的索引扫描,它奇迹般便宜.

所以,添加复合索引.这可能会对您的查询性能产生重大影响.

您的外部查询看起来像这样.

SELECT sum(acctinputoctets) as upload,
       sum(acctoutputoctets) as     download
  FROM radacct a  
 INNER JOIN (  /*an aggregate 
                * yielding acctuniqueid and raddactid
                * naturally ordered on those two columns
                */
           ) b ON a.acctuniqueid = b.acctuniqueid
              AND a.radacctid =     b.radacctid
Run Code Online (Sandbox Code Playgroud)

为此,您需要复合覆盖指数

(acctuniqueid, radacctid, acctinputoctets, acctoutputoctets)
Run Code Online (Sandbox Code Playgroud)

这部分查询也满足于索引魔法.

  1. 索引中的前两列允许根据内部查询的结果查找所需的每一行.
  2. 然后,查询引擎可以扫描索引,将其他两列的值相加.

(这就是所谓的覆盖,因为它包含了一些列中存在只是因为我们希望自己的价值,而不是因为我们希望他们编入索引.其他一些品牌和DBMS的模型允许包含在没有使他们搜索的索引额外列的索引.这是稍微便宜一点,特别是在INSERT操作方面.MySQL不这样做.)

因此,您的第一个操作项:添加这两个复合索引并重试您的查询.

从你的问题来看,你在桌面上放置了很多单列索引,希望它们可以加快速度.这是数据库设计中臭名昭着的反模式.尊重,您应该摆脱任何您不需要的索引.他们没有帮助查询,他们放慢速度INSERTS.那是你的第二个行动项目.

第三,请阅读 http://use-the-index-luke.com/这非常有帮助.

专业提示:你看到我如何格式化你的查询?ON当您必须了解查询时,开发清晰显示查询的表,列,条件和其他方面的个人格式约定非常重要.