mysql - 优化mysql慢查询

标签 mysql indexing subquery query-optimization

添加到数据库的记录越多,查询就越慢。从 1 秒。现在几秒钟​​,结果网页加载时间很长

CREATE TABLE `ads` (
     `id` int(11) NOT NULL AUTO_INCREMENT,
     `user_id` int(11) NOT NULL,
     `user_status` enum('register','unregister') COLLATE latin1_general_ci NOT NULL DEFAULT 'register',
     `title` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `tags` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `ad_type` enum('offer','want') COLLATE latin1_general_ci NOT NULL,
     `price` float NOT NULL,
     `image` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `address` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `google_address` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `country_id` int(11) NOT NULL,
     `state_id` int(11) NOT NULL,
     `address2` text COLLATE latin1_general_ci NOT NULL,
     `city` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `location` int(11) NOT NULL,
     `postal_code` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `Latitude` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `Longitude` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `working_remote` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `emergency_service` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `ad_description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
     `cat_id` int(11) NOT NULL,
     `sub_cat_id` int(11) NOT NULL,
     `sub_sub_cat_id` int(11) NOT NULL,
     `status` enum('0','1') COLLATE latin1_general_ci NOT NULL,
     `delete_status` enum('0','1') COLLATE latin1_general_ci NOT NULL DEFAULT '0',
     `publication_days` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `publication_total` float(11,2) NOT NULL,
     `added_date` datetime NOT NULL,
     `expiry_date` datetime NOT NULL,
     `payment_status` enum('pending','paid','cancel') COLLATE latin1_general_ci NOT NULL,
     `closed_date` datetime NOT NULL,
     `deleted_date` datetime NOT NULL,
     `ad_status` enum('active','closed') COLLATE latin1_general_ci NOT NULL DEFAULT 'active',
     `user_first_name` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `user_last_name` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `user_phone_number` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `user_email_id` varchar(255) COLLATE latin1_general_ci NOT NULL,
     `ads_extend_date` datetime NOT NULL,
     `ads_extend_expiry_date` datetime NOT NULL,
     `ads_extend_status` enum('yes','no') COLLATE latin1_general_ci NOT NULL DEFAULT 'no',
     `actvation_notification` enum('yes','no') COLLATE latin1_general_ci NOT NULL DEFAULT 'no',
     `ads_view_count` int(11) NOT NULL,
     `md5_key` varchar(100) COLLATE latin1_general_ci NOT NULL,
     PRIMARY KEY (`id`),
     KEY `user_id` (`user_id`,`user_status`,`title`,`ad_type`,`price`),
     KEY `title` (`title`),
     KEY `ad_type` (`ad_type`),
     KEY `price` (`price`),
     KEY `google_address` (`google_address`),
     KEY `country_id` (`country_id`),
     KEY `state_id` (`state_id`),
     KEY `city` (`city`),
     KEY `postal_code` (`postal_code`),
     KEY `cat_id` (`cat_id`),
     KEY `sub_cat_id` (`sub_cat_id`),
     KEY `sub_sub_cat_id` (`sub_sub_cat_id`),
     KEY `status` (`status`),
     KEY `payment_status` (`payment_status`),
     KEY `ad_status` (`ad_status`),
     KEY `added_date` (`added_date`),
     KEY `expiry_date` (`expiry_date`),
     KEY `id_2` (`id`,`user_id`,`user_status`,`title`,`ad_type`,`country_id`,`state_id`,`city`,`postal_code`,`cat_id`,`sub_cat_id`,`sub_sub_cat_id`,`added_date`,`expiry_date`,`payment_status`,`ad_status`)
    ) ENGINE=MyISAM AUTO_INCREMENT=1208 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci

慢查询日志

# Query_time: 3.859838  Lock_time: 0.000368 Rows_sent: 340  Rows_examined: 1248768
SET timestamp=1448331158;

SELECT ads.id,ads.user_id, ads.user_status, ads.title, ads.ad_type,ads.price, ads.address, ads.google_address, ads.state_id, ads.address2, ads.city as city_id, ads. location as location_id, ads.postal_code, ads.Latitude, ads.Longitude, ads.working_remote, ads.emergency_service, ads.ad_description, ads.cat_id, ads. sub_cat_id, ads.sub_sub_cat_id, ads.status, ads.publication_total, ads.ads_view_count, ads.added_date,cat.category_name, sub_cat.category_name as sub_category_name, sub_sub_cat.category_name as sub_sub_category_name, usr.id as user_id, usr.username as user_name, usr.first_name as first_name, usr.rating as rating, adimg.thumbnail, state.state,state.state_abbr, city.city, location.location as locationname,
       (SELECT added_date 
        FROM ads_publication as pub 
             WHERE pub.ad_id = ads.id 
             AND pub.publication_id != '0' 
             ORDER BY pub.sort_type ASC LIMIT 0,1) as publication_srt_id,
   SQRT((((69.1*(ads.Latitude -(0)))*(69.1*(ads.Latitude -(0))))+((53*(ads.Longitude -(0)))*(53*(ads.Longitude -(0)))))) as dist_in_miles
   FROM ads as ads      
     LEFT JOIN ads_images as adimg ON (ads.id = adimg.ad_id AND default_image = '1')
     LEFT JOIN workrange as wr ON ads.user_id = wr.user_id 
     LEFT JOIN users as usr ON ads.user_id = usr.id 
     LEFT JOIN ads_service as price_list ON ads.id = price_list.ad_id   
     LEFT JOIN ads_publication as promot ON ads.id = promot.ad_id           
     LEFT JOIN user_languages as language ON ads.id = language.ad_id
     LEFT JOIN categories as cat ON (ads.cat_id = cat.id AND cat.parent_category_id = 0)
     LEFT JOIN categories as sub_cat ON ads.sub_cat_id = sub_cat.id
     LEFT JOIN categories as sub_sub_cat ON ads.sub_sub_cat_id = sub_cat.id
     LEFT JOIN location as state ON ads.state_id = state.locationId
     LEFT JOIN location as city ON ads.city = city.locationId
     LEFT JOIN location as location ON ads.location = location.locationId   
   WHERE  ads.status = '1' 
   AND ads.payment_status = 'paid' 
   AND ads.delete_status = '0' 
   AND ads.expiry_date >= '2015-11-23 21:12:38'
   AND ads.ad_status = 'active' 
   AND ads.ad_type = 'offer' 
   GROUP BY ads.id  
   ORDER BY ads.user_status ASC, publication_srt_id DESC, ads.added_date DESC;

解释

id | select_type        |table          | type        | possible_keys                                           | key                 | key_len | ref                       | rows | Extra  
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1  | PRIMARY            | ads           | index_merge | "ad_type,status,payment_status,ad_status,expiry_dat..." | "status,ad_status"  | "1,1"   | NULL                      | 173  | "Using intersect(status,ad_status); Using where; Us..."    
1  | PRIMARY            | adimg         | index       | NULL                                                    | id                  | 526     | NULL                      | 1398 | Using index    
1  | PRIMARY            | wr            | ALL         | NULL                                                    | NULL                | NULL    | NULL                      | 75   |        
1  | PRIMARY            | usr           | eq_ref      | PRIMARY                                                 | PRIMARY             | 4       | serv_co_za.ads.user_id    | 1    |    
1  | PRIMARY            | price_list    | ALL         | NULL                                                    | NULL                | NULL    | NULL                      | 57   |    
1  | PRIMARY            | promot        | ref         | "ad_id,ad_id_2,ad_id_3"                                 | ad_id_3             | 4       | serv_co_za.ads.id         | 11   | Using index    
1  | PRIMARY            | language      | ALL         | NULL                                                    | NULL                | NULL    | NULL                      | 393  |        
1  | PRIMARY            | cat           | eq_ref      | "PRIMARY,id"                                            | PRIMARY             | 4       | serv_co_za.ads.cat_id     | 1    |    
1  | PRIMARY            | sub_cat       | eq_ref      | "PRIMARY,id"                                            | PRIMARY             | 4       | serv_co_za.ads.sub_cat_id | 1    |    
1  | PRIMARY            | state         | eq_ref      | PRIMARY                                                 | PRIMARY             | 4       | serv_co_za.ads.state_id   | 1    |        
1  | PRIMARY            | city          | eq_ref      | PRIMARY                                                 | PRIMARY             | 4       | serv_co_za.ads.city       | 1    |        
1  | PRIMARY            | location      | eq_ref      | PRIMARY                                                 | PRIMARY             | 4       | serv_co_za.ads.location   | 1    |        
1  | PRIMARY            | sub_sub_cat   | index       | NULL                                                    | id                  | 111     | NULL                      | 1193 | Using index    
2  | DEPENDENT SUBQUERY | pub           | ref         | "ad_id,ad_id_2,ad_id_3"                                 | ad_id               | 4       | func                      | 115  | Using where; Using filesort

配置:

key_buffer_size 33554432
max_allowed_packet 268435456
query_cache_limit 1048576
query_cache_min_res_unit 4096
query_cache_size 33554432
myisam_sort_buffer_size 16777216
sort_buffer_size 524288
thread_cache_size 4
thread_concurrency 10
interactive_timeout 28800
wait_timeout 28800

我注意到当这部分从查询的最后删除时

GROUP BY ads.id ORDER BY ads.user_status ASC, publication_srt_id DESC, ads.added_date DESC;

查询时间约为 0.06 秒。

非常感谢任何帮助或起点。

先谢谢你, 德里克

最佳答案

使用 intersect(status,ad_status) -- 复合索引总是胜过它。所以添加 INDEX(status, ad_status)。假设这些列只是标志,去掉它们上的各个索引。 (摆脱其他状态字段上的其他简单索引。)

WHERE  ads.status = '1'
  AND ads.payment_status = 'paid'
  AND ads.delete_status = '0'
  AND ads.expiry_date >= '2015-11-23 21:12:38'
  AND ads.ad_status = 'active'
  AND ads.ad_type = 'offer'

对于WHERE,这样更好:

INDEX(status, payment_status, delete_status, ad_status, at_type,  -- in any order
      expiry_date)  -- deliberately last

这将使第一步更有效率。 Index Cookbook解释我是如何得到的。

删除 LEFT 除非“右”表确实是可选的。这可以让优化器在评估查询时有更多选择。

wrprice_listlanguage 需要扫描ALL 行。让我们找出原因。他们分别需要 user_idad_idad_id 的索引。并且数据类型必须与您要比较的内容相匹配。

不要在 FLOATDOUBLE 中使用 (M,N)(例如,float(11,2)) ,它会导致额外的舍入,这可能会导致意外。对于货币,切换到 DECIMAL(11,2)(或类似的)。

不要将 VARCHAR 用于连续的数字值,例如 Latitude 和 Longitude。 FLOATDOUBLE 都不错。

考虑迁移到 InnoDB。 MyISAM 正在消亡。

DROP INDEX id_2 -- 它可能毫无用处。

查询越来越慢的可能原因:

  • wrprice_listlanguage 越来越大。索引应该可以解决这个问题。
  • MyISAM 涉及表锁。
  • key_buffer_size 应设置为可用内存的 20% 左右。随着表的增长,key_buffer 可能会发生抖动。 (注意:InnoDB 需要不同的设置。)

((编辑))

  • 由于 Latitude 需要转换以进行表达式计算,因此使用一些数字数据类型更为重要。
  • pub 需要 INDEX(ads_id, sort_type)
  • publication_id 可能会妨碍上述 INDEX;你能摆脱考试吗?

关于mysql - 优化mysql慢查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33990479/

相关文章:

在 Lucene 中为包含多个数字的文本建立索引

sql-server - 获取给定日期之前的最大日期的有效方法

mysql - SQL:查找商店的唯一客户时出现问题

mysql - mysql中如何显示表的字段?

PHP for 分页时出现循环错误

Mysql索引没有被正确使用

mysql - 将 mysql 子查询转换为 codeigniter

php - CakePHP 3 属于多个具有默认值的列

php - 堆栈空间不足错误

performance - Elasticsearch 中的索引别名和通配符索引端点是否完全相同?