【问题标题】:Mysql query staying in 'SENDING DATA' state for long time when using LEFT JOIN使用 LEFT JOIN 时,Mysql 查询长时间处于“SENDING DATA”状态
【发布时间】:2011-03-31 11:17:02
【问题描述】:

我有很长一段时间处于SENDING DATA状态的查询。 有人可以帮我解决这个问题:以下是详细信息

Mysql 查询:

select a.msgId,a.senderId,a.destination,a.inTime,a.status as InStatus,b.status as SubStatus,c.deliverTime,substr(c.receipt,82,7) as DlvStatus 
from inserted_history a  left join submitted_history b  on b.msgId = a.msgId left join delivered_history c  on a.msgId = c.msgId 
where a.inTime between '2010-08-10 00:00:00' and '2010-08-010 23:59:59' and a.systemId='ND_arber'

delivered_history 中的总记录:223870168

inserted_history 中的总记录:264817239

submitted_history 中的总记录:226637058

解释查询返回:

id  ,  select_type  ,  table  ,  type  ,  possible_keys  ,  key  ,  key_len  ,  ref  ,  rows  ,  Extra  
  1  ,  SIMPLE  ,  a  ,  ref  ,  systemId,idx_time  ,  systemId  ,  14  ,  const  ,  735310  ,  Using where  
  1  ,  SIMPLE  ,  b  ,  ref  ,  PRIMARY  ,  PRIMARY  ,  66  ,  gwreports2.a.msgId  ,  2270405  ,    
  1  ,  SIMPLE  ,  c  ,  ref  ,  PRIMARY  ,  PRIMARY  ,  66  ,  gwreports2.a.msgId  ,  2238701  ,  

为 Delivered_history 创建表

CREATE TABLE `delivered_history` (
 `msgId` VARCHAR(64) NOT NULL,
 `systemId` VARCHAR(12) NOT NULL,
 `deliverTime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
 `smscId` VARCHAR(64) NOT NULL,
 `smsc` VARCHAR(20) NOT NULL,
 `receipt` BLOB NULL,
 `errcode` INT(11) NULL DEFAULT NULL,
 PRIMARY KEY (`msgId`, `deliverTime`),
 INDEX `systemId` (`systemId`),
 INDEX `smsc` (`smsc`),
 INDEX `idx_time` (`deliverTime`)
)
ROW_FORMAT=DEFAULT

为插入的历史创建表

CREATE TABLE `inserted_history` (
 `msgId` VARCHAR(64) NOT NULL,
 `systemId` VARCHAR(12) NOT NULL,
 `senderId` VARCHAR(15) NOT NULL,
 `destination` VARCHAR(15) NOT NULL,
 `inTime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
 `status` VARCHAR(20) NOT NULL,
 `msgText` BLOB NULL,
 `msgType` VARCHAR(15) NULL DEFAULT NULL,
 PRIMARY KEY (`msgId`, `inTime`),
 INDEX `systemId` (`systemId`),
 INDEX `senderId` (`senderId`),
 INDEX `destination` (`destination`),
 INDEX `status` (`status`),
 INDEX `idx_time` (`inTime`)
)
ROW_FORMAT=DEFAULT

为提交的历史创建表

CREATE TABLE `submitted_history` (
 `msgId` VARCHAR(64) NOT NULL,
 `systemId` VARCHAR(12) NOT NULL,
 `submitTime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
 `status` VARCHAR(20) NOT NULL,
 `smscId` VARCHAR(64) NOT NULL,
 `smsc` VARCHAR(16) NOT NULL,
 `errcode` INT(6) NULL DEFAULT '0',
 PRIMARY KEY (`msgId`, `submitTime`),
 INDEX `systemId` (`systemId`),
 INDEX `smsc` (`smsc`),
 INDEX `status` (`status`),
 INDEX `idx_time` (`submitTime`)
)
ROW_FORMAT=DEFAULT

所有表都在时间戳字段上进行日期分区

Mysql Server中的全局变量列表

Variable_name  ,  Value  
  auto_increment_increment  ,  1  
  auto_increment_offset  ,  1  
  autocommit  ,  ON  
  automatic_sp_privileges  ,  ON  
  back_log  ,  50  
  basedir  ,  /usr/  
  big_tables  ,  OFF  
  binlog_cache_size  ,  32768  
  binlog_format  ,  STATEMENT  
  bulk_insert_buffer_size  ,  8388608  
  character_set_client  ,  latin1  
  character_set_connection  ,  latin1  
  character_set_database  ,  latin1  
  character_set_filesystem  ,  binary  
  character_set_results  ,  latin1  
  character_set_server  ,  latin1  
  character_set_system  ,  utf8  
  character_sets_dir  ,  /usr/share/mysql/charsets/  
  collation_connection  ,  latin1_swedish_ci  
  collation_database  ,  latin1_swedish_ci  
  collation_server  ,  latin1_swedish_ci  
  completion_type  ,  0  
  concurrent_insert  ,  1  
  connect_timeout  ,  10  
  datadir  ,  /var/lib/mysql/  
  date_format  ,  %Y-%m-%d  
  datetime_format  ,  %Y-%m-%d %H:%i:%s  
  default_week_format  ,  0  
  delay_key_write  ,  ON  
  delayed_insert_limit  ,  100  
  delayed_insert_timeout  ,  300  
  delayed_queue_size  ,  1000  
  div_precision_increment  ,  4  
  engine_condition_pushdown  ,  ON  
  error_count  ,  0  
  event_scheduler  ,  OFF  
  expire_logs_days  ,  10  
  flush  ,  OFF  
  flush_time  ,  0  
  foreign_key_checks  ,  ON  
  ft_boolean_syntax  ,  + -><()~*:        &|  
  ft_max_word_len  ,  84  
  ft_min_word_len  ,  4  
  ft_query_expansion_limit  ,  20  
  ft_stopword_file  ,  (built-in)  
  general_log  ,  OFF  
  general_log_file  ,  /var/run/mysqld/mysqld.log  
  group_concat_max_len  ,  1024  
  have_community_features  ,  YES  
  have_compress  ,  YES  
  have_crypt  ,  YES  
  have_csv  ,  YES  
  have_dynamic_loading  ,  YES  
  have_geometry  ,  YES  
  have_innodb  ,  YES  
  have_ndbcluster  ,  NO  
  have_openssl  ,  DISABLED  
  have_partitioning  ,  YES  
  have_query_cache  ,  YES  
  have_rtree_keys  ,  YES  
  have_ssl  ,  DISABLED  
  have_symlink  ,  YES  
  hostname  ,  smscdb  
  identity  ,  0  
  ignore_builtin_innodb  ,  OFF  
  init_connect  ,    
  init_file  ,    
  init_slave  ,    
  innodb_adaptive_hash_index  ,  ON  
  innodb_additional_mem_pool_size  ,  1048576  
  innodb_autoextend_increment  ,  8  
  innodb_autoinc_lock_mode  ,  1  
  innodb_buffer_pool_size  ,  8388608  
  innodb_checksums  ,  ON  
  innodb_commit_concurrency  ,  0  
  innodb_concurrency_tickets  ,  500  
  innodb_data_file_path  ,  ibdata1:10M:autoextend  
  innodb_data_home_dir  ,    
  innodb_doublewrite  ,  ON  
  innodb_fast_shutdown  ,  1  
  innodb_file_io_threads  ,  4  
  innodb_file_per_table  ,  OFF  
  innodb_flush_log_at_trx_commit  ,  1  
  innodb_flush_method  ,    
  innodb_force_recovery  ,  0  
  innodb_lock_wait_timeout  ,  50  
  innodb_locks_unsafe_for_binlog  ,  OFF  
  innodb_log_buffer_size  ,  1048576  
  innodb_log_file_size  ,  5242880  
  innodb_log_files_in_group  ,  2  
  innodb_log_group_home_dir  ,  ./  
  innodb_max_dirty_pages_pct  ,  90  
  innodb_max_purge_lag  ,  0  
  innodb_mirrored_log_groups  ,  1  
  innodb_open_files  ,  300  
  innodb_rollback_on_timeout  ,  OFF  
  innodb_stats_on_metadata  ,  ON  
  innodb_support_xa  ,  ON  
  innodb_sync_spin_loops  ,  20  
  innodb_table_locks  ,  ON  
  innodb_thread_concurrency  ,  8  
  innodb_thread_sleep_delay  ,  10000  
  innodb_use_legacy_cardinality_algorithm  ,  ON  
  insert_id  ,  0  
  interactive_timeout  ,  28800  
  join_buffer_size  ,  131072  
  keep_files_on_create  ,  OFF  
  key_buffer_size  ,  1073741824  
  key_cache_age_threshold  ,  300  
  key_cache_block_size  ,  1024  
  key_cache_division_limit  ,  100  
  language  ,  /usr/share/mysql/english/  
  large_files_support  ,  ON  
  large_page_size  ,  0  
  large_pages  ,  OFF  
  last_insert_id  ,  0  
  lc_time_names  ,  en_US  
  license  ,  GPL  
  local_infile  ,  ON  
  locked_in_memory  ,  OFF  
  log  ,  OFF  
  log_bin  ,  ON  
  log_bin_trust_function_creators  ,  OFF  
  log_bin_trust_routine_creators  ,  OFF  
  log_error  ,    
  log_output  ,  FILE  
  log_queries_not_using_indexes  ,  OFF  
  log_slave_updates  ,  OFF  
  log_slow_queries  ,  OFF  
  log_warnings  ,  1  
  long_query_time  ,  10.000000  
  low_priority_updates  ,  OFF  
  lower_case_file_system  ,  OFF  
  lower_case_table_names  ,  0  
  max_allowed_packet  ,  536870912  
  max_binlog_cache_size  ,  4294963200  
  max_binlog_size  ,  104857600  
  max_connect_errors  ,  10  
  max_connections  ,  151  
  max_delayed_threads  ,  20  
  max_error_count  ,  64  
  max_heap_table_size  ,  16777216  
  max_insert_delayed_threads  ,  20  
  max_join_size  ,  18446744073709551615  
  max_length_for_sort_data  ,  1024  
  max_prepared_stmt_count  ,  16382  
  max_relay_log_size  ,  0  
  max_seeks_for_key  ,  4294967295  
  max_sort_length  ,  1024  
  max_sp_recursion_depth  ,  0  
  max_tmp_tables  ,  32  
  max_user_connections  ,  0  
  max_write_lock_count  ,  4294967295  
  min_examined_row_limit  ,  0  
  multi_range_count  ,  256  
  myisam_data_pointer_size  ,  6  
  myisam_max_sort_file_size  ,  2146435072  
  myisam_recover_options  ,  BACKUP  
  myisam_repair_threads  ,  1  
  myisam_sort_buffer_size  ,  8388608  
  myisam_stats_method  ,  nulls_unequal  
  myisam_use_mmap  ,  OFF  
  net_buffer_length  ,  16384  
  net_read_timeout  ,  30  
  net_retry_count  ,  10  
  net_write_timeout  ,  60  
  new  ,  OFF  
  old  ,  OFF  
  old_alter_table  ,  OFF  
  old_passwords  ,  OFF  
  open_files_limit  ,  20000  
  optimizer_prune_level  ,  1  
  optimizer_search_depth  ,  62  
  optimizer_switch  ,  index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on  
  pid_file  ,  /var/run/mysqld/mysqld.pid  
  plugin_dir  ,  /usr/lib/mysql/plugin  
  port  ,  3306  
  preload_buffer_size  ,  32768  
  profiling  ,  OFF  
  profiling_history_size  ,  15  
  protocol_version  ,  10  
  pseudo_thread_id  ,  0  
  query_alloc_block_size  ,  8192  
  query_cache_limit  ,  1073741824  
  query_cache_min_res_unit  ,  4096  
  query_cache_size  ,  536870912  
  query_cache_type  ,  ON  
  query_cache_wlock_invalidate  ,  OFF  
  query_prealloc_size  ,  8192  
  rand_seed1  ,    
  rand_seed2  ,    
  range_alloc_block_size  ,  4096  
  read_buffer_size  ,  131072  
  read_only  ,  OFF  
  read_rnd_buffer_size  ,  33554432  
  relay_log  ,    
  relay_log_index  ,    
  relay_log_info_file  ,  relay-log.info  
  relay_log_purge  ,  ON  
  relay_log_space_limit  ,  0  
  report_host  ,    
  report_password  ,    
  report_port  ,  3306  
  report_user  ,    
  rpl_recovery_rank  ,  0  
  secure_auth  ,  OFF  
  secure_file_priv  ,    
  server_id  ,  3  
  skip_external_locking  ,  ON  
  skip_networking  ,  OFF  
  skip_show_database  ,  OFF  
  slave_compressed_protocol  ,  OFF  
  slave_exec_mode  ,  STRICT  
  slave_load_tmpdir  ,  /tmp  
  slave_net_timeout  ,  3600  
  slave_skip_errors  ,  OFF  
  slave_transaction_retries  ,  10  
  slow_launch_time  ,  2  
  slow_query_log  ,  OFF  
  slow_query_log_file  ,  /var/run/mysqld/mysqld-slow.log  
  socket  ,  /var/run/mysqld/mysqld.sock  
  sort_buffer_size  ,  67108864  
  sql_auto_is_null  ,  ON  
  sql_big_selects  ,  ON  
  sql_big_tables  ,  OFF  
  sql_buffer_result  ,  OFF  
  sql_log_bin  ,  ON  
  sql_log_off  ,  OFF  
  sql_log_update  ,  ON  
  sql_low_priority_updates  ,  OFF  
  sql_max_join_size  ,  18446744073709551615  
  sql_mode  ,    
  sql_notes  ,  ON  
  sql_quote_show_create  ,  ON  
  sql_safe_updates  ,  OFF  
  sql_select_limit  ,  18446744073709551615  
  sql_slave_skip_counter  ,    
  sql_warnings  ,  OFF  
  ssl_ca  ,    
  ssl_capath  ,    
  ssl_cert  ,    
  ssl_cipher  ,    
  ssl_key  ,    
  storage_engine  ,  MyISAM  
  sync_binlog  ,  0  
  sync_frm  ,  ON  
  system_time_zone  ,  IST  
  table_definition_cache  ,  256  
  table_lock_wait_timeout  ,  50  
  table_open_cache  ,  500  
  table_type  ,  MyISAM  
  thread_cache_size  ,  8  
  thread_handling  ,  one-thread-per-connection  
  thread_stack  ,  196608  
  time_format  ,  %H:%i:%s  
  time_zone  ,  SYSTEM  
  timed_mutexes  ,  OFF  
  timestamp  ,  1282125419  
  tmp_table_size  ,  16777216  
  tmpdir  ,  /tmp  
  transaction_alloc_block_size  ,  8192  
  transaction_prealloc_size  ,  4096  
  tx_isolation  ,  REPEATABLE-READ  
  unique_checks  ,  ON  
  updatable_views_with_limit  ,  YES  
  version  ,  5.1.37-1ubuntu5-log  
  version_comment  ,  (Ubuntu)  
  version_compile_machine  ,  i486  
  version_compile_os  ,  debian-linux-gnu  
  wait_timeout  ,  28800  
  warning_count  ,  0  

【问题讨论】:

    标签: mysql join


    【解决方案1】:

    您给出的解释计划:

    id , select_type , table , type , possible_keys      , key      , key_len  , ref                 , rows , Extra
    1  , SIMPLE      , a     , ref  , systemId idx_time) , systemId , 14       , const              , 735310 , Using where
    1  , SIMPLE      , b     , ref  , PRIMARY            , PRIMARY  , 66       , gwreports2.a.msgId , 2270405 ,
    1  , SIMPLE      , c     , ref  , PRIMARY            , PRIMARY  , 66       , gwreports2.a.msgId , 2238701 , 
    

    表明您正在击中:735310 * 2270405 * 2238701 = 3T 行!!!!!! 实际上,您没有充分发挥索引的潜力。

    如何解释您的“解释计划”: 对于表“a”(735310)中的每一行,您点击表“b”2270405 次。 对于您在表“b”中点击的每一行,您点击表“c”2238701 次。 如您所见,这是一个指数级问题。

    是的,8MB 的 InnoDb 缓冲区空间很小,但是将您的解释计划降低到 xxxx * 1 * 1 将产生令人难以置信的速度,即使对于 8MB 的缓冲区空间也是如此。

    根据您的查询:

    SELECT a.msgId,a.senderId,a.destination,a.inTime,a.status as InStatus,b.status as SubStatus,c.deliverTime,substr(c.receipt,82,7) as DlvStatus 
      FROM inserted_history a
      LEFT JOIN submitted_history b ON b.msgId = a.msgId  -- USES 1 column of PK
      LEFT JOIN delivered_history c ON a.msgId = c.msgId  -- USES 1 column of PK 
     WHERE a.inTime BETWEEN '2010-08-10 00:00:00' AND '2010-08-010 23:59:59' -- NO key 
       AND a.systemId='ND_arber' -- Uses non-unique PK
    

    以下是我看到的问题: A)您的 _history 表在具有“时间戳”数据类型的列上进行分区,但您不是 JOIN/WHERE 标准中的那些列。引擎必须在没有该信息的情况下命中每个分区。 B)对提交历史和交付历史的访问仅使用 2 列 PK 中的 1 列。您只能从 PK 中获得部分收益。您可以让更多列成为 JOIN 的一部分吗?您必须使为该表找到的行数尽可能接近“1”。
    C) msg​​ID = varchar(64) 这是每个表的 PK 的第一列。你在每张桌子上的钥匙都是 ** HUGE **!!
    - 尽量减少PK的列大小,或使用不同的列。

    您的其他密钥的数据模式表明您有大量的磁盘/内存空间被非 PK 密钥占用。

    问题 1) 每个表的“显示索引来自”(Link) 报告什么? “基数”列将向您展示每个键的实际效果。基数越小,索引的最差/越差。您希望基数尽可能接近“总行数”以获得理想性能。

    问题 2)您能否重构 SQL 以使每个表的 JOIN 列都是该表的基数最高的列?

    问题 3) 'timestamp' 数据类型的列真的是分区的最佳列吗?如果您的访问模式始终使用“msgId”,并且 msgId 是 PK 的第一列,那么。

    问题 4) msg​​Id 是唯一的吗?我的猜测是肯定的,PK的第2列其实没必要。

    阅读优化 SQL (Link) 并获得表的索引基数报告。这是弄清楚如何优化查询的途径。您希望解释计划的“行”为 N * 1 * 1。

    旁注:InnoDb 和 MyISAM 引擎不会自动更新非唯一列的表基数,DBA 需要定期手动运行“分析表”以确保其准确性。

    祝你好运。

    【讨论】:

      【解决方案2】:

      能不能改变inserted_history的索引,

      系统标识(系统标识)

      成为

      systemId (systemId, inTime)。或者添加一个额外的索引

      我的逻辑是,这应该有助于加快对组成连接基础的插入历史 (a) 行的选择。

      where 子句“where a.inTime between '2010-08-10 00:00:00' and '2010-08-010 23:59:59' and a.systemId='ND_arber'”都可以选择按索引。目前,行可以通过 systemId 选择,但是所有这些行都需要扫描。

      作为一个有趣的问题,每个系统 ID 将有(平均)多少条记录。此外,由于 msgid 本身并不是唯一的,其他表中有多少条记录(平均而言)将具有相同的 msgid。

      【讨论】:

        【解决方案3】:

        主要思想

        你在使用 InnoDB 吗?看起来您的缓冲池只有 8MB。这很容易成为问题,您正在处理大量数据而 InnoDB 没有太多内存。你能提高 innodb_buffer_pool_size 吗?您必须重新启动 MySQL,但我敢打赌,即使您只给它 256 或 512MB,这也会产生巨大的影响。

        更新:我看到您的存储引擎和表格格式似乎默认为 MyISAM,因此除非您另行指定,否则这将不适用。我想知道 myisam_sort_buffer_size 是否有帮助?我们不使用 MyISAM,所以我不熟悉调整它。

        随机想法

        我想知道主键是字母数字(尤其是 VARCHAR)是否与此有关。我记得我们在使用非数字主键时遇到了性能问题,但该数据库的历史可以追溯到 4.0 或 4.1,所以这可能不适用(或者曾经是真的)。​​

        次要想法

        在上面的内存问题之后,我最好的猜测是给 MySQL 更多的提示。当我有一个运行缓慢的查询时,我经常发现向它提供更多信息会有所帮助。您在每个表上都有 messageId/time 索引。也许更像这样的东西会更好:

        select a.msgId,a.senderId,a.destination,a.inTime,a.status as InStatus,
        b.status as SubStatus,c.deliverTime,substr(c.receipt,82,7) as DlvStatus
        from inserted_history a left join submitted_history b on b.msgId = a.msgId
        left join delivered_history c on a.msgId = c.msgId
        where a.inTime between '2010-08-10 00:00:00' and '2010-08-010 23:59:59'
        and a.systemId='ND_arber' AND c.inTime between b.inTime >= a.inTime
        and c.inTime >= b.inTime
        

        我猜东西会被插入 A,然后是 B,然后是 C。如果你有更好的限制(比如当有东西进入 A 时,它总是会在一天内发送出去并提交)添加这些信息可能会有所帮助。

        我对此感到疑惑,因为我已经看到它在某些情况下有助于提高查询性能,还因为您在日期时间对数据进行了分区。这可能对优化器有所帮助。

        我的另一个建议是在短时间内运行您的查询,例如 10 分钟而不是一整天,并确保结果正确。然后尝试 30。增加它,看看它什么时候会掉到“明天回来”的范围内。这可能会告诉你一些事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-09
          • 2018-02-07
          • 2018-10-06
          • 2015-03-16
          • 1970-01-01
          • 2012-05-14
          • 2015-02-22
          • 2012-09-05
          相关资源
          最近更新 更多