mysql并发查询缓慢单条很快

今天接到一个需求,说在一个mysql库上面做20个并发的纯查询,需要40s左右,但是单条的语句执行时间大概在1s左右,而且并发加得愈多,需要耗费的时间越长。

mysql> explain select a.*
    ->   from (select fci.certificate_id      certificateID,
    ->                fci.certificate_code    certificateCode,
    ->                fci.paper_document_code paperDocumentCode,
    ->                fci.archive_by          createByUM,
    ->                fci.status              status,
    ->                fci.file_type           archiveType
    ->           from fs_certificate_info fci
    ->          where 1 = 1
    ->            and fci.ledger_id = '1'
    ->            and exists
    ->          (select fpi.period_name
    ->                   from fs_period_info fpi
    ->                  where fpi.period_year = '2015'
    ->                    and fpi.period_month between '02' + 0 and '02' + 0
    ->                    and source = '1'
    ->                    and fpi.period_name = fci.period_name)
    ->            and fci.file_type ='010200'
    ->            and exists
    ->          (select fcli.certificate_id
    ->                   from fs_certificate_line_info fcli
    ->                  where 1 = 1
    ->                    and fcli.company_segment_code in ('000000')
    ->                    and fcli.business_segment_code in ('0101')
    ->                    and fcli.is_delete = '0'
    ->                    and fcli.certificate_id = fci.certificate_id)
    ->          order by certificate_code) a limit 0,
    ->        20
    -> \G;

*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: 
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 48711
        Extra: NULL
*************************** 2. row ***************************
           id: 2
  select_type: DERIVED
        table: fci
         type: ref
possible_keys: idx_leger_id,xl_test
          key: xl_test
      key_len: 66
          ref: const,const
         rows: 48711
        Extra: Using where
*************************** 3. row ***************************
           id: 4
  select_type: DEPENDENT SUBQUERY
        table: fcli
         type: ref
possible_keys: idx_certificate_id,idx_segment
          key: idx_certificate_id
      key_len: 8
          ref: d0fams.fci.certificate_id
         rows: 1
        Extra: Using index condition; Using where
*************************** 4. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: fpi
         type: ref
possible_keys: indexYearAndMonth,idx_period_name
          key: idx_period_name
      key_len: 33
          ref: d0fams.fci.period_name
         rows: 1
        Extra: Using where
4 rows in set (0.00 sec)


执行计划如上,并没有什么异常,rows也都比较少,最多也就5w左右,整个表索引情况:
  PRIMARY KEY (`certificate_info_id`),
  UNIQUE KEY `certificate_info_id_unique` (`certificate_info_id`),
  UNIQUE KEY `certificate_id_unique` (`certificate_id`),
  KEY `idx_accrediation` (`accreditation`,`accreditation_replaceable`),
  KEY `idx_certificate_code` (`certificate_code`),
  KEY `idx_period_name` (`period_name`),
  KEY `idx_leger_id` (ledger_id,file_type,certificate_code)
  KEY `xl_test` (ledger_id,file_type,certificate_code)   --这个索引是我后面添加的。

索引的选择率:

mysql> select count(1),ledger_id from fs_certificate_info group by ledger_id;
+----------+-----------+
| count(1) | ledger_id |
+----------+-----------+
|   108240 | 1         |
|     1000 | 86        |
|    10030 | 99        |
+----------+-----------+
3 rows in set (0.17 sec)

mysql> select count(1),file_type from fs_certificate_info group by file_type;
+----------+-----------+
| count(1) | file_type |
+----------+-----------+
|    13170 | 010100    |
|   106100 | 010200    |
+----------+-----------+
2 rows in set (0.12 sec)

可以看到,这个索引选择率太差了基本全表扫描,但是这个也不能说明什么,单条sql依然很快,但是并发起来就不怎么样了。看到三思君的博客,说是因为随机I/O的原因导致。有兴趣的可以看下。

http://blog.itpub.net/7607759/viewspace-719645/

确实说的很有道理,通过二级索引找到乱序的聚簇索引的key值,然后在找需要的列。我也觉得这是原因,所以试了一下通过all的方式访问表,但是结果还是和原来的一样,单条很快,并发20 需要50s左右!

mysql> alter table fs_certificate_info add index xl_test(ledger_id,file_type,certificate_code);explain select a.*   from (select fci.certificate_id      certper_document_code paperDocumentCode,                fci.archive_by          createByUM,                fci.status              status,                fci.file_type           archiveType           from fs_certificate_info fci ignore index(idx_leger_id,xl_test)          where 1 = 1            and fci.ledger_id = '1'            and exists          (select 1                   from fs_period_info fpi                  where fpi.period_year = '2015'                    and fpi.period_month between '02' + 0 and '02' + 0                    and source = '1'                    and fpi.period_name = fci.period_name)            and fc       and fcli.company_segment_code in ('000000')                    and fcli.business_segment_code in ('0101')                    and fcli.is_delete = '0'                    and fcli.certificate_id = fci.certificate_id)          order by certificate_code) a limit 0,20\G;
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: 
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 97422
        Extra: NULL
*************************** 2. row ***************************
           id: 2
  select_type: DERIVED
        table: fci
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 97422
        Extra: Using where; Using filesort
*************************** 3. row ***************************
           id: 4
  select_type: DEPENDENT SUBQUERY
        table: fcli
         type: ref
possible_keys: idx_certificate_id,idx_segment
          key: idx_certificate_id
      key_len: 8
          ref: d0fams.fci.certificate_id
         rows: 1
        Extra: Using index condition; Using where
*************************** 4. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: fpi
         type: ref
possible_keys: indexYearAndMonth,idx_period_name
          key: idx_period_name
      key_len: 33
          ref: d0fams.fci.period_name
         rows: 1
        Extra: Using where
4 rows in set (0.00 sec)


开始怀疑开发如何进行的并发测试的?是否并发有问题啊?所以我自己写了个简单的shell造并发验证:

executemysql.sh

#!/bin/sh
echo `date`
qry='select a.*
  from (select fci.certificate_id      certificateID,
               fci.certificate_code    certificateCode,
               fci.paper_document_code paperDocumentCode,
               fci.archive_by          createByUM,
               fci.status              status,
               fci.file_type           archiveType
          from fs_certificate_info fci ignore index(idx_leger_id,xl_test)
         where 1 = 1
           and fci.ledger_id = '1'
           and exists
         (select 1
                  from fs_period_info fpi
                 where fpi.period_year = '2015'
                   and fpi.period_month between '02' + 0 and '02' + 0
                   and source = '1'
                   and fpi.period_name = fci.period_name)
           and fci.file_type ='010200'
           and exists
         (select 1
                  from fs_certificate_line_info fcli
                 where 1 = 1
                   and fcli.company_segment_code in ('000000')
                   and fcli.business_segment_code in ('0101')
                   and fcli.is_delete = '0'
                   and fcli.certificate_id = fci.certificate_id)
         order by certificate_code) a limit 0,20;'
mysql -uroot -pxxxxxx --socket=/paic/my5003/var/mysql.sock --port=5003 << eof
use d0fams
$qry
exit
eof
echo `date`

1.sh

cnsz081161:d0fams :MAS > more 1.sh
echo `date`
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
sh executemysql.sh &
echo `date`

验证了一下,时间上确实需要50s左右,而且奇怪的是所以的结果几乎都是同时出来。而且观察了执行脚本的时候io和cpu都比较空闲。

现在没有办法了,不知道原因了!哎,只能从sql入手了,使用了exsits的方式,是否可以改写为join的方式。抱着试试的心态改了一下。

mysql> explain select fci.certificate_id      certificateID,
    ->                fci.certificate_code    certificateCode,
    ->                fci.paper_document_code paperDocumentCode,
    ->                fci.archive_by          createByUM,
    ->                fci.status              status,
    ->                fci.file_type           archiveType
    -> from fs_certificate_info fci inner join fs_period_info fpi on fpi.period_name = fci.period_name
    -> inner join fs_certificate_line_info fcli on fcli.certificate_id = fci.certificate_id
    -> where fci.ledger_id = '1' and fci.file_type ='010200' and fpi.period_year = '2015'
    ->                    and fpi.period_month between '02' + 0 and '02' + 0
    ->                    and source = '1'
    ->                    and fcli.company_segment_code in ('000000')
    ->                    and fcli.business_segment_code in ('0101')
    ->                    and fcli.is_delete = '0'
    -> order by certificate_code limit 0,20\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: fci
         type: ref
possible_keys: certificate_id_unique,idx_period_name,idx_leger_id,xl_test
          key: xl_test
      key_len: 66
          ref: const,const
         rows: 48711
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: fpi
         type: ref
possible_keys: indexYearAndMonth,idx_period_name
          key: idx_period_name
      key_len: 33
          ref: d0fams.fci.period_name
         rows: 1
        Extra: Using where
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: fcli
         type: ref
possible_keys: idx_certificate_id,idx_segment
          key: idx_certificate_id
      key_len: 8
          ref: d0fams.fci.certificate_id
         rows: 1
        Extra: Using index condition; Using where
3 rows in set (0.00 sec)

执行时间也从1s变到了0.03s

mysql> select fci.certificate_id      certificateID,
    ->                fci.certificate_code    certificateCode,
    ->                fci.paper_document_code paperDocumentCode,
    ->                fci.archive_by          createByUM,
    ->                fci.status              status,
    ->                fci.file_type           archiveType
    -> from fs_certificate_info fci inner join fs_period_info fpi on fpi.period_name = fci.period_name
    -> inner join fs_certificate_line_info fcli on fcli.certificate_id = fci.certificate_id
    -> where fci.ledger_id = '1' and fci.file_type ='010200' and fpi.period_year = '2015'
    ->                    and fpi.period_month between '02' + 0 and '02' + 0
    ->                    and source = '1'
    ->                    and fcli.company_segment_code in ('000000')
    ->                    and fcli.business_segment_code in ('0101')
    ->                    and fcli.is_delete = '0'
    -> order by certificate_code limit 0,20;
+---------------+-------------------+-------------------+------------+--------+-------------+
| certificateID | certificateCode   | paperDocumentCode | createByUM | status | archiveType |
+---------------+-------------------+-------------------+------------+--------+-------------+
|            12 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            12 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            12 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            13 | cerfiticatecode01 | NULL              | CAOJING260 | 100    | 010200      |
|            13 | cerfiticatecode01 | NULL              | CAOJING260 | 100    | 010200      |
|            13 | cerfiticatecode01 | NULL              | CAOJING260 | 100    | 010200      |
|            15 | cerfiticatecode01 | NULL              | CAOJING260 | 50     | 010200      |
|            15 | cerfiticatecode01 | NULL              | CAOJING260 | 50     | 010200      |
|            15 | cerfiticatecode01 | NULL              | CAOJING260 | 50     | 010200      |
|            17 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            17 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            17 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            19 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            19 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|            19 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|           111 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|           111 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|           111 | cerfiticatecode01 | NULL              | CAOJING260 | 10     | 010200      |
|           112 | cerfiticatecode01 | NULL              | CAOJING260 | 90     | 010200      |
|           112 | cerfiticatecode01 | NULL              | CAOJING260 | 90     | 010200      |
+---------------+-------------------+-------------------+------------+--------+-------------+
20 rows in set (0.03 sec)

然后对这个sql进行了20个并发测试,2s就完成了。。真是太坑了。。


总结:mysql没有oracle那么强大的cbo,所以需要有严格的规范和高手写高效的sql,才能发挥mysql的性能!


你可能感兴趣的:(mysql)