1 /* 2 Copyright (c) 2015 MariaDB Corporation Ab 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; version 2 of the License. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License 14 along with this program; if not, write to the Free Software 15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ 16 17 /* 18 19 == ANALYZE-stmt classes == 20 21 This file contains classes for supporting "ANALYZE statement" feature. These are 22 a set of data structures that can be used to store the data about how the 23 statement executed. 24 25 There are two kinds of data collection: 26 27 1. Various counters. We assume that incrementing counters has very low 28 overhead. Because of that, execution code increments counters unconditionally 29 (even when not running "ANALYZE $statement" commands. You run regular SELECT/ 30 UPDATE/DELETE/etc and the counters are incremented). 31 32 As a free bonus, this lets us print detailed information into the slow query 33 log, should the query be slow. 34 35 2. Timing data. Measuring the time it took to run parts of query has noticeable 36 overhead. Because of that, we measure the time only when running "ANALYZE 37 $stmt"). 38 39 */ 40 41 /* 42 A class for tracking time it takes to do a certain action 43 */ 44 class Exec_time_tracker 45 { 46 protected: 47 ulonglong count; 48 ulonglong cycles; 49 ulonglong last_start; 50 cycles_stop_tracking()51 void cycles_stop_tracking() 52 { 53 ulonglong end= my_timer_cycles(); 54 cycles += end - last_start; 55 if (unlikely(end < last_start)) 56 cycles += ULONGLONG_MAX; 57 } 58 public: Exec_time_tracker()59 Exec_time_tracker() : count(0), cycles(0) {} 60 61 // interface for collecting time start_tracking()62 void start_tracking() 63 { 64 last_start= my_timer_cycles(); 65 } 66 stop_tracking()67 void stop_tracking() 68 { 69 count++; 70 cycles_stop_tracking(); 71 } 72 73 // interface for getting the time get_loops()74 ulonglong get_loops() const { return count; } get_time_ms()75 double get_time_ms() const 76 { 77 // convert 'cycles' to milliseconds. 78 return 1000 * ((double)cycles) / sys_timer_info.cycles.frequency; 79 } 80 }; 81 82 83 /* 84 A class for counting certain actions (in all queries), and optionally 85 collecting the timings (in ANALYZE queries). 86 */ 87 88 class Time_and_counter_tracker: public Exec_time_tracker 89 { 90 public: 91 const bool timed; 92 Time_and_counter_tracker(bool timed_arg)93 Time_and_counter_tracker(bool timed_arg) : timed(timed_arg) 94 {} 95 96 /* Loops are counted in both ANALYZE and regular queries, as this is cheap */ incr_loops()97 void incr_loops() { count++; } 98 99 /* 100 Unlike Exec_time_tracker::stop_tracking, we don't increase loops. 101 */ stop_tracking()102 void stop_tracking() 103 { 104 cycles_stop_tracking(); 105 } 106 }; 107 108 #define ANALYZE_START_TRACKING(tracker) \ 109 { \ 110 (tracker)->incr_loops(); \ 111 if (unlikely((tracker)->timed)) \ 112 { (tracker)->start_tracking(); } \ 113 } 114 115 #define ANALYZE_STOP_TRACKING(tracker) \ 116 if (unlikely((tracker)->timed)) \ 117 { (tracker)->stop_tracking(); } 118 119 /* 120 A class for collecting read statistics. 121 122 The idea is that we run several scans. Each scans gets rows, and then filters 123 some of them out. We count scans, rows, and rows left after filtering. 124 125 (note: at the moment, the class is not actually tied to a physical table. 126 It can be used to track reading from files, buffers, etc). 127 */ 128 129 class Table_access_tracker 130 { 131 public: Table_access_tracker()132 Table_access_tracker() : 133 r_scans(0), r_rows(0), /*r_rows_after_table_cond(0),*/ 134 r_rows_after_where(0) 135 {} 136 137 ha_rows r_scans; /* How many scans were ran on this join_tab */ 138 ha_rows r_rows; /* How many rows we've got after that */ 139 ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */ 140 has_scans()141 bool has_scans() { return (r_scans != 0); } get_loops()142 ha_rows get_loops() { return r_scans; } get_avg_rows()143 double get_avg_rows() 144 { 145 return r_scans ? ((double)r_rows / r_scans): 0; 146 } 147 get_filtered_after_where()148 double get_filtered_after_where() 149 { 150 double r_filtered; 151 if (r_rows > 0) 152 r_filtered= (double)r_rows_after_where / r_rows; 153 else 154 r_filtered= 1.0; 155 156 return r_filtered; 157 } 158 on_scan_init()159 inline void on_scan_init() { r_scans++; } on_record_read()160 inline void on_record_read() { r_rows++; } on_record_after_where()161 inline void on_record_after_where() { r_rows_after_where++; } 162 }; 163 164 165 class Json_writer; 166 167 /* 168 This stores the data about how filesort executed. 169 170 A few things from here (e.g. r_used_pq, r_limit) belong to the query plan, 171 however, these parameters are calculated right during the execution so we 172 can't easily put them into the query plan. 173 174 The class is designed to handle multiple invocations of filesort(). 175 */ 176 177 class Filesort_tracker : public Sql_alloc 178 { 179 public: Filesort_tracker(bool do_timing)180 Filesort_tracker(bool do_timing) : 181 time_tracker(do_timing), r_limit(0), r_used_pq(0), 182 r_examined_rows(0), r_sorted_rows(0), r_output_rows(0), 183 sort_passes(0), 184 sort_buffer_size(0) 185 {} 186 187 /* Functions that filesort uses to report various things about its execution */ 188 report_use(ha_rows r_limit_arg)189 inline void report_use(ha_rows r_limit_arg) 190 { 191 if (!time_tracker.get_loops()) 192 r_limit= r_limit_arg; 193 else 194 r_limit= (r_limit != r_limit_arg)? 0: r_limit_arg; 195 196 ANALYZE_START_TRACKING(&time_tracker); 197 } incr_pq_used()198 inline void incr_pq_used() { r_used_pq++; } 199 report_row_numbers(ha_rows examined_rows,ha_rows sorted_rows,ha_rows returned_rows)200 inline void report_row_numbers(ha_rows examined_rows, 201 ha_rows sorted_rows, 202 ha_rows returned_rows) 203 { 204 r_examined_rows += examined_rows; 205 r_sorted_rows += sorted_rows; 206 r_output_rows += returned_rows; 207 } 208 report_merge_passes_at_start(ulong passes)209 inline void report_merge_passes_at_start(ulong passes) 210 { 211 sort_passes -= passes; 212 } report_merge_passes_at_end(ulong passes)213 inline void report_merge_passes_at_end(ulong passes) 214 { 215 ANALYZE_STOP_TRACKING(&time_tracker); 216 sort_passes += passes; 217 } 218 report_sort_buffer_size(size_t bufsize)219 inline void report_sort_buffer_size(size_t bufsize) 220 { 221 if (sort_buffer_size) 222 sort_buffer_size= ulonglong(-1); // multiple buffers of different sizes 223 else 224 sort_buffer_size= bufsize; 225 } 226 227 /* Functions to get the statistics */ 228 void print_json_members(Json_writer *writer); 229 get_r_loops()230 ulonglong get_r_loops() const { return time_tracker.get_loops(); } get_avg_examined_rows()231 double get_avg_examined_rows() 232 { 233 return ((double)r_examined_rows) / get_r_loops(); 234 } get_avg_returned_rows()235 double get_avg_returned_rows() 236 { 237 return ((double)r_output_rows) / get_r_loops(); 238 } get_r_filtered()239 double get_r_filtered() 240 { 241 if (r_examined_rows > 0) 242 return ((double)r_sorted_rows / r_examined_rows); 243 else 244 return 1.0; 245 } 246 private: 247 Time_and_counter_tracker time_tracker; 248 249 //ulonglong r_loops; /* How many times filesort was invoked */ 250 /* 251 LIMIT is typically a constant. There is never "LIMIT 0". 252 HA_POS_ERROR means we never had a limit 253 0 means different values of LIMIT were used in 254 different filesort invocations 255 other value means the same LIMIT value was used every time. 256 */ 257 ulonglong r_limit; 258 ulonglong r_used_pq; /* How many times PQ was used */ 259 260 /* How many rows were examined (before checking the select->cond) */ 261 ulonglong r_examined_rows; 262 263 /* 264 How many rows were put into sorting (this is examined_rows minus rows that 265 didn't pass the WHERE condition) 266 */ 267 ulonglong r_sorted_rows; 268 269 /* 270 How many rows were returned. This is equal to r_sorted_rows, unless there 271 was a LIMIT N clause in which case filesort would not have returned more 272 than N rows. 273 */ 274 ulonglong r_output_rows; 275 276 /* How many sorts in total (divide by r_count to get the average) */ 277 ulonglong sort_passes; 278 279 /* 280 0 - means not used (or not known 281 (ulonglong)-1 - multiple 282 other - value 283 */ 284 ulonglong sort_buffer_size; 285 }; 286 287