📜 ⬆️ ⬇️

Balance

Until recently, our resource database was served on a couple of two servers: Bonnie and Clyde . Clyde is the main server of the project, which responds to all requests, Bonnie is a server that supports databases of other projects and a slave client of the super-Sabra database.

Clyde copes well with his business, a lot of work has been done to optimize the database, so he completely grinded all calls when generating a million or more documents per day. However, in moments of unpredictable peak surges, the load from time to time passed beyond the permissible limits.

Given the ever-increasing amount of data and load, it is time to take a step towards scaling the database hardware resources.
')

A set of serious tools that can help in this matter are well known, for example, these are proxying and sharding tools and a super cluster: MySQL Proxy , Spock Proxy , MySQL Cluster . The latter, of course, is seriously different in nature from the first two.

However, at this stage, it was decided to limit ourselves to a simple and trivial solution, adding some functionality to our Propeller framework, so I decided to make a very simple balancing between Bonnie and Clyde with an assessment of their current load, that is, choosing a minimally loaded server to work out individual resource-intensive parts of the project .

It was possible to avoid the need to use an external server state assessment tool: for the experiment, the ' Threads Running ' option was selected (the number of active threads) from the standard MySQL status report. As the tests showed, it quite objectively reflects the level of server load when it comes to two machines with identical settings.

As a result, this solution came out:


The solution has been applied on the super gab on the main page and on the pages of all blogs for almost a day. In-time development and testing - one day. The result of the application is positive. Servers seem to breathe and breathe evenly most of the time :-)

  PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
 32130 mysql 22 20 0 3401M 3007M kserel 6 30.3H 1.51% mysqld 


  PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
 36399 mysql 26 20 0 4697M 3565M kserel 5 22.6H 1.61% mysqld 


In order not to be unfounded, I will give the corresponding method from our class Db (a low-level class for working with a database).

Attention, this code is really used on the super gab!

<?
/**
*
* @param array $connections
* @param int $permission
*/
function connectFree ($connections, $permission = DB_R) {
if (! is_array($connections)) throw new Exception( 'connectFree: ' );

$statuses = array(); //
$pool = array(); //
$newConnections = array(); //
$minLoad = 0; //
$avName = '' ; //
$report = '' ; //

$cachePref = 'db_cStat_' ; // memcached
$cacheTime = 35; //
$cache = Box:: getInstanceOf( 'CacheMC' ); //

//
foreach ($connections as $conName) {
/**
*
*/
$cacheKey = $cachePref . $conName;
$status = $cache->smartGet($cacheKey);

//
if (!$status) {
//
if (empty($ this ->connections[$conName])) {
try {
$connection = $ this ->connect($conName);
$newConnections[] = $conName;
} catch (Exception $e) {
err( ' ' . $conName);
$report .= $conName . ': ' . '; ' ;

continue ;
}
} else {
$connection = $ this ->connections[$conName];
}

// ,
if (! empty($ this ->conParams[$conName][ 'slave' ]) && !$ this ->checkSlave($connection)) {
$report .= $conName . ': ' . '; '; ;
$status = -2;
$statuses[$conName] = $status;
}

//
// ( , )
if (!$status) {
$status = $ this ->query( 'show status like \'Threads_running\'' , $connection);

//
if ($status && $status->rowCount()) $status = $status->fetchAll();
if (! empty($status[0][ 'Value' ])) $status = $status[0][ 'Value' ]; else $status = -1;
}

//
$cache->smartSet($cacheKey, $status, $cacheTime);
}

//
if ($status) {
//
if ((!$minLoad && $status >= 0) || ($status > 0 && $status < $minLoad)) {
$minLoad = $status;
$avName = $conName;
}

$statuses[$conName] = $status;
$report .= $conName . ': ' . $status . '; ' ;
}
}

//
if (! count($statuses)) throw new Exception( 'connectFree: ' );

//
foreach ($connections as $conName) {
if ($statuses[$conName] == $minLoad) $pool[] = $conName;
}

// ,
if (count($pool) > 1) {
shuffle($pool);
$avName = $pool[0];
}

// , ,
foreach ($newConnections as $cName) {
if ($cName != $avName) $ this ->disconnect($cName);
}

//
if (empty($ this ->connections[$avName])) {
$ this ->connect($avName);
}

//
if ($permission == DB_R) {
if ($ this ->conNameRead != $avName) $ this ->init($avName);
} else {
if ($ this ->conNameWrite != $avName) $ this ->init( null , $avName);
}

//
$report .= ': ' . $avName;
debug($report, 'Db:: connectFree' );
}
?>

Source: https://habr.com/ru/post/40140/


All Articles