简体   繁体   中英

How to improve performance of mysql query?

I am updating a number of rows in a table. The query takes long time to execute. How can I improve the execution performance of below query?

update org_products op
inner join access_history
    on access_history.access_key = op.id and access_history.access_type = "OrgProduct.View"
set views =
(
    select count(access_key)
    from access_history
    where access_history.access_key = op.id and
          access_history.created_at >= DATE_SUB(CURDATE(), INTERVAL 90 DAY) and
          access_history.access_type = "OrgProduct.View" and
          access_history.product_id = op.product_id
    GROUP BY  access_history.product_id
)
where access_history.access_key = op.id and
      access_history.access_type = "OrgProduct.View";

Update: Output of SHOW CREATE TABLE access_history;

 'access_history', 'CREATE TABLE `access_history` (
\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,
\n  `product_id` bigint(20) unsigned NOT NULL,
\n  `access_type` varchar(50) DEFAULT NULL,
\n  `access_key` varchar(50) DEFAULT NULL,
\n  `access_key_full` varchar(200) DEFAULT NULL,
\n  `client_ip_addr` varbinary(16) DEFAULT NULL,
\n  `userid` bigint(20) unsigned DEFAULT NULL,\n  `username` tinytext,
\n  `anon_user_id` bigint(20) unsigned DEFAULT NULL,
\n  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\n  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),
\n  KEY `fk_access_history_has_product_product1_idx` (`product_id`),
\n  KEY `idx_access_history_prod_type_key` (`product_id`,`access_type`,`access_key`),
\n  CONSTRAINT `fk_access_history_has_product_product1_idx` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON 
DELETE NO ACTION ON UPDATE NO ACTION\n) ENGINE=InnoDB AUTO_INCREMENT=1290353 DEFAULT CHARSET=utf8'

Output of SHOW INDEX from access_history; 在此输入图像描述

Output of EXPLAIN query 在此输入图像描述

What changes in the query are required to increase the performance?

Appreciate any help! Thanks!

There are 2 variants here:

1) you execute the discussed query periodically by CRON - so you want to update all products whose access history was changed within the last 90 days. Try the following query (you'd better compute the starting date in advance and then use this constant value - using DATE_SUB() will make MySQL to think this is a dynamic value and will compute it each time, will not notice that it is essentially a constant):

UPDATE org_products op
  SET views = (SELECT COUNT(*) FROM access_history
        WHERE access_key = op.id
            AND access_type = "OrgProduct.View"
    )
    WHERE op.id IN (SELECT access_key FROM access_history 
        WHERE access_type = "OrgProduct.View"
            AND created_at >= "YYYY-MM-DD"
    )

2) you execute the discussed query after each insert to table access_history - try the following (ACCESS_KEY_VALUE is what you have just inserted into table access_history ):

UPDATE org_products op
    SET views = (SELECT COUNT(*) FROM access_history
        WHERE access_key = ACCESS_KEY_VALUE
            AND access_type = "OrgProduct.View"
    )
    WHERE op.id = ACCESS_KEY_VALUE

In both cases, I recommend you to move all distinct strings from column access_type into separate table, convert the column into INT and make it a foreign key into that new table. The reason is that indexing and comparing numeric values is faster than comparing strings.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM