├── .gitignore ├── LICENSE ├── README.md └── query-digester /.gitignore: -------------------------------------------------------------------------------- 1 | !Build/ 2 | .last_cover_stats 3 | /META.yml 4 | /META.json 5 | /MYMETA.* 6 | *.o 7 | *.pm.tdy 8 | *.bs 9 | 10 | # Devel::Cover 11 | cover_db/ 12 | 13 | # Devel::NYTProf 14 | nytprof.out 15 | 16 | # Dizt::Zilla 17 | /.build/ 18 | 19 | # Module::Build 20 | _build/ 21 | Build 22 | Build.bat 23 | 24 | # Module::Install 25 | inc/ 26 | 27 | # ExtUtils::MakeMaker 28 | /blib/ 29 | /_eumm/ 30 | /*.gz 31 | /Makefile 32 | /Makefile.old 33 | /MANIFEST.bak 34 | /pm_to_blib 35 | /*.zip 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Masahiro Nagano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # query-digester 2 | 3 | pt-query-digest wrapper to make ops simple. 4 | 5 | query-digester changes slow_query_log_file and enable slow_query_log. 6 | wait several seconds and restores them. 7 | After finish logging slow query log, query-digester call pt-query-digest and save result to /tmp. 8 | 9 | # Usage 10 | 11 | sudo is recommended. slowlog will be stores like `-rw-r----- 1 mysql mysql 178 Oct 20 00:06 slow_query_20210920000610.log`. 12 | root privileges will be required to read it. 13 | 14 | ``` 15 | % sudo perl ./query-digester -duration 10 16 | exec mysql to change long_query_time and slow_query_log_file 17 | save slowlog to /tmp/slow_query_20200811172244.log 18 | wait 10 seconds 19 | finished capturing slowlog. 20 | start query-digest 21 | finished pt-query-digest. 22 | digest saved to /tmp/slow_query_20200811172244.digest 23 | 24 | % head /tmp/slow_query_20200811172244.digest 25 | ``` 26 | -------------------------------------------------------------------------------- /query-digester: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IO::Handle; 6 | use Getopt::Long; 7 | use File::Spec; 8 | 9 | sub find_path { 10 | my $pg = shift; 11 | my $path; 12 | for ( split /:/, $ENV{PATH} ) { 13 | if ( -x "$_/$pg" ) { 14 | $path = "$_/$pg"; 15 | last; 16 | } 17 | } 18 | $path; 19 | } 20 | 21 | my $limit = 40; 22 | my $duration = 10; 23 | Getopt::Long::Configure ("no_ignore_case"); 24 | GetOptions( 25 | "duration=s" => \$duration, 26 | "clear-log" => \my $clear_log, 27 | "stdout" => \my $stdout, 28 | "limit=s" => \$limit, 29 | "h|help" => \my $help, 30 | ); 31 | my @mysqlopt = @ARGV; 32 | $|=1; 33 | 34 | die "duration does not seems numeric" unless $duration =~ m!^\d+$!; 35 | $duration += 0; 36 | 37 | my $pt_query_digest = find_path('pt-query-digest') 38 | or die "could not find pt-query-digest"; 39 | my $mysql = find_path('mysql') 40 | or die "could not find mysql"; 41 | my $tmpdir = "/tmp";#File::Spec->tmpdir(); 42 | 43 | my $before = <<'EOF'; 44 | SET @cur_long_query_time = @@long_query_time; 45 | SET @cur_slow_query_log_file = @@slow_query_log_file; 46 | SET @cur_slow_query_log = @@slow_query_log; 47 | SET GLOBAL slow_query_log_file = "/slow_query_.log"; 48 | SET GLOBAL long_query_time = 0; 49 | SET GLOBAL slow_query_log = 1; 50 | EOF 51 | 52 | my $after = <<'EOF'; 53 | SET GLOBAL long_query_time = @cur_long_query_time; 54 | SET GLOBAL slow_query_log_file = @cur_slow_query_log_file; 55 | SET GLOBAL slow_query_log = @cur_slow_query_log; 56 | EOF 57 | 58 | $before =~ s!!$tmpdir!; 59 | my @lt = localtime(); 60 | my $date = sprintf('%04d%02d%02d%02d%02d%02d',$lt[5]+1900,$lt[4],$lt[3],$lt[2],$lt[1],$lt[0]); 61 | $before =~ s!!$date!; 62 | 63 | print STDERR "exec mysql to change long_query_time and slow_query_log_file\n"; 64 | print STDERR "save slowlog to $tmpdir/slow_query_$date.log\n"; 65 | 66 | #open(my $fh, ">", "$tmpdir/slow_query_$date.log"); 67 | #close($fh); 68 | #chmod 0666, "$tmpdir/slow_query_$date.log"; 69 | my $pid = fork; 70 | if ( defined $pid && $pid == 0 ) { 71 | my $stop = 0; 72 | local $SIG{INT} = sub { 73 | $stop++; 74 | }; 75 | local $SIG{TERM} = sub { 76 | $stop++; 77 | }; 78 | 79 | open(STDOUT,'>/dev/null'); 80 | open(my $pipe, '|-', $mysql, @mysqlopt, '--sigint-ignore'); 81 | $pipe->autoflush; 82 | $pipe->print($before); 83 | for my $i ( 0..$duration ) { 84 | last if $stop; 85 | $pipe->print("SELECT 1;\n") if $i % 7 == 0; 86 | sleep 1; 87 | } 88 | $pipe->print($after); 89 | exit; 90 | } 91 | print STDERR "wait $duration seconds\n"; 92 | while (wait == -1) {} 93 | my $exit_code = $?; 94 | if ( $exit_code != 0 ) { 95 | die sprintf("Error: mysql exited with code: %d", $exit_code >> 8); 96 | } 97 | 98 | print STDERR "finished capturing slowlog.\n"; 99 | print STDERR "start query-digest\n"; 100 | my $digest = *STDOUT; 101 | if ( !$stdout ) { 102 | open($digest, '>', "$tmpdir/slow_query_$date.digest"); 103 | } 104 | open(my $pipe, '-|', $pt_query_digest, '--limit',$limit,"$tmpdir/slow_query_$date.log"); 105 | while(<$pipe>){ 106 | $digest->print($_); 107 | } 108 | print STDERR "finished pt-query-digest.\n"; 109 | print STDERR "digest saved to $tmpdir/slow_query_$date.digest\n" if !$stdout; 110 | unlink "$tmpdir/slow_query_$date.log" if $clear_log; 111 | 112 | --------------------------------------------------------------------------------