{"id":46,"date":"2008-01-21T19:37:52","date_gmt":"2008-01-22T02:37:52","guid":{"rendered":"http:\/\/codefork.com\/blog\/index.php\/2008\/01\/21\/optimizing-mysql\/"},"modified":"2008-01-21T19:43:18","modified_gmt":"2008-01-22T02:43:18","slug":"optimizing-mysql","status":"publish","type":"post","link":"https:\/\/codefork.com\/blog\/index.php\/2008\/01\/21\/optimizing-mysql\/","title":{"rendered":"The Inexact Science of Optimizing MySQL"},"content":{"rendered":"<p>The past week or so, I&#8217;ve been playing database administrator: monitoring load, examining the log for slow queries, tweaking parameters, rewriting queries, adding indexes, and repeating the cycle over and over again. It&#8217;s a tedious, time-consuming, and exhausting process.<\/p>\n<p>Tuning is tricky because inefficiencies aren&#8217;t always immediately apparent, and there may be uncontrollable factors affecting performance in VPS environments. Here are some things I&#8217;ve learned or had to refresh myself about with respect to tuning MySQL.<\/p>\n<p><b>Don&#8217;t assume anything about why queries are slow.<\/b> Under high load, a lot of queries can pop up in the slow query log just because the database is working hard. It doesn&#8217;t necessarily mean every query needs optimization. Try to look for patterns, and expect to spend time reviewing queries that are already optimized.<\/p>\n<p><b>Look for strange queries.<\/b> Don&#8217;t recognize it? Well, maybe it shouldn&#8217;t be there. I found an expensive query for a website feature that was obsolete months ago; it was being made even though the data wasn&#8217;t being used or displayed anywhere.<\/p>\n<p><b>EXPLAIN is your friend.<\/b> Interpreting the output makes my head pound sometimes, but it&#8217;s absolutely necessary. Invest the time and effort to understand it.<\/p>\n<p><b>Multi-column indexes are a necessary evil.<\/b> Most tables I was examining had only single-column indexes, causing complex WHERE clauses and\/or ORDER BY to do costly &#8220;Using temporary; Using filesort&#8221; operations. Adding a few multi-column indexes helped a great deal. In general, I dislike multi-column indexes because they require maintenance: if you modify a complex query, you might have to add or change the index, or it will become slow as molasses. But unfortunately, that&#8217;s the tradeoff for performance.<\/p>\n<p><b>The &#8220;same&#8221; query can behave differently with different datasets.<\/b> A query can sometimes use an index, and sometimes use a filesort, depending, for example, on constants in the WHERE clause. Be sure to use EXPLAIN with different values.<\/p>\n<p><b>Give the query optimizer hints.<\/b> Disk access is slow, especially on the VPS, so I wanted to avoid filesorts at all costs. Using STRAIGHT_JOIN forced MySQL to process an indexed table first; otherwise, it believed that a filesort would be faster in some cases. It might be, sometimes&#8211;but if you think you know better, use the hint.<\/p>\n<p><b>Disk access on a VPS can be wonky.<\/b> It depends a lot on what else is happening on the hardware node. This relates somewhat to the previous point: all other things being equal, MySQL could be right about a filesort being faster than an index. But it&#8217;s usually not the case in an environment where disk I\/O is very costly.<\/p>\n<p><b>Reduce the number of joins wherever possible.<\/b> It doesn&#8217;t seem like a few indexed joins into &#8220;lookup&#8221; tables (basically key-value types of things) would affect performance that much, but amazingly, it can. Really. Break up queries into smaller, simpler ones if at all possible.<\/p>\n<p><b>Run ANALYZE TABLE and OPTIMIZE TABLE.<\/b> The manual says you don&#8217;t typically need to run these commands, but I found frequently updated tables benefit from this maintenance, even if they have only a few thousand records.<\/p>\n<p><b>Work iteratively.<\/b> Somtimes performance problems are localized to one or two spots, but more often, inefficiencies are spread out over a lot of queries; they may become noticeable only after several passes, as server conditions fluctuate. Discovering these problem areas takes a lot of patience.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The past week or so, I&#8217;ve been playing database administrator: monitoring load, examining the log for slow queries, tweaking parameters, rewriting queries, adding indexes, and repeating the cycle over and over again. It&#8217;s a tedious, time-consuming, and exhausting process. Tuning is tricky because inefficiencies aren&#8217;t always immediately apparent, and there may be uncontrollable factors affecting &hellip; <a href=\"https:\/\/codefork.com\/blog\/index.php\/2008\/01\/21\/optimizing-mysql\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;The Inexact Science of Optimizing MySQL&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-46","post","type-post","status-publish","format-standard","hentry","category-admin"],"_links":{"self":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/46","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=46"}],"version-history":[{"count":0,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/46\/revisions"}],"wp:attachment":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=46"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=46"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=46"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}