mysql查询的高CPU使用率

时间:2013-11-29 16:03:06

标签: mysql ubuntu cpu-usage myisam

我在使用此查询时遇到了一些问题,每当我使用它时,cpu的使用率从5%增加到67%-100%。

我正在通过java服务在ubuntu中运行mysql服务器,但即使我通过任何mysql ide执行查询,结果都是一样的。

我已在网上搜索过一些关于它的内容,所以我发布了mysql的配置文件。我添加了一些属性,然后我在一些帖子中发现但我认为我只是让它变得更糟。

嗯,这是我的my.cnf文件:

[mysqld]
innodb_file_per_table=1
innodb_buffer_pool_size = 256M
wait_timeout = 1800
local-infile=0
open_files_limit=10192
query_cache_size=128M
join_buffer_size=128K
thread_cache_size=4
table_cache=64
key_buffer_size=128M
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 1336
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address        = 0.0.0.0

key_buffer      = 2014M
max_allowed_packet  = 2014M
thread_stack        = 512K
thread_cache_size       = 1024
myisam-recover         = BACKUP
max_connections        = 200

query_cache_limit   = 2048M

log_error = /var/log/mysql/error.log

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 16M

[mysql]

[isamchk]
key_buffer      = 16M

!includedir /etc/mysql/conf.d/

我正在使用此查询:

select regPosition.deviceId, count(regPosition.speed), max(regPosition.speed) from regPosition where (TIMESTAMPDIFF(MINUTE, lastPositionTime,now()) <= '5') and regPosition.speed >= '10' group by regPosition.deviceId;

表的类型是Myisam,它有大约2M的注册表,并且idPosition作为索引。 这是创建表查询:

    CREATE TABLE `regPosition` (
  `idPosition` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id autoincremental.',
  `deviceId` int(5) NOT NULL COMMENT 'Id numérico del equipo. Identificador único para cada vehículo.',
  `lastPositionTime` datetime NOT NULL COMMENT 'Fecha/hora en que se registra la marca de posición (realizada por el dvr).',
  `divisionew` varchar(2) DEFAULT NULL COMMENT 'Orientación  Este u Oeste.',
  `longitude` int(11) NOT NULL COMMENT 'longitud.',
  `divisionns` varchar(2) DEFAULT NULL COMMENT 'Orientación Norte o Sur.',
  `latitude` int(11) NOT NULL COMMENT 'Latitud.',
  `direction` int(11) DEFAULT NULL COMMENT 'Dirección en que apunta el dispositivo.',
  `gradeLon` varchar(100) DEFAULT NULL COMMENT 'Longitud transformada a grados (en decimal).',
  `gradeLat` varchar(100) DEFAULT NULL COMMENT 'Latitud transformada a grados (en decimal).',
  `speed` int(11) NOT NULL COMMENT 'Velocidad del vehículo. Registrada por el dvr',
  PRIMARY KEY (`idPosition`),
  KEY `index` (`idPosition`) USING HASH
) ENGINE=MyISAM AUTO_INCREMENT=6562682 DEFAULT CHARSET=latin1;

[编辑]

查询的目的是获取设备的id和速度大于10的次数(这只是一个例子,它可能更多)并获得数据库记录的最大速度。

这个想法如下:如果速度在5分钟内超过60kmh 5次,我需要知道设备的id,最大速度和超过速度限制的次数。

如果你能给我任何帮助,我会非常高兴:)。

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

添加索引:

 ... ON (deviceId, lastPositionTime, speed) 

并更改查询。替换此谓词:

TIMESTAMPDIFF(MINUTE, lastPositionTime,now()) <= '5'

用这个:

lastPositionTime >= NOW() + INTERVAL -5 MINUTE

这将为您提供查询的覆盖索引(EXPLAIN将显示“使用索引”),这意味着可以从索引中满足查询。

首先需要deviceId列,因为MySQL可以使用以该列作为前导列的索引来优化GROUP BY操作(避免排序操作)。

您希望裸lastPositionTime列上有谓词,因此MySQL可以进行索引范围扫描。当该列被隐藏在函数内部时(例如TIMESTAMPDIFF),MySQL必须为每一行评估该函数。索引范围扫描更快,因为它可以消除对很多行的功能评估。

由于speed列上还有一个谓词,因此您也希望在索引中使用该谓词。

没有引用其他列,因此可以完全从索引中满足查询,而无需访问基础表中的页面。

答案 1 :(得分:1)

这是我的方法,它应该通过表ONCE,没有使用MySQL变量的连接。我在这里申请的前提如下。有序结果必须是设备ID和报告时间的结果。 MySQL变量将跟踪......我是否在同一台设备上工作?和,是最后一条记录启动设备的5分钟内的当前记录,或上次超过给定速度限制的记录这就是原因。假设我有一个给定的设备在15分钟内报告,从早上8点开始报告,并且为了简单的目的每分钟报告一次。速度如下:

8:00   58  -- Start group 8:00, set max time to still consider as 8:05
8:01   60  -- speeding... within the 8:00-8:05 range.  NEW end 5 minutes from now 8:06
8:02   58  -- not speeding
8:03   58  -- not speeding
8:04   59  -- not speeding
8:05   58  -- not speeding
8:06   59  -- not speeding... end of the 8:06 range, 1 over limit, ignore this
8:07   60  -- NEW cycle for device, start at 8:07 set ending time to 8:12 -- SPEEDING 1
8:08   61  -- STILL speeding max 61, extend ending time from 8:08 + 5 minutes to 8:13
8:09   62  -- STILL speeding max 62, extend ending time from 8:09 + 5 minutes to 8:14
8:10   59  -- not speeding
8:11   59  -- not speeding
8:12   60  -- SPEEDING AGAIN, within the 8:14 cutoff... reset 5 minutes from now 8:17
8:13   62  -- speeding still, extend to 8:18
8:14   64  -- speeding still, new max 64 and extend to 8:19
8:15   62  -- still speeding... total times 7 with max of 64 (so far)

如果不考虑上述格式,则必须计算

中的所有记录
8:00 to 8:05
8:01 to 8:06
8:02 to 8:07, etc.

对于样本数据,您可以从

加速
8:05 to 8:09
8:06 to 8:10
8:07 to 8:11, etc 

但是这种方式一直在观察从最后一个加速时间开始的5分钟滚动截止时间。例如,如果在8:20-8:26之间,该人减少到少于60,那么在下次遇到超速时将开始新的循环。否则,请考虑如果一个人从8:00到10:00超速,将返回多少条记录。如果报告是每分钟1次,您将有2 * 60条记录...如果报告更频繁,则会显示更多。

随着设备ID的变化,“第一时间”识别出下一个加速考虑周期的开始。处理完每条记录后,将该设备ID放入“@lastDevice”变量中,以进行下一个循环比较。如果在当前记录上没有遇到超速,则@nextTimeCutoff将与前一行相同,或者无论设备是否为ADD 5 MINUTES ...如果设备正在改变,因为它是条目的最后一行,下一行将开始无论如何,新的“LimitSequence”增加1。

现在,所有这一切,这个查询将为您简化项目。如果你知道你所担心的速度限制(60kph),我的内部查询只会预先知道那些设备被标记为超速的记录。如果你有2M的记录要通过,而且其中大部分都没有超速,那么为什么甚至可以和他们一起玩。因此,内部查询仅限于那些正在加速并将它们放入mysql变量处理的正确顺序的查询。如上所述,它将处理人员第一次超速时的滚动时间段。

select
      r1.DeviceID,
      @overLimitSeq := if( r1.DeviceID = @lastDevice 
                        AND r1.lastPositionTime <= @nextTimeCutoff,
                        @overLimitSeq, @overLimitSeq +1 ) as LimitSequence,
      min( r1.lastPositionTime ) as SpeedingTimeStart,
      max( r1.lastPositionTime ) as SpeedingTimeEnd,
      max( r1.speed ) as MaxSpeed,
      count(*) as TimesOverLimit,
      @lastDevice := r1.DeviceID as CompareForNextID,
      @nextTimeCutoff := r1.lastPositionTime + interval 5 minute as NextCutoff
   from
      ( select rp.deviceID,
               rp.lastPositionTime,
               rp.speed
           from
              regPosition rp
           where
              rp.speed >= 60
           order by
              rp.deviceID,
              rp.lastPositionTime ) r1,
      ( select @lastDevice := 0,
               @overLimitSeq := 0,
               @nextTimeCutoff := now() ) sqlvars
   group by
      r1.DeviceID,
      LimitSequence
   having
      TimesOverLimit > 2
   order by
      r1.deviceID,
      r1.lastPositionTime

答案 2 :(得分:1)

我实际上发现了高CPU使用率的问题。

问题是表的使用不良索引,基本上是我创建索引时定义的顺序。

当我使用&#34;解释&#34;在查询中我得到的搜索是在数百万个注册表中完成的,即使使用直接过滤器(即使它发生了限制100)。

原因是索引,所以我重新确定了索引的顺序,这样解释显示搜索在不到400个注册表中完成,并且cpu使用情况非常完美。

例如这个sql:

EXPLAIN
SELECT d.deviceId, r.divisionew 
FROM 
device d, regPosition r 
WHERE 
d.enabled = 1 
AND d.deviceId = r.deviceId 
AND (DATE_SUB(now(),INTERVAL 8 MINUTE)) < r.lastPositionTime 
ORDER BY d.deviceId DESC

这个是在不使用索引的情况下进行搜索,因此查询完成了大约8百万个注册表的搜索。

通过重新分配索引并且订单正确,此查询在少于400行中搜索完成。

所以基本上改变了索引的顺序,我可以得到索引的工作,以及高cpu使用率的消失。

我只是改变了索引的顺序。我之前有过deviceId,lastPositionTime和更多。 按照这个顺序,索引在改变之后没有工作,添加了分区,改变了顺序并删除了无用的参数,索引就像魅力一样。

因此,当您遇到一些使用mysql的高CPU使用率的麻烦时,您应该检查索引顺序,因为如果您设计的索引应该能够加快查询的速度,并且如果它不存在则可能存在问题索引分配。

至少解决了我的问题。

谢谢大家的帮助。