Showing posts with label mysql. Show all posts
Showing posts with label mysql. Show all posts

Thursday, April 3, 2014

Monitoring multiline logs by email in full color

I use tools like 'logwatch' and 'logcheck' to monitor new events in logs, but they have serious drawbacks. It is most obvious when it comes to multiline logs like PHP error log or MySQL slow log.

So, I wrote a trivial script that would send email messages with new events from the logs. Besides, the messages must be htmlized/colorized, highlighting the SQL/PHP syntax.

So, here's the script:

#!/bin/bash

L=$(/usr/sbin/logtail2 -f $2 )
if [ "x$L" != x"" ]; then
        echo "${L}"|source-highlight -s $3 -f html|mail $1 -a "Content-type: text/html" \
-s "$(hostname): $2" fi

The syntax is: logmail <recipients> <log file> <syntax>

To extract only new events from the log, I use 'logtail2' from 'logcheck' package. For syntax highlighting I chose 'source-highlight' (it was also used to highlight the script code above).

So, to process MySQL slow log, call the script like this:

logmail name@mail.host /var/log/mysql/mysql.slow.log sql

Or, to produce a report from the php-fpm slow log:

logmail name@mail.host /var/log/php-fpm.slow.log php

The results may look like this:

# Time: 140403 12:01:14
# Thread_id: 12983054  Schema: dsa  Last_errno: 0  Killed: 0
# Query_time: 12.672162  Lock_time: 0.000246  Rows_sent: 300  Rows_examined: 5604906  Rows_affected: 0  Rows_read: 5604906
# Bytes_sent: 14625
SET timestamp=1396512074;
SELECT
       document_id
     , external_document_id
     , DATE_FORMAT(created_at, "%Y-%m-%d") AS created_at
     , source_id
    FROM
     document
    WHERE
     (
      #(
      # status = 'preparsed'
      # AND flag = ""
      #)
      #OR (
       status = 'converted'
      #)
     )
     
     
    ORDER BY
     document.source_priority DESC
    LIMIT
     0, 300;

Tuesday, August 16, 2011

The case of slow MySQL connect

Quite of a sudden, PHP scripts on one of my servers became very slow. Other servers worked fine. PHP-FPM logs showed that the biggest delays happened during database operations. The database server, though, recorded nothing in the mysql-slow.log.

Then, we had a look at the process list in MySQL. There were some dozens of lines like these:

| 1409888 | unauthenticated user | 11.11.11.11:59585    | NULL | Connect     |   NULL | login                                                          | NULL             |
| 1409889 | unauthenticated user | 11.11.11.11:59584    | NULL | Connect     |   NULL | login                                                          | NULL             |
| 1409890 | unauthenticated user | 11.11.11.11:59587    | NULL | Connect     |   NULL | login                                                          | NULL             |

where 11.11.11.11 is the IP address of that application server whence the connection was opened. Google said that these messages usually indicate at DNS problems. One of possible solutions would be to restart MySQL with --skip-name-resolve option, but we didn't want to stop the database. So, we added the hostname of the application server to the /etc/hosts file on the database server, and voilá! Unauthenticated users were gone in some seconds and the performance was restored.

The hosting company had informed us before of the planned maintenance, but they only mentioned their own website and their 'whois' server. They must have done something wrong with their DNS servers, too, which caused our database accept new connections very slowly.

Monday, December 20, 2010

SOLVED: Moving InnoDB tables between Percona MySQL servers

Update. Warning: The procedure is not working. The table can be copied, imported into another server and successfully read, but an attempt to write to the table causes a server crash. The problem is probably caused by remaining tablespace ids in the ibd file.

Update 2. Kudos to the Percona team, who pointed me to an error in my configuration file. To import tablespaces, one more option had to be set on the server, innodb_expand_import. Xtrabackup documentation has been updated.

When trying to move a table from one server to another, I found a problem. I followed the procedures outlined in the Percona Xtrabackup manual, chapter Exporting Tables.

On the last step, when doing IMPORT TABLE, I received an error message saying:

mysql> alter table `document_entity_bodies` import tablespace;
ERROR 1030 (HY000): Got error -1 from storage engine

There was some more information in the log:

101220 16:14:51  InnoDB: Error: tablespace id and flags in file './dbx_replica/document_entity_bodies.ibd'
 are 21 and 0, but in the InnoDB
InnoDB: data dictionary they are 181 and 0.
InnoDB: Have you moved InnoDB .ibd files around without using the
InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?
InnoDB: Please refer to
InnoDB: http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting-datadict.html
InnoDB: for how to resolve the issue.
101220 16:14:51  InnoDB: cannot find or open in the database directory the .ibd file of
InnoDB: table `dbx_replica`.`document_entity_bodies`
InnoDB: in ALTER TABLE ... IMPORT TABLESPACE

The page mentioned in the log was not really helpful. What saved me was this article: Recovering an InnoDB table from only an .ibd file.

Following the instructions, I used a hex editor (shed) to change tablespace id in the ibd file from 0x15 to 0xB5 and then the import worked fine.

I wonder if there is a way to avoid these manipulations. Perhaps, one more operation should be added to xtrabackup to make the tablespace ids agree?

Oh, and it's Xtrabackup 1.4 working with Percona Server 5.1.51

Friday, November 19, 2010

MySQL: enable innodb_file_per_table with zero downtime

I thought that while my wife is preoccupied with the lemon pie, I might tell you this story.

InnoDB is a very good storage engine for MySQL that combines reasonable performance with wide popularity and, as a consequence, a good set of tools for diagnostics and fine-tuning. One of its downsides is that it is inefficient when it comes to the disk space management. While an extent of HDD space was added to the storage, InnoDB will not return it back even when you delete tables or databases. To add some flexibility, you should use innodb_file_per_table option. Unfortunately, if you have a running database, you cannot just enable this option. You will have to make a dump of the database and restore it on a new instance of MySQL with the option enabled from the very beginning. This scenario means that the database will be inaccessible from the moment you start mysqldump to the moment you finish restoring the data in the new instance. Is there a way to minimize the downtime?

Yes, you can run mysqldump on a backup of your database. But, then you lose the data written to the database from the moment you make the backup to the moment the new instance is ready. But that's a bit closer to the solution. You can also set up replication between the original database and the new one and then, when the new instance catches up with the old one, your task is completed. And the backup can be done online, without stopping MySQL, if you use Xtrabackup tool by Percona.

So, the basic steps you have to follow are:

  • Configure your original database as master. Unless your database is already using binlogs for security, this is the only step that will require restarting MySQL.
  • Make a backup of the original database using Xtrabackup.
  • Restore the backup and run a second instance of MySQL.
  • Run mysqldump on the second instance.
  • Stop the second instance, but do not delete it yet.
  • Create a new database and start the third instance of MySQL with the enabled option innodb_file_per_table.
  • Restore the dump by feeding it into the third instance of MySQL.
  • Configure the third instance as slave and run the replication.
  • When the initial replication finishes and the slave catches up with the master, reconfigure your clients to use the new instance.
  • That's it. You can stop the first instance now and delete it.

I wrote an even more detailed guide illustrated with example commands. It was published on Linux.com recently: HOWTO: Reconfigure MySQL to use innodb_file_per_table with zero downtime

Sunday, October 24, 2010

MySQL reference I had always missed

InnoDB Glossary. These terms are commonly used in information about the InnoDB storage engine. No idea, why I have never seen this before. I found the article today and I'm still reading. It's a very dense explanation of many important construction blocks of InnoDB (and, of course, XtraDB!) engine. Must read.

Monday, August 30, 2010

XtraBackup 1.3b

I have installed the newer version of XtraBackup, a tool for online backup of MySQL databases. The first tests show that a lot of errors were fixed in this, not yet released, version. Version 1.2 failed to make incremental backups on one of my servers, could not restore full backup on another, and segfaulted during full backup on the third one. 1.3b makes copies of all three and is able to restore them flawlessly.

A 233GB database was copied in 2:15'. I highly recommend you upgrade your installation of Xtrabackup to v.1.3b and to test it on production servers.

Download page at Percona.com is here: XtraBackup-1.3-beta. I usually download x86_64 binary files for Linux here.

Thursday, June 24, 2010

PINBA: PHP Is Not a Bottleneck Anymore

Yesterday I installed Pinba on one of my servers. Pinba is a set of tools to monitor performance of PHP scripts. Pinba MySQL database engine runs a list of timers and automatically fills report databases. Pinba PHP extension uses two functions to open and close these timers. Besides, there are default timers, which open when a script is executed and close when it finishes. If you put a timer around some critical piece of code you can get information on how often it runs and how much time it is being executed. Timers can be tagged and the data can be grouped by tags. So, in some pieces of code you can set tags "author" and "task" with corresponding values. Then you'll manage to compare the performance of code written by different developers and identify the most time-consuming parts. The most interesting thing is that when you create database tables following certain rules, these tables become automagically filled with the necessary data. So, if you use the tags "author" and "tags" to group the data, the reports will include all valid combinations of these tags and show summaries on these combinations: how much time Joe's scripts parsed new documents and how often Jackie's front-end scripts were called. Very impressive.

By default, Pinba stores this information for some limited period of time (15 minutes, IIUC), so you need some way to make the data persistent. Since we use Munin to monitor various system indicators, I wrote a couple of plugins (in TCL and Lua, just for fun :)) to display the frequency of execution and the average execution time for each timer. Our developers added a handful of timers in various places of code and here it is:

The last graph looks cluttered and not too informative, so I plan to employ Munin's 'suggest' feature to draw some diagrams using one script. Perhaps, organizing the graphs will be the most difficult part of the deployment. I have to say, though, that the installation was not simple, either. Prerequisites include compiled sources of the installed MySQL (Percona Server 10.2 in my case), Google Protocol Buffers, Judy library, libevent 1.4.1+ (Ubuntu's default one will do) and Hoard memory allocator. And here is the installation process (paths will be different for you, so check carefully):

wget http://pinba.org/files/pinba_engine-0.0.5.tar.gz
tar -xzf pinba_engine-0.0.5.tar.gz
wget http://pinba.org/files/pinba_extension-0.0.5.tgz
tar -xzf pinba_extension-0.0.5.tgz
wget http://protobuf.googlecode.com/files/protobuf-2.3.0.tar.gz
tar -xzf protobuf-2.3.0.tar.gz
wget http://downloads.sourceforge.net/project/judy/judy/\
Judy-1.0.5/Judy-1.0.5.tar.gz?use_mirror=ignum
tar -xzf Judy-1.0.5.tar.gz
wget http://www.cs.umass.edu/%7Eemery/hoard/hoard-3.8/source/hoard-38.tar.gz
tar -xzf hoard-38.tar.gz
sudo aptitude install libevent-1.4-2 libevent-dev
cd protobuf-2.3.0
./configure
make -j
sudo make install
cd ../judy-1.0.5/
./configure
make
sudo make install
cd ../hoard-38/src
make linux-gcc-x86-64
sudo cp libhoard.so /usr/local/lib
sudo cp *.h /usr/local/include
sudo ldconfig
cd pinba_engine-0.0.5/
./configure --with-mysql=/home/minaev/Percona-Server-10.2/ \
--with-judy=/usr/local --with-protobuf=/usr/local \
--with-event=/usr --libdir=/usr/lib/mysql/plugin/ \
--with-hoard=/usr/local
make 
sudo make install
echo "INSTALL PLUGIN pinba SONAME 'libpinba_engine.so'"|mysql
echo "CREATE DATABASE pinba"|mysql
mysql -D pinba <default_tables.sql
cd pinba-0.0.5/
sed -i 's/NOTICE/CHECKING/' config.m4
phpize
./configure --with-pinba=/usr/local
sudo make install

I had to edit config.m4 because my version autoconf was a bit buggy. After this process you'll have to add three lines to your php.ini:

extension=pinba.so
pinba.enabled=1
pinba.server=[MySQL server address]

And here is one of Munin plugins, written in TCL. It collects data on how often certain API parts were called.

#!/usr/bin/tclsh

package require mysqltcl 3.05


proc clean_fieldname arg {
    return [regsub -all {[^A-Za-z]} $arg "_"]
}

set dbuser "pinba"
set db "pinba"

set conn [::mysql::connect -user $dbuser -db $db]

set fields [::mysql::sel $conn 
  "select concat(module_value, '+', action_value) from \
tag_info_module_action" -list]

if {$argc > 0} {
    switch [lindex $argv 0] {
        "config" {
            puts "graph_title PHP Actions per second"
            puts "graph_vlabel reqs per second"
            puts "graph_category Pinba"
            foreach fld $fields {
                set clean [clean_fieldname $fld]
                 puts "$clean.label $fld"
                 puts "$clean.draw LINE3"
            }
        }
        "autoconf" {
            puts "yes"
        }
    }
} else {
    foreach fld $fields {
        set clean [clean_fieldname $fld]
        set data [::mysql::sel $conn 
  "select req_per_sec from tag_info_module_action where \
  concat(module_value, '+', action_value)='$fld'" -list]
        puts "$clean.value $data"
    }
}

::mysql::close $conn

BTW, you may find it interesting that the performance of TCL scripts was almost the same as that of Lua scripts and about 3-4 times higher than for Bash.

Thursday, April 15, 2010

MySQL Proxies

While searching for a MySQL proxy solution, I found the following four products:

At least two of them share the same codebase, MySQL-proxy being the forefather of Spock proxy. Spock proxy tries to increase performance by eliminating the scripting layer. The goal of Spock is to provide efficient sharding, not fault-tolerance. Dormando's proxy positions itself as an alternative to the official MySQL-proxy, and it tries to retain compatibility. It even supports Lua scripting. I'm not sure, though, if the API is the same. And, finally, Proximo is written in Perl, which means its performance is lower than that of the other competitors. But Proximo is in very early stage of development and has a promising architecture. It may have good future.

Thursday, March 25, 2010

Slightly disappointed with Percona products

A large part of my to-do list was devoted to the products by Percona, company well-known in the MySQL world. I installed XtraDB engine on our servers and began using Xtrabackup to backup our databases. Not bad, I have to admit. I mean, the servers still work and the backups are made. But I had to solve so many problems that I kept recalling my first days with Linux almost fifteen years ago, when you had to compile everything from sources, manually track dependencies and even then there was only one chance in two that the program will run.

First, I installed XtraDB, MySQL engine which is supposd to cure the long-standing deficiencies of InnoDB. It was not too difficult. I downloaded the sources of XtraDB-1.0.6, tried to compile, failed, found a description of a non-standard (easily explainable, though) installation procedure, copied the sources to the MySQL source tree, replacing the InnoDB engine, tried to compile, found an error, googled for a solution, fixed a bug in handler/i_s.cc, tried to compile, found an error, googled for a solution, fixed a bug in Makefile.in, tried to compile, succeeded, installed and there it is. Easy, right? :)

Next, I wanted to have a look at another product by Percona, Xtrabackup, version 1.0 of which was announced in December 2009. Xtrabackup is to become the main backup solution for MySQL, being the only free tool able to perform online backups. There is more than one link at Percona.com leading to the sources. Or should I say misleading? Here's one page saying you have to use Bazaar to get the sources. So I did. There were some screens chockfull of error messages, which I tried to quench with a bunch of header files stolen from MySQL source tree, but to no avail. I checked the Percona web-site once again and found another link. This tarball included a whole distribution of MySQL. I tried to compile it as it is. Then, I tried to copy the sources to MySQL sources, strictly following the recommended procedure. Then, I tried to copy the sources to my source tree (with XtraDB substituted for InnoDB, as described above). I admit I almost gave up. In the end, I downloaded a binary version of Xtrabackup, compiled by the authors. It just worked. Well, to a degree...

This package contained Xtrabackup itself and a Perl script called Innobackupex. The problem with Xtrabackup is that it does not support anything but InnoDB (and XtraDB). Fortunately, MyISAM tables can be just copied as files, and Innobackupex does exactly this. Unfortunately, Innobackupex does this only when making full backups. Incremental backups only include InnoDB tables.

This problem was easy to fix, but there was another. Innobackupex does not support the option --incremental-basedir specifying the last full backup. The reference point for the incremental backup is defined as the earliest directory. So, I had to add support for this option to the script manually.

So, to sum it up, the number of problems I met in Percona products is unusually high. Or is it normal for a company that makes money solving users' problems? :)

Friday, February 26, 2010

Testing new MySQL on production server

Even when your MySQL works on a mission-critical server, there is a way to make downtime as short as possible after you have compiled a new version of the DBMS. MySQL documentation contains a chapter called 5.6. Running Multiple MySQL Servers on the Same Machine. But the reality is a bit more complicated.

First of all, you have to create a test database with the mysql_install_db command. Create an empty directory called, for example, test-db in your home directory and give full permissions to all users, to avoid problems with mysql user trying to create files:

$ mkdir test-db $ chmod 777 test-db

Now, let's populate the directory with a test database:

$ mysql_install_db --user=mysql --datadir=/DIR/db

Note that we assume that your database runs under the username mysql, as it should. Now, you can try to run the test database. It must listen on a different IP port, use different data directory, different files for socket and pid, different log and error log. Hence, the following command:

$ PATH-TO-NEW/mysqld --port=12345 --datadir=/DIR/db --socket=/DIR/db/sock --pid-file=/DIR/db/pid --log=/DIR/db/log --log-error=/DIR/db/log-err

Now, you can check whether the new, fresh copy of MySQL works. If it does, you can stop the running version and then run make install for the new one.

Note that we did not grant any permissions to any users, so you might not be able to connect to the new database with mysql client.

MySQL+XtraDB: fixing compilation errors

Trying to launch MySQL with the new XtraDB engine. First, I downloaded MySQL sources with patches by Percona and untarred them. They compile without errors and MySQL starts without problems. However, this tarball uses InnoDB, while we wanted to test XtraDB.

Next, I downloaded XtraDB 5.1.42-1.0.6-9 sources from Percona web-site. According to the installation instructions, to use XtraDB, you have to replace the contents of storage/innobase directory with the contents of the XtraDB archive. This time, I received two error messages during the compilation phase, but both of them had already been addressed by the developers and described at Percona's Launchpad.net. In the first case, the file handler/i_s.cc contained a minor error at line 801: error: invalid conversion from ‘const char*’ to ‘char*’

To fix this,

if((p = strchr(index->table_name, '/')))

in line 801 in handler/i_s.cc has to be replaced with:

if((p = strchr((char *) index->table_name, '/')))

Then I got another error message: ha_innodb.cc:2622: undefined reference to `active_mi'. To solve this second issue, I had to add disable compilation of MySQL embedded server by adding the following option to the ./configure script: --without-embedded-server.

So, finally, make command succeeded and I tried to install MySQL, but then there was the third error:/bin/sh: @MKDIR_P@: command not found. Fortunately, someone else had seen this message before and the solution is available here. In the file /storage/innobase/Makefile.in line MKDIR_P = @MKDIR_P@ must be replaced with MKDIR_P = @mkdir_p@.

So, to save your time, do these three modifications before you run ./configure. So, the session transcript would look like this:

$ wget http://www.mysqlperformanceblog.com/mysql/5.1/\
source/mysql-5.1.26-percona.tar.bz2
$ wget http://www.percona.com/percona-builds/Percona-XtraDB/\
Percona-XtraDB-5.1.43-9.1/source/percona-xtradb-1.0.6-9.1.tar.gz
$ tar -xjf mysql-5.1.26-percona.tar.bz2
$ tar -xzf percona-xtradb-1.0.6-9.1.tar.gz
$ rm -r mysql-5.1.26-percona/storage/innobase/*
$ mv percona-xtradb-1.0.6-9.1/* mysql-5.1.26-percona/storage/innobase
$ cd mysql-5.1.26-percona/
mysql-5.1.26-percona$ sed -i 's/strchr(index/strchr((char *) index/' storage/innobase/handler/i_s.cc
mysql-5.1.26-percona$ sed -i 's/@MKDIR_P@/@mkdir_p@/' storage/innobase/Makefile.in
mysql-5.1.26-percona$ ./configure '--build=x86_64-linux-gnu' \
'--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' \
'--libexecdir=/usr/sbin' '--datadir=/usr/share' \
'--localstatedir=/var/lib/mysql' '--includedir=/usr/include' \
'--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-1ubuntu2' \
'--with-comment=(Ubuntu)' '--with-system-type=debian-linux-gnu' \
'--enable-shared' '--enable-static' '--enable-thread-safe-client' \
'--enable-assembler' '--enable-local-infile' '--with-pic' \
'--with-lib-ccflags=-fPIC' '--with-pstack' '--with-fast-mutexes' \
'--with-big-tables' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' \
'--with-mysqld-user=mysql' '--with-libwrap' '--with-ssl' \
'--without-docs' '--with-extra-charsets=all' '--with-plugins=max' \
'--without-ndbcluster' '--without-embedded-server' '--with-embedded-privilege-control' \
'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' \
'CC=gcc' 'CFLAGS=-O3 -DBIG_JOINS=1 -fPIC -fno-strict-aliasing' \
'LDFLAGS=-Wl,-Bsymbolic-functions' 'CPPFLAGS=' 'CXX=g++' \
'CXXFLAGS=-O3 -DBIG_JOINS=1 -felide-constructors -fno-exceptions \
-fno-rtti -fPIC -fno-strict-aliasing' 'FFLAGS=-g -O2'
mysql-5.1.26-percona$ make
mysql-5.1.26-percona$ sudo make install