{"id":66094,"date":"2024-09-20T11:00:47","date_gmt":"2024-09-20T05:30:47","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=66094"},"modified":"2024-09-24T15:11:56","modified_gmt":"2024-09-24T09:41:56","slug":"mysql-performance-tuning","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/mysql-performance-tuning\/","title":{"rendered":"MySQL | Performance Tuning"},"content":{"rendered":"<h3>Performance Tuning<\/h3>\n<p style=\"text-align: left;\">As our data grows in the database, it is always important to view database insights. We\u2019ve been using some complex queries to solve difficult problems without checking how they affect the server performance.<\/p>\n<p><em>It&#8217;s always preferred to frequently visit the performance schema.<\/em><\/p>\n<blockquote><p><code>USE performance_schema;<\/code><\/p><\/blockquote>\n<h3 style=\"text-align: left;\">Where to Begin With<\/h3>\n<p style=\"text-align: left;\">We want to boost cluster performance but aren\u2019t sure where to begin.<\/p>\n<ul style=\"text-align: left;\">\n<li style=\"list-style-type: none;\">\n<ul>\n<li><a href=\"#slowQueries\">Why Queries are Slow<\/a><\/li>\n<li><a href=\"#Top10Queries\">Top 10 Time-Consuming Queries<\/a><\/li>\n<li><a href=\"#ExplainAnalyze\">EXPLAIN ANALYZE<\/a><\/li>\n<li><a href=\"#CandidateQuery\">Finding Candidate Queries for Optimisation<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<div id=\"attachment_66091\" style=\"width: 635px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-66091\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-66091 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL-1024x489.jpeg\" alt=\"SQL Layer\" width=\"625\" height=\"298\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL-1024x489.jpeg 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL-300x143.jpeg 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL-768x367.jpeg 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL-624x298.jpeg 624w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SQL.jpeg 1280w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-66091\" class=\"wp-caption-text\">SQL Layer<\/p><\/div>\n<div style=\"height: 50px;\"><\/div>\n<h3 id=\"slowQueries\"><strong>Why Queries are Slow<\/strong><\/h3>\n<div class=\"mceTemp\"><\/div>\n<ul style=\"text-align: left;\">\n<li>Queries are tasks, and they are composed of subtasks, and those subtasks consume time.\n<ul>\n<li>To optimize the query, we must optimize the subtasks by eliminating them, making them happen fewer times, or making them happen more quickly.<\/li>\n<li>Talking about the Query life cycle, it\u2019s first parsed, then planned, executed, and finally sent back to the client.<\/li>\n<li>It involves a lot of calls to the storage engine to retrieve rows and post-retrieval executions such as grouping and sorting.<\/li>\n<\/ul>\n<\/li>\n<li>While accomplishing the above tasks, the query spends time on the network in the CPU on operations such as statistics, locking, and most importantly, calls to the storage engine to retrieve rows.<\/li>\n<li>This call consumes time in the memory and the CPU, and especially I\/O, if the data is not in memory. Excessive time is consumed because transactions are performed needlessly and too many times, so our goal is to eliminate and reduce the operation by making them faster.<\/li>\n<\/ul>\n<h3 id=\"Top10Queries\" style=\"text-align: left;\"><strong>Top 10 Time-Consuming Queries<\/strong><\/h3>\n<p style=\"padding-left: 40px;\">To optimize the DB, we don&#8217;t really need to find the query that is taking the maximum time, but we have to find the query that is taking more time and that is getting called more frequently.<\/p>\n<blockquote style=\"padding-left: 40px;\">\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><code>SELECT (100 * SUM_TIMER_WAIT \/ sum(SUM_TIMER_WAIT) OVER ()) percent, SUM_TIMER_WAIT AS total, COUNT_STAR AS calls, AVG_TIMER_WAIT AS mean, substring(DIGEST_TEXT, 1, 75) FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/blockquote>\n<blockquote>\n<table class=\" alignleft\" style=\"width: 100%;\">\n<tbody>\n<tr style=\"height: 44px;\">\n<td style=\"width: 14.4835%; height: 44px; text-align: center;\"><strong>Projection<\/strong><\/td>\n<td style=\"width: 15.5332%; height: 44px; text-align: center;\"><strong>Field Name<\/strong><\/td>\n<td style=\"width: 31.2178%; height: 44px; text-align: center;\"><strong>Description<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 219px;\">\n<td style=\"width: 14.4835%; height: 219px; text-align: center;\">\u00a0 \u00a0 total<\/td>\n<td style=\"width: 15.5332%; height: 219px; text-align: center;\">\u00a0 SUM_TIMER_WAIT<\/td>\n<td style=\"width: 31.2178%; height: 219px; text-align: center;\">Number of times a query has been executed (COUNT_STAR)<\/p>\n<p>multiplied by<\/p>\n<p>The average execution time. (AVG_TIMER_WAIT)<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 14.4835%; height: 24px; text-align: center;\">\u00a0 \u00a0 mean<\/td>\n<td style=\"width: 15.5332%; height: 24px; text-align: center;\">\u00a0 AVG_TIMER_WAIT<\/td>\n<td style=\"width: 31.2178%; height: 24px; text-align: center;\">\u00a0 Average Response Time<\/td>\n<\/tr>\n<tr style=\"height: 48px;\">\n<td style=\"width: 14.4835%; height: 48px; text-align: center;\">\u00a0 \u00a0 substring(DIGEST_TEXT, 1, 75)<\/td>\n<td style=\"width: 15.5332%; height: 48px; text-align: center;\">\u00a0 Query<\/td>\n<td style=\"width: 31.2178%; height: 48px; text-align: center;\">\u00a0 Query<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/blockquote>\n<p>&nbsp;<\/p>\n<h3><\/h3>\n<h3 style=\"text-align: left;\">EXPLAIN<\/h3>\n<p style=\"text-align: left;\">List of things expected to happen when the query is executed (list of subtasks).<\/p>\n<div id=\"attachment_66092\" style=\"width: 635px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-66092\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-66092 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN-1024x534.jpeg\" alt=\"EXLAIN\" width=\"625\" height=\"326\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN-1024x534.jpeg 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN-300x157.jpeg 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN-768x401.jpeg 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN-624x326.jpeg 624w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/EXPLAIN.jpeg 1280w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-66092\" class=\"wp-caption-text\">EXPLAIN<\/p><\/div>\n<h3 id=\"ExplainAnalyze\" style=\"text-align: left;\">EXPLAIN ANALYZE<\/h3>\n<p style=\"text-align: left;\">You will get both the estimation of what the planner expected and what happened when the query ran.<\/p>\n<blockquote><p>EXPLAIN DELETE from the table won&#8217;t delete the actual rows; therefore, it is safe to get the query plan, but EXPLAIN ANALYZE is not just going to show the query plan; it is going to delete those rows.<\/p><\/blockquote>\n<h3 style=\"text-align: left;\"><strong>Understanding how to ANALYZE a query<\/strong><\/h3>\n<p style=\"text-align: left;\">If we execute the query with EXPLAIN ANALYZE, we will seek all the sub tasks, executing that query. We have to observe which sub task has the biggest jump in the execution time and see if it is justified.<\/p>\n<p style=\"text-align: left;\">The red flag in EXPLAIN ANALYZE is a large difference between the estimated number of rows and the actual number of rows.<\/p>\n<h3 id=\"CandidateQuery\" style=\"text-align: left;\">Finding Candidate Queries for the Optimization<\/h3>\n<ul style=\"text-align: left;\">\n<li>Large Number of SUM_ROWS_EXAMINED, in comparison to SUM_ROWS_SENT\n<ul>\n<li>This may suggest poor index usage since a large number of rows are sent to the client but discarded afterward.<\/li>\n<li>If the number of full joins is high, this suggests that either an index is needed or there is a joint condition missing. A full table scan will occur when there is no index for the joint condition and when there is no join condition.\n<ul>\n<li><code>SELECT * FROM sys.statements_with_full_table_scans ORDER BY no_index_used_count DESC\\G<\/code><\/li>\n<\/ul>\n<\/li>\n<li>\n<p><div id=\"attachment_66093\" style=\"width: 635px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-66093\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-66093 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT-1024x562.jpeg\" alt=\"SUM_ROWS_SENT\" width=\"625\" height=\"343\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT-1024x562.jpeg 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT-300x165.jpeg 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT-768x421.jpeg 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT-624x342.jpeg 624w, \/blog\/wp-ttn-blog\/uploads\/2024\/09\/SUM_ROWS_SENT.jpeg 1280w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-66093\" class=\"wp-caption-text\">SUM_ROWS_SENT<\/p><\/div><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul style=\"text-align: left;\">\n<li>If the number of full joins is high, this suggests that either an index is needed or there is a joint condition missing. A full table scan will occur when there is no index for the joint condition and when there is no join condition.\n<ul>\n<li>\n<blockquote><p><code>SELECT * FROM sys.statements_with_full_table_scans ORDER BY no_index_used_count DESC\\G<\/code><\/p><\/blockquote>\n<\/li>\n<\/ul>\n<\/li>\n<li>When the number of SUM_SELECT_RANGE_CHECK is high, this may suggest that we need to change the indexes on the tables.<\/li>\n<li>If the number of internal temporary tables created on disk is high, we need to consider which indexes are used for sorting and grouping and the amount of memory allowed for internal temporary tables.<\/li>\n<li>Writing on disk is still more expensive than internal memory. If you see that the number SUM_SORT_MERGE_PASSES is high for a query (at least as the number of times the query was executed), then increase the sort_buffer_size value only for the sessions that need a larger size.<\/li>\n<\/ul>\n<blockquote>\n<h3><strong>Check Index Sizes<\/strong><\/h3>\n<ul>\n<li><code>SELECT TABLE_NAME AS `Table`, ROUND((DATA_LENGTH + INDEX_LENGTH) \/ 1024 \/ 1024) AS `Size (MB)` FROM information_schema.TABLES WHERE TABLE_SCHEMA = \"test\" ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;<\/code><\/li>\n<\/ul>\n<\/blockquote>\n<h3 style=\"text-align: left;\"><strong>Some interesting facts that are good to knowledge about<\/strong><\/h3>\n<ul style=\"text-align: left;\">\n<li>MySQL&#8217;s design separates query processing and other server tasks from data storage and retrieval.<\/li>\n<li>When we fetch rows from MySQL when using a library, by default the rows are buffered in the library&#8217;s memory and then fetched from there.<\/li>\n<li>The optimizer does not care what storage engine a particular table uses. Still, the storage engine does affect how the server optimizes the query. For example, the optimizer asks the storage engine about some of its capabilities and the cost of certain operations, and for statistics on the table data.<\/li>\n<\/ul>\n<h2 style=\"text-align: left;\"><\/h2>\n<h3 style=\"text-align: left;\"><strong>Queries that are creating deadlocks<\/strong><\/h3>\n<blockquote>\n<ul>\n<li><code>SELECT * FROM performance_schema.events_errors_summary_by_account_by_error WHERE error_name = 'ER_LOCK_DEADLOCK' \\G<\/code><\/li>\n<\/ul>\n<\/blockquote>\n<h3 style=\"text-align: left;\">Conclusion<\/h3>\n<blockquote>\n<p style=\"text-align: left;\">events_statements_summary_by_digest in performance_schema is the gold mine when looking for queries that may have performance issues.<\/p>\n<p style=\"text-align: left;\">Other than performance, we should also consider queries that cause errors and lock time out.<\/p>\n<\/blockquote>\n<h3><strong>References<\/strong><\/h3>\n<ul>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-perfschema-excerpt\/8.0\/en\/performance-schema.html\">https:\/\/dev.mysql.com\/doc\/mysql-perfschema-excerpt\/8.0\/en\/performance-schema.html<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Performance Tuning As our data grows in the database, it is always important to view database insights. We\u2019ve been using some complex queries to solve difficult problems without checking how they affect the server performance. It&#8217;s always preferred to frequently visit the performance schema. USE performance_schema; Where to Begin With We want to boost cluster [&hellip;]<\/p>\n","protected":false},"author":1967,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":69},"categories":[5872],"tags":[76,6558],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/66094"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1967"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=66094"}],"version-history":[{"count":19,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/66094\/revisions"}],"predecessor-version":[{"id":67455,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/66094\/revisions\/67455"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=66094"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=66094"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=66094"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}