Beware of Joins on Derived Tables
MySQL is a powerful tool but it’s also a great way to shoot yourself in the foot. The other day we received reports of people finding a specific page slow but it was only slow when multiple people where using it.
Using New Relic I was able to determine that on the page in question had a query like the one below which took between 1 and 10 seconds to run. As the number of requests to the page increased so did the amount of time to run the query.
When we do an explain on this query we get:
+----+-------------+------------+------------+------+---------------+-------------+---------+----------------------+---------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+-------------+---------+----------------------+---------+----------+----------------------------------------------+ | 1 | PRIMARY | a | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where | | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key1> | <auto_key1> | 4 | example.a.templateId | 3874 | 33.33 | Using where | | 2 | DERIVED | b | NULL | ALL | NULL | NULL | NULL | NULL | 2092640 | 11.11 | Using where; Using temporary; Using filesort | +----+-------------+------------+------------+------+---------------+-------------+---------+----------------------+---------+----------+----------------------------------------------+
What we’re looking at here means MySQL is creating a derived table (line 3) and is looking at all 2092640 rows (because no index is able to restrict it) and then those results are returned and filtered based on the templateId (row 2). It’s also doing a filesort because the results are so large which is a huge performance hit.
To fix this problem the query had to be changed so it uses a subquery:
And our explain:
+----+--------------------+-------+------------+------+---------------+------------+---------+----------------------+--------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+-------+------------+------+---------------+------------+---------+----------------------+--------+----------+------------------------------------+ | 1 | PRIMARY | a | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where | | 2 | DEPENDENT SUBQUERY | b | NULL | ref | templateId | templateId | 4 | example.a.templateId | 523160 | 11.11 | Using index condition; Using where | +----+--------------------+-------+------------+------+---------------+------------+---------+----------------------+--------+----------+------------------------------------+
The reason we were seeing decreasing response time with more users is because each page load cause the system to run this query which took several seconds. During this time another page load would occur which would have to wait for the original to finish and then a third page load would occur again slowing down the process.
Scott Keck-Warren
Scott is the Director of Technology at WeCare Connect where he strives to provide solutions for his customers needs. He's the father of two and can be found most weekends working on projects around the house with his loving partner.
Top Posts
- Working With Soft Deletes in Laravel (By Example)
- Fixing CMake was unable to find a build program corresponding to "Unix Makefiles"
- Upgrading to Laravel 8.x
- Get The Count of the Number of Users in an AD Group
- Multiple Vagrant VMs in One Vagrantfile
- Fixing the "this is larger than GitHub's recommended maximum file size of 50.00 MB" error
- Changing the Directory Vagrant Stores the VMs In
- Accepting Android SDK Licenses From The OSX Command Line
- Fixing the 'Target class [config] does not exist' Error
- Using Rectangle to Manage MacOS Windows