Krotko i na temat. Szukam optymalizacji pod katem redukcji zuzycia pamieci i przyblizonym utrzymaniu czasu procesu/zapytania. Przy prawie pelnej tabeli dla zakresu np. 501000000-500000000
pozera w granicach ~50MB (zalezy od konfiguracji serwera) jednak chcialbym to zredukowac, bo... tych procesow bedzie nie mniej niz 25 wiec... 1,25GB co jest dla mnie zbyt duza wartoscia. Zalozenia podstawowe. Na poczatku zliczamy ilosc rekordow dla zakresu. Jezeli mniej niz 50% to pobieramy aktualne i w petli uzupelniamy braki. Jezeli jest na odwrot to potrzebuje wszystkie brakujace ID do petli aby je uzupelnic. Ma to na celu redukcje pozartej pamieci. Prosze sie nie przejmowac syfem w metodzie some_operation()
- wklejone na szybko. Odsylanie do Google... to nie rozwiazanie, bo... np. takie rozwiazania (http://stackoverflow.com/questions/12325132/mysql-get-missing-ids-from-table) trwaja za dlugo. Tworzenie tymczasowej tabeli dla miliona rekordow tez mija sie z celem jezeli przemnozy sie przez ilosc procesow (nie bede ich opozniac celowo). Zastanawialem sie nad procedura aby pobrac wszystkie i wylapac brakujace. Moze jest jakies zapytanie, ktore jest optymalne i wykona cel?
index.php
<?php
$config['mysql']['host'] = 'localhost';
$config['mysql']['database'] = 'test2';
$config['mysql']['login'] = 'root';
$config['mysql']['password'] = '';
// Jezeli nie masz ustawionego to... juz masz :D
set_time_limit(0);
$range = '501000000-500000000';
require './debug.class.php';
require './test.class.php';
$test = new test($config);
// $test -> add_records($range);
// $test -> create_random_holes();
$test -> some_operation($range);
?>
debug.class.php
<?php
class debug
{
static private $units = array('B','kB','MB','GB');
static public function get_memory_usage()
{
return debug::convert(memory_get_usage()).'/'.debug::convert(memory_get_usage(true));
}
static private function convert($size)
{
$unit = floor(log($size, 1024));
return round($size / pow(1024, $unit), 2).' '.debug::$units[$unit];
}
}
?>
test.class.php
<?php
class test
{
private $db;
public function test($config)
{
$this -> db = new mysqli($config['mysql']['host'], $config['mysql']['login'], $config['mysql']['password'], $config['mysql']['database']);
if($this -> db -> connect_errno !== 0)
{
throw new Exception('Could not connect to database');
}
else
{
// $this -> db -> set_charset('utf8');
}
}
public function add_records($range)
{
// PHP_EOL for CLI or <br> for browser
echo debug::get_memory_usage().'<br>';
$range = explode('-', $range);
$range[0] = (int)$range[0];
$range[1] = (int)$range[1];
$stmt = $this -> db -> prepare('INSERT INTO players (account_id) VALUES (?)');
$this -> db -> autocommit(false);
while($range[0] >= $range[1])
{
// Fill the table
$stmt -> bind_param('i', $range[0]);
$stmt -> execute();
--$range[0];
}
if($this -> db -> commit() === false)
{
$this -> db -> rollback();
}
$this -> db -> autocommit(true);
echo debug::get_memory_usage().'<br>';
}
public function create_random_holes()
{
// May take a while
$stmt = $this -> db -> prepare('DELETE FROM players ORDER BY RAND() LIMIT 10000');
$stmt -> execute();
}
public function some_operation($range)
{
// Uwaga syf
echo debug::get_memory_usage().'<br>';
$range = explode('-', $range);
$range[0] = (int)$range[0];
$range[1] = (int)$range[1];
$stmt = $this -> db -> prepare('SELECT account_id FROM players WHERE account_id BETWEEN ? AND ? ORDER BY account_id DESC');
$stmt -> bind_param('ii', $range[1], $range[0]);
$stmt -> execute();
$result = $stmt -> get_result();
$stmt -> free_result();
// $stmt -> close();
// $stmt -> store_result();
// $stmt -> bind_result($account_id);
// $stmt -> fetch();
while ($obj = $result->fetch_object())
{
// echo $obj -> account_id.'<br>';
}
echo debug::get_memory_usage().'<br>';
// var_dump($result);
exit;
// $result = $stmt->get_result();
// $cached = new CachingIterator($result, CachingIterator::FULL_CACHE);
while($range[0] >= $range[1])
{
if($range[0] === $account_id)
{
$stmt -> fetch();
}
else
{
// Adding missing player
// $this -> add_player($range[0]);
}
--$range[0];
}
echo debug::get_memory_usage().'<br>';
}
private function add_player($account_id)
{
$stmt1 = $this -> db -> prepare('INSERT INTO players (account_id) VALUES (?)');
$stmt1 -> bind_param('i', $account_id);
$stmt1 -> execute();
return $stmt -> affected_rows === 1;
}
}
?>
Struktura tabeli
CREATE TABLE IF NOT EXISTS `players` (`account_id` bigint(1) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
ALTER TABLE `players` ADD PRIMARY KEY (`account_id`);
PS
Widzialem kilka tematow na polskich forach programistycznych (w tym takze na tym) dotyczacych tego problemu jednak rozwiazania, ktore padly nie satysfakcjonuja mnie, bo to nie ta skala.