qpsmon v0.1
This is a simple script that will print the average qps (using a specified period) for one or more MySQL databases. If doesn’t have many practical uses, but it’s fun to watch when running large imports or exports.
<?php
/*
qpsmon.php v0.1
This is an (extremely) simple script that will get the average
number of queries/second for several MySQL databases.
Example Output:
qpsmon v0.1
Sample size 120 seconds. [60 times every 2 seconds]
Margaret 176 qps
Yarra 11 qps
Murray 1066 qps
*/
error_reporting(E_ALL | E_STRICT);
define('NUMBER_SAMPLES', 60); // Number of samples to keep
define('PEROID_LENGTH', 2); // How often (in seconds) to take a simple
define('AUTO_CLEAR', true); // Automatically clear the screen each refresh
define('PROGRAM_NAME', 'qpsmon');
define('PROGRAM_VERSION', '0.1');
$databases = array
(
// Define you MySQL databases here. E.G.
'My Server' => new MysqlConn('localhost', 'username', 'password', MysqlConn::MYSQL_VERSION_5),
);
class LimBuffer
{
private $maxSize;
private $buffer = array();
public function __construct($maxSize = 20)
{
$this->maxSize = $maxSize;
}
public function add($v)
{
$this->buffer[] = $v;
if(count($this->buffer) > $this->maxSize)
{
$this->buffer = array_slice($this->buffer, -$this->maxSize);
}
}
public function get()
{
return $this->buffer;
}
public function first()
{
return $this->buffer[0];
}
public function last()
{
return $this->buffer[count($this->buffer) - 1];
}
}
class MysqlConn
{
const MYSQL_VERSION_4 = 1010;
const MYSQL_VERSION_5 = 2020;
private $host;
private $username;
private $password;
private $mysqlVersion;
private $conn = false;
public function __construct($host, $username, $password, $mysqlVersion = self::MYSQL_VERSION_5)
{
if(!in_array($mysqlVersion, array(self::MYSQL_VERSION_4, self::MYSQL_VERSION_5)))
{
die("Invalid mysql version specified\n");
}
$this->host = $host;
$this->username = $username;
$this->password = $password;
$this->mysqlVersion = $mysqlVersion;
}
public function getQueryCount()
{
if(!$this->conn)
{
$this->connect();
}
$sql = ($this->mysqlVersion == self::MYSQL_VERSION_4) ? ' SHOW STATUS' : 'SHOW GLOBAL STATUS';
$query = mysql_query($sql, $this->conn);
while(($row = mysql_fetch_row($query)) !== false)
{
if($row[0] == 'Questions')
{
return $row[1];
}
}
return 0;
}
private function connect()
{
$this->conn = mysql_connect($this->host, $this->username, $this->password, true);
}
}
function printHeader()
{
printf("%s v%s\n", PROGRAM_NAME, PROGRAM_VERSION);
printf("Sample size %d seconds. [%d times every %d seconds]\n", NUMBER_SAMPLES * PEROID_LENGTH, NUMBER_SAMPLES, PEROID_LENGTH);
printf("\n");
}
// For nicer formatting, here we get the maximum length of the
// databases names.
$maxDatabaseNameSize = 0;
foreach($databases as $name => $connection)
{
$maxDatabaseNameSize = (strlen($name) > $maxDatabaseNameSize) ? strlen($name) : $maxDatabaseNameSize;
}
if(!AUTO_CLEAR)
{
printHeader();
}
// Our main loop
$stats = array();
while(true)
{
foreach($databases as $name => $connection)
{
// First time round we need to init our stats array with
// a new Limit buffer with the correct sample size
if(!isset($stats[$name]))
{
$stats[$name] = new LimBuffer(NUMBER_SAMPLES);
}
// Add a new item, with the current time, and the total number
// of queries
$stats[$name]->add(array('time' => time(), 'count' => $connection->getQueryCount()));
}
// No point doing anything on the first run through
// Abusing scope here: $name is the from the last loop for the for statement above
if(count($stats[$name]->get()) == 1)
{
sleep(PEROID_LENGTH);
continue;
}
if(AUTO_CLEAR)
{
system("clear");
printHeader();
}
foreach($stats as $name => $stat)
{
$first = $stat->first();
$last = $stat->last();
$period = $last['time'] - $first['time'];
$queryCount = $last['count'] - $first['count'];
$qps = $queryCount / ($period == 0 ? 1 : $period);
printf(" %{$maxDatabaseNameSize}s %5d qps\n", $name, $qps);
}
printf("\n");
// Warn the user that we have yet to fill up out buffer
if(count($stats[$name]->get()) != NUMBER_SAMPLES)
{
printf("Warming up...\n");
printf("\n");
}
sleep(PEROID_LENGTH);
}
?>