1MK-VISUAL-EXPLAIN(1)  User Contributed Perl Documentation MK-VISUAL-EXPLAIN(1)
2
3
4

NAME

6       mk-visual-explain - Format EXPLAIN output as a tree.
7

SYNOPSIS

9       Usage: mk-visual-explain [OPTION...] [FILE...]
10
11       mk-visual-explain transforms EXPLAIN output into a tree representation
12       of the query plan.  If FILE is given, input is read from the file(s).
13       With no FILE, or when FILE is -, read standard input.
14
15       Examples:
16
17         mk-visual-explain <file_containing_explain_output>
18
19         mk-visual-explain -c <file_containing_query>
20
21         mysql -e "explain select * from mysql.user" | mk-visual-explain
22

RISKS

24       The following section is included to inform users about the potential
25       risks, whether known or unknown, of using this tool.  The two main
26       categories of risks are those created by the nature of the tool (e.g.
27       read-only tools vs. read-write tools) and those created by bugs.
28
29       mk-visual-explain is read-only and very low-risk.
30
31       At the time of this release, we know of no bugs that could cause
32       serious harm to users.
33
34       The authoritative source for updated information is always the online
35       issue tracking system.  Issues that affect this tool will be marked as
36       such.  You can see a list of such issues at the following URL:
37       <http://www.maatkit.org/bugs/mk-visual-explain>.
38
39       See also "BUGS" for more information on filing bugs and getting help.
40

DESCRIPTION

42       mk-visual-explain reverse-engineers MySQL's EXPLAIN output into a query
43       execution plan, which it then formats as a left-deep tree -- the same
44       way the plan is represented inside MySQL.  It is possible to do this by
45       hand, or to read EXPLAIN's output directly, but it requires patience
46       and expertise.  Many people find a tree representation more
47       understandable.
48
49       You can pipe input into mk-visual-explain or specify a filename at the
50       command line, including the magical '-' filename, which will read from
51       standard input.  It can do two things with the input: parse it for
52       something that looks like EXPLAIN output, or connect to a MySQL
53       instance and run EXPLAIN on the input.
54
55       When parsing its input, mk-visual-explain understands three formats:
56       tabular like that shown in the mysql command-line client, vertical like
57       that created by using the \G line terminator in the mysql command-line
58       client, and tab separated.  It ignores any lines it doesn't know how to
59       parse.
60
61       When executing the input, mk-visual-explain replaces everything in the
62       input up to the first SELECT keyword with 'EXPLAIN SELECT,' and then
63       executes the result.  You must specify "--connect" to execute the input
64       as a query.
65
66       Either way, it builds a tree from the result set and prints it to
67       standard output.  For the following query,
68
69        select * from sakila.film_actor join sakila.film using(film_id);
70
71       mk-visual-explain generates this query plan:
72
73        JOIN
74        +- Bookmark lookup
75        |  +- Table
76        |  |  table          film_actor
77        |  |  possible_keys  idx_fk_film_id
78        |  +- Index lookup
79        |     key            film_actor->idx_fk_film_id
80        |     possible_keys  idx_fk_film_id
81        |     key_len        2
82        |     ref            sakila.film.film_id
83        |     rows           2
84        +- Table scan
85           rows           952
86           +- Table
87              table          film
88              possible_keys  PRIMARY
89
90       The query plan is left-deep, depth-first search, and the tree's root is
91       the output node -- the last step in the execution plan.  In other
92       words, read it like this:
93
94       1.  Table scan the 'film' table, which accesses an estimated 952 rows.
95
96       2.  For each row, find matching rows by doing an index lookup into the
97           film_actor->idx_fk_film_id index with the value from
98           sakila.film.film_id, then a bookmark lookup into the film_actor
99           table.
100
101       For more information on how to read EXPLAIN output, please see
102       <http://dev.mysql.com/doc/en/explain.html>, and this talk titled "Query
103       Optimizer Internals and What's New in the MySQL 5.2 Optimizer," from
104       Timour Katchaounov, one of the MySQL developers:
105       <http://maatkit.org/presentations/katchaounov_timour.pdf>.
106

MODULES

108       This program is actually a runnable module, not just an ordinary Perl
109       script.  In fact, there are two modules embedded in it.  This makes
110       unit testing easy, but it also makes it easy for you to use the parsing
111       and tree-building functionality if you want.
112
113       The ExplainParser package accepts a string and parses whatever it
114       thinks looks like EXPLAIN output from it.  The synopsis is as follows:
115
116        require "mk-visual-explain";
117        my $p    = ExplainParser->new();
118        my $rows = $p->parse("some text");
119        # $rows is an arrayref of hashrefs.
120
121       The ExplainTree package accepts a set of rows and turns it into a tree.
122       For convenience, you can also have it delegate to ExplainParser and
123       parse text for you.  Here's the synopsis:
124
125        require "mk-visual-explain";
126        my $e      = ExplainTree->new();
127        my $tree   = $e->parse("some text", \%options);
128        my $output = $e->pretty_print($tree);
129        print $tree;
130

ALGORITHM

132       This section explains the algorithm that converts EXPLAIN into a tree.
133       You may be interested in reading this if you want to understand EXPLAIN
134       more fully, or trying to figure out how this works, but otherwise this
135       section will probably not make your life richer.
136
137       The tree can be built by examining the id, select_type, and table
138       columns of each row.  Here's what I know about them:
139
140       The id column is the sequential number of the select.  This does not
141       indicate nesting; it just comes from counting SELECT from the left of
142       the SQL statement.  It's like capturing parentheses in a regular
143       expression.  A UNION RESULT row doesn't have an id, because it isn't a
144       SELECT.  The source code actually refers to UNIONs as a fake_lex, as I
145       recall.
146
147       If two adjacent rows have the same id value, they are joined with the
148       standard single-sweep multi-join method.
149
150       The select_type column tells a) that a new sub-scope has opened b) what
151       kind of relationship the row has to the previous row c) what kind of
152       operation the row represents.
153
154       •   SIMPLE means there are no subqueries or unions in the whole query.
155
156       •   PRIMARY means there are, but this is the outermost SELECT.
157
158       •   [DEPENDENT] UNION means this result is UNIONed with the previous
159           result (not row; a result might encompass more than one row).
160
161       •   UNION RESULT terminates a set of UNIONed results.
162
163       •   [DEPENDENT|UNCACHEABLE] SUBQUERY means a new sub-scope is opening.
164           This is the kind of subquery that happens in a WHERE clause, SELECT
165           list or whatnot; it does not return a so-called "derived table."
166
167       •   DERIVED is a subquery in the FROM clause.
168
169       Tables that are JOINed all have the same select_type.  For example, if
170       you JOIN three tables inside a dependent subquery, they'll all say the
171       same thing: DEPENDENT SUBQUERY.
172
173       The table column usually specifies the table name or alias, but may
174       also say <derivedN> or <unionN,N...N>.  If it says <derivedN>, the row
175       represents an access to the temporary table that holds the result of
176       the subquery whose id is N.  If it says <unionN,..N> it's the same
177       thing, but it refers to the results it UNIONs together.
178
179       Finally, order matters.  If a row's id is less than the one before it,
180       I think that means it is dependent on something other than the one
181       before it.  For example,
182
183        explain select
184           (select 1 from sakila.film),
185           (select 2 from sakila.film_actor),
186           (select 3 from sakila.actor);
187
188        | id | select_type | table      |
189        +----+-------------+------------+
190        |  1 | PRIMARY     | NULL       |
191        |  4 | SUBQUERY    | actor      |
192        |  3 | SUBQUERY    | film_actor |
193        |  2 | SUBQUERY    | film       |
194
195       If the results were in order 2-3-4, I think that would mean 3 is a
196       subquery of 2, 4 is a subquery of 3.  As it is, this means 4 is a
197       subquery of the nearest previous recent row with a smaller id, which is
198       1.  Likewise for 3 and 2.
199
200       This structure is hard to programatically build into a tree for the
201       same reason it's hard to understand by inspection: there are both
202       forward and backward references.  <derivedN> is a forward reference to
203       selectN, while <unionM,N> is a backward reference to selectM and
204       selectN.  That makes recursion and other tree-building algorithms hard
205       to get right (NOTE: after implementation, I now see how it would be
206       possible to deal with both forward and backward references, but I have
207       no motivation to change something that works).  Consider the following:
208
209        select * from (
210           select 1 from sakila.actor as actor_1
211           union
212           select 1 from sakila.actor as actor_2
213        ) as der_1
214        union
215        select * from (
216           select 1 from sakila.actor as actor_3
217           union all
218           select 1 from sakila.actor as actor_4
219        ) as der_2;
220
221        | id   | select_type  | table      |
222        +------+--------------+------------+
223        |  1   | PRIMARY      | <derived2> |
224        |  2   | DERIVED      | actor_1    |
225        |  3   | UNION        | actor_2    |
226        | NULL | UNION RESULT | <union2,3> |
227        |  4   | UNION        | <derived5> |
228        |  5   | DERIVED      | actor_3    |
229        |  6   | UNION        | actor_4    |
230        | NULL | UNION RESULT | <union5,6> |
231        | NULL | UNION RESULT | <union1,4> |
232
233       This would be a lot easier to work with if it looked like this (I've
234       bracketed the id on rows I moved):
235
236        | id   | select_type  | table      |
237        +------+--------------+------------+
238        | [1]  | UNION RESULT | <union1,4> |
239        |  1   | PRIMARY      | <derived2> |
240        | [2]  | UNION RESULT | <union2,3> |
241        |  2   | DERIVED      | actor_1    |
242        |  3   | UNION        | actor_2    |
243        |  4   | UNION        | <derived5> |
244        | [5]  | UNION RESULT | <union5,6> |
245        |  5   | DERIVED      | actor_3    |
246        |  6   | UNION        | actor_4    |
247
248       In fact, why not re-number all the ids, so the PRIMARY row becomes 2,
249       and so on?  That would make it even easier to read.  Unfortunately that
250       would also have the effect of destroying the meaning of the id column,
251       which I think is important to preserve in the final tree.  Also, though
252       it makes it easier to read, it doesn't make it easier to manipulate
253       programmatically; so it's fine to leave them numbered as they are.
254
255       The goal of re-ordering is to make it easier to figure out which rows
256       are children of which rows in the execution plan.  Given the reordered
257       list and some row whose table is <union...> or <derived>, it is easy to
258       find the beginning of the slice of rows that should be child nodes in
259       the tree: you just look for the first row whose ID is the same as the
260       first number in the table.
261
262       The next question is how to find the last row that should be a child
263       node of a UNION or DERIVED.   I'll start with DERIVED, because the
264       solution makes UNION easy.
265
266       Consider how MySQL numbers the SELECTs sequentially according to their
267       position in the SQL, left-to-right.  Since a DERIVED table encloses
268       everything within it in a scope, which becomes a temporary table, there
269       are only two things to think about: its child subqueries and unions (if
270       any), and its next siblings in the scope that encloses it.  Its
271       children will all have an id greater than it does, by definition, so
272       any later rows with a smaller id terminate the scope.
273
274       Here's an example.  The middle derived table here has a subquery and a
275       UNION to make it a little more complex for the example.
276
277        explain select 1
278        from (
279           select film_id from sakila.film limit 1
280        ) as der_1
281        join (
282           select film_id, actor_id, (select count(*) from sakila.rental) as r
283           from sakila.film_actor limit 1
284           union all
285           select 1, 1, 1 from sakila.film_actor as dummy
286        ) as der_2 using (film_id)
287        join (
288           select actor_id from sakila.actor limit 1
289        ) as der_3 using (actor_id);
290
291       Here's the output of EXPLAIN:
292
293        | id   | select_type  | table      |
294        |  1   | PRIMARY      | <derived2> |
295        |  1   | PRIMARY      | <derived6> |
296        |  1   | PRIMARY      | <derived3> |
297        |  6   | DERIVED      | actor      |
298        |  3   | DERIVED      | film_actor |
299        |  4   | SUBQUERY     | rental     |
300        |  5   | UNION        | dummy      |
301        | NULL | UNION RESULT | <union3,5> |
302        |  2   | DERIVED      | film       |
303
304       The siblings all have id 1, and the middle one I care about is
305       derived3.  (Notice MySQL doesn't execute them in the order I defined
306       them, which is fine).  Now notice that MySQL prints out the rows in the
307       opposite order I defined the subqueries: 6, 3, 2.  It always seems to
308       do this, and there might be other methods of finding the scope
309       boundaries including looking for the lower boundary of the next largest
310       sibling, but this is a good enough heuristic.  I am forced to rely on
311       it for non-DERIVED subqueries, so I rely on it here too.  Therefore, I
312       decide that everything greater than or equal to 3 belongs to the
313       DERIVED scope.
314
315       The rule for UNION is simple: they consume the entire enclosing scope,
316       and to find the component parts of each one, you find each part's
317       beginning as referred to in the <unionN,...> definition, and its end is
318       either just before the next one, or if it's the last part, the end is
319       the end of the scope.
320
321       This is only simple because UNION consumes the entire scope, which is
322       either the entire statement, or the scope of a DERIVED table.  This is
323       because a UNION cannot be a sibling of another UNION or a table,
324       DERIVED or not.  (Try writing such a statement if you don't see it
325       intuitively).  Therefore, you can just find the enclosing scope's
326       boundaries, and the rest is easy.  Notice in the example above, the
327       UNION is over <union3,5>, which includes the row with id 4 -- it
328       includes every row between 3 and 5.
329
330       Finally, there are non-derived subqueries to deal with as well.  In
331       this case I can't look at siblings to find the end of the scope as I
332       did for DERIVED.  I have to trust that MySQL executes depth-first.
333       Here's an example:
334
335        explain
336        select actor_id,
337        (
338           select count(film_id)
339           + (select count(*) from sakila.film)
340           from sakila.film join sakila.film_actor using(film_id)
341           where exists(
342              select * from sakila.actor
343              where sakila.actor.actor_id = sakila.film_actor.actor_id
344           )
345        )
346        from sakila.actor;
347
348        | id | select_type        | table      |
349        |  1 | PRIMARY            | actor      |
350        |  2 | SUBQUERY           | film       |
351        |  2 | SUBQUERY           | film_actor |
352        |  4 | DEPENDENT SUBQUERY | actor      |
353        |  3 | SUBQUERY           | film       |
354
355       In order, the tree should be built like this:
356
357       •   See row 1.
358
359       •   See row 2.  It's a higher id than 1, so it's a subquery, along with
360           every other row whose id is greater than 2.
361
362       •   Inside this scope, see 2 and 2 and JOIN them.  See 4.  It's a
363           higher id than 2, so it's again a subquery; recurse.  After that,
364           see 3, which is also higher; recurse.
365
366       But the only reason the nested subquery didn't include select 3 is
367       because select 4 came first.  In other words, if EXPLAIN looked like
368       this,
369
370        | id | select_type        | table      |
371        |  1 | PRIMARY            | actor      |
372        |  2 | SUBQUERY           | film       |
373        |  2 | SUBQUERY           | film_actor |
374        |  3 | SUBQUERY           | film       |
375        |  4 | DEPENDENT SUBQUERY | actor      |
376
377       I would be forced to assume upon seeing select 3 that select 4 is a
378       subquery of it, rather than just being the next sibling in the
379       enclosing scope.  If this is ever wrong, then the algorithm is wrong,
380       and I don't see what could be done about it.
381
382       UNION is a little more complicated than just "the entire scope is a
383       UNION," because the UNION might itself be inside an enclosing scope
384       that's only indicated by the first item inside the UNION.  There are
385       only three kinds of enclosing scopes: UNION, DERIVED, and SUBQUERY.  A
386       UNION can't enclose a UNION, and a DERIVED has its own "scope markers,"
387       but a SUBQUERY can wholly enclose a UNION, like this strange example on
388       the empty table t1:
389
390        explain select * from t1 where not exists(
391           (select t11.i from t1 t11) union (select t12.i from t1 t12));
392
393        |   id | select_type  | table      | Extra                          |
394        +------+--------------+------------+--------------------------------+
395        |    1 | PRIMARY      | t1         | const row not found            |
396        |    2 | SUBQUERY     | NULL       | No tables used                 |
397        |    3 | SUBQUERY     | NULL       | no matching row in const table |
398        |    4 | UNION        | t12        | const row not found            |
399        | NULL | UNION RESULT | <union2,4> |                                |
400
401       The UNION's backward references might make it look like the UNION
402       encloses the subquery, but studying the query makes it clear this isn't
403       the case.  So when a UNION's first row says SUBQUERY, it is this
404       special case.
405
406       By the way, I don't fully understand this query plan; there are 4
407       numbered SELECT in the plan, but only 3 in the query.  The parens
408       around the UNIONs are meaningful.  Removing them will make the EXPLAIN
409       different.  Please tell me how and why this works if you know.
410
411       Armed with this knowledge, it's possible to use recursion to turn the
412       parent-child relationship between all the rows into a tree representing
413       the execution plan.
414
415       MySQL prints the rows in execution order, even the forward and backward
416       references.  At any given scope, the rows are processed as a left-deep
417       tree.  MySQL does not do "bushy" execution plans.  It begins with a
418       table, finds a matching row in the next table, and continues till the
419       last table, when it emits a row.  When it runs out, it backtracks till
420       it can find the next row and repeats.  There are subtleties of course,
421       but this is the basic plan.  This is why MySQL transforms all RIGHT
422       OUTER JOINs into LEFT OUTER JOINs and cannot do FULL OUTER JOIN.
423
424       This means in any given scope, say
425
426        | id   | select_type  | table      |
427        |  1   | SIMPLE       | tbl1       |
428        |  1   | SIMPLE       | tbl2       |
429        |  1   | SIMPLE       | tbl3       |
430
431       The execution plan looks like a depth-first traversal of this tree:
432
433              JOIN
434             /    \
435           JOIN  tbl3
436          /    \
437        tbl1   tbl2
438
439       The JOIN might not be a JOIN.  It might be a subquery, for example.
440       This comes from the type column of EXPLAIN.  The documentation says
441       this is a "join type," but I think "access type" is more accurate,
442       because it's "how MySQL accesses rows."
443
444       mk-visual-explain decorates the tree significantly more than just
445       turning rows into nodes.  Each node may get a series of transformations
446       that turn it into a subtree of more than one node.  For example, an
447       index scan not marked with 'Using index' must do a bookmark lookup into
448       the table rows; that is a three-node subtree.  However, after the above
449       node-ordering and scoping stuff, the rest of the process is pretty
450       simple.
451

OPTIONS

453       This tool accepts additional command-line arguments.  Refer to the
454       "SYNOPSIS" and usage information for details.
455
456       --ask-pass
457           Prompt for a password when connecting to MySQL.
458
459       --charset
460           short form: -A; type: string
461
462           Default character set.  If the value is utf8, sets Perl's binmode
463           on STDOUT to utf8, passes the mysql_enable_utf8 option to
464           DBD::mysql, and runs SET NAMES UTF8 after connecting to MySQL.  Any
465           other value sets binmode on STDOUT without the utf8 layer, and runs
466           SET NAMES after connecting to MySQL.
467
468       --clustered-pk
469           Assume that PRIMARY KEY index accesses don't need to do a bookmark
470           lookup to retrieve rows.  This is the case for InnoDB.
471
472       --config
473           type: Array
474
475           Read this comma-separated list of config files; if specified, this
476           must be the first option on the command line.
477
478       --connect
479           Treat input as a query, and obtain EXPLAIN output by connecting to
480           a MySQL instance and running EXPLAIN on the query.  When this
481           option is given, mk-visual-explain uses the other connection-
482           specific options such as "--user" to connect to the MySQL instance.
483           If you have a .my.cnf file, it will read it, so you may not need to
484           specify any connection-specific options.
485
486       --database
487           short form: -D; type: string
488
489           Connect to this database.
490
491       --defaults-file
492           short form: -F; type: string
493
494           Only read mysql options from the given file.  You must give an
495           absolute pathname.
496
497       --format
498           type: string; default: tree
499
500           Set output format.
501
502           The default is a terse pretty-printed tree. The valid values are:
503
504            value  meaning
505            =====  =======
506            tree   Pretty-printed terse tree.
507            dump   Data::Dumper output (see L<Data::Dumper> for more).
508
509       --help
510           Show help and exit.
511
512       --host
513           short form: -h; type: string
514
515           Connect to host.
516
517       --password
518           short form: -p; type: string
519
520           Password to use when connecting.
521
522       --pid
523           type: string
524
525           Create the given PID file.  The file contains the process ID of the
526           script.  The PID file is removed when the script exits.  Before
527           starting, the script checks if the PID file already exists.  If it
528           does not, then the script creates and writes its own PID to it.  If
529           it does, then the script checks the following: if the file contains
530           a PID and a process is running with that PID, then the script dies;
531           or, if there is no process running with that PID, then the script
532           overwrites the file with its own PID and starts; else, if the file
533           contains no PID, then the script dies.
534
535       --port
536           short form: -P; type: int
537
538           Port number to use for connection.
539
540       --set-vars
541           type: string; default: wait_timeout=10000
542
543           Set these MySQL variables.  Immediately after connecting to MySQL,
544           this string will be appended to SET and executed.
545
546       --socket
547           short form: -S; type: string
548
549           Socket file to use for connection.
550
551       --user
552           short form: -u; type: string
553
554           User for login if not current user.
555
556       --version
557           Show version and exit.
558

DSN OPTIONS

560       These DSN options are used to create a DSN.  Each option is given like
561       "option=value".  The options are case-sensitive, so P and p are not the
562       same option.  There cannot be whitespace before or after the "=" and if
563       the value contains whitespace it must be quoted.  DSN options are
564       comma-separated.  See the maatkit manpage for full details.
565
566       •   A
567
568           dsn: charset; copy: yes
569
570           Default character set.
571
572       •   D
573
574           dsn: database; copy: yes
575
576           Default database.
577
578       •   F
579
580           dsn: mysql_read_default_file; copy: yes
581
582           Only read default options from the given file
583
584       •   h
585
586           dsn: host; copy: yes
587
588           Connect to host.
589
590       •   p
591
592           dsn: password; copy: yes
593
594           Password to use when connecting.
595
596       •   P
597
598           dsn: port; copy: yes
599
600           Port number to use for connection.
601
602       •   S
603
604           dsn: mysql_socket; copy: yes
605
606           Socket file to use for connection.
607
608       •   u
609
610           dsn: user; copy: yes
611
612           User for login if not current user.
613

DOWNLOADING

615       You can download Maatkit from Google Code at
616       <http://code.google.com/p/maatkit/>, or you can get any of the tools
617       easily with a command like the following:
618
619          wget http://www.maatkit.org/get/toolname
620          or
621          wget http://www.maatkit.org/trunk/toolname
622
623       Where "toolname" can be replaced with the name (or fragment of a name)
624       of any of the Maatkit tools.  Once downloaded, they're ready to run; no
625       installation is needed.  The first URL gets the latest released version
626       of the tool, and the second gets the latest trunk code from Subversion.
627

ENVIRONMENT

629       The environment variable "MKDEBUG" enables verbose debugging output in
630       all of the Maatkit tools:
631
632          MKDEBUG=1 mk-....
633

SYSTEM REQUIREMENTS

635       You need Perl, DBI, DBD::mysql, and some core packages that ought to be
636       installed in any reasonably new version of Perl.
637

BUGS

639       For a list of known bugs see
640       <http://www.maatkit.org/bugs/mk-visual-explain>.
641
642       Please use Google Code Issues and Groups to report bugs or request
643       support: <http://code.google.com/p/maatkit/>.  You can also join
644       #maatkit on Freenode to discuss Maatkit.
645
646       Please include the complete command-line used to reproduce the problem
647       you are seeing, the version of all MySQL servers involved, the complete
648       output of the tool when run with "--version", and if possible,
649       debugging output produced by running with the "MKDEBUG=1" environment
650       variable.
651

COPYRIGHT, LICENSE AND WARRANTY

653       This program is copyright 2007-2011 Baron Schwartz.  Feedback and
654       improvements are welcome.
655
656       THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
657       WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
658       MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
659
660       This program is free software; you can redistribute it and/or modify it
661       under the terms of the GNU General Public License as published by the
662       Free Software Foundation, version 2; OR the Perl Artistic License.  On
663       UNIX and similar systems, you can issue `man perlgpl' or `man
664       perlartistic' to read these licenses.
665
666       You should have received a copy of the GNU General Public License along
667       with this program; if not, write to the Free Software Foundation, Inc.,
668       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
669

SEE ALSO

671       See also mk-query-profiler.
672

AUTHOR

674       Baron "Xaprb" Schwartz
675

ABOUT MAATKIT

677       This tool is part of Maatkit, a toolkit for power users of MySQL.
678       Maatkit was created by Baron Schwartz; Baron and Daniel Nichter are the
679       primary code contributors.  Both are employed by Percona.  Financial
680       support for Maatkit development is primarily provided by Percona and
681       its clients.
682

VERSION

684       This manual page documents Ver 1.0.22 Distrib 7540 $Revision: 7477 $.
685
686
687
688perl v5.34.0                      2022-01-20              MK-VISUAL-EXPLAIN(1)
Impressum