Быстрая база на файлах для узких целей

workOnFood

Новичок
Файл m_index.bin у меня в ANSI, и возможно я ошибаюсь так как не проверял все битые позиции, но те что проверял при открытии файла через редактор отображают так или иначе перевод строки.

PHP:
$fp = fopen('m_index.bin','rb');
$bin = fgets($fp,13);
var_dump(unpack('N3',$bin));
Warning выдается даже в таком случае. В файле m_index.bin описанные выше данные перекодированные в двоичный код.
 

workOnFood

Новичок
В мане который я читал по fgets() написано что она считывает length - 1 байт.
 

MiksIr

miksir@home:~$
Может ты даже прочитал - почему? И даже прочитал вообще что делает fgets и чем она отличается, например, от fgetc и fread?
 

workOnFood

Новичок
Блин! Спасибо.=)

До этого читал про fputs и fwrite и там написано что они являются синонимами. Подумал что с fgets и fread то же самое, а оказывается бинарно безопасным является только fread.
 

workOnFood

Новичок
Дописал индекс, потестил самые часто используемые функции на ab, с базой не сравнивал, но помоему результат неплохой. Все тестил командой - c50 -n500.

0.055 - Создание новой темы(назовем так) - по сути диалог между двумя юзерами в который можно добавлять других юзеров
0.0015 - Запись сообщения размером в 1кб
0.005 - Выборка 20 сообщений каждое размером 1 кб в массив полностью сформированный для вывода array(array(имя, аватар, дата(в нужном формате), сообщение)
 

vovanium

Новичок
потестил самые часто используемые функции на ab, с базой не сравнивал, но помоему результат неплохой
вот интересно на основании чего можно судить что результат неплохой, если ни с чем не сравнивать? :) и опять же, уже писал в начале, возьми добавь хотя бы 10-50 тысяч сообщений в свою базу, а потом тестируй. Или ты ожидаешь что при твоей мега нагрузке сообщений будет 20 штук?
 

workOnFood

Новичок
вот интересно на основании чего можно судить что результат неплохой, если ни с чем не сравнивать? :)
Ну я до этого иногда замерял скорость запросов в базу. Некоторое впечатление сформировалось, возможно я не прав.

и опять же, уже писал в начале, возьми добавь хотя бы 10-50 тысяч сообщений в свою базу, а потом тестируй. Или ты ожидаешь что при твоей мега нагрузке сообщений будет 20 штук?
Вот по этому поводу у меня как раз был вопрос, вот я допустим запускаю ab -c50 -n500, то есть получается моя страница открывается 500 раз по 50 конкурентных запросов - насколько я понял должно выходить 500*50 запросов. Вот тут я скорее всего не прав потому что при прогонке скрипта на запись он оставляет только 500 сообщений, объясните пожалуйста.

А по поводу 10-50 тысяч, я не совсем понял что ты имеешь ввиду. Пытаться вытащить оттуда такое количество за один запрос? Тут любая база загнется, мне кажется, ну выполняться по крайней мере будет очень долго.

Или имеешь ввиду добавить в базу 10-50 тыс. а выбирать как и выбирал скажем 20шт.? В таком случае производительность упадет только на операции удаления юзера из группы так как значительно вырастет количество записей о группах в файле пользователя. Да тут слабое место надо думать.

Я подправил код поэтому логичнее будет его обсуждать если его будет всем видно -

PHP:
class Filebase{
	protected $dirOfBase;
	protected $basedir;
	function __construct($path_to_filebase){
		$this->dirOfBase = $path_to_filebase;
	}
	function sys_getUserArr($id){
		return $arr = array('Vasia','img/avatar.png');
	}
	function sys_addUserOrGr($id,$gr_id){
		if(file_exists($this->dirOfBase.'users/'.$id.'.fb')){
				$fp = fopen($this->dirOfBase.'users/'.$id.'.fb','a');
				flock($fp,LOCK_EX);
				fputs($fp,$gr_id."\t");
				fclose($fp);
			}else{
				$user = $this->sys_getUserArr($id);
				file_put_contents($this->dirOfBase.'users/'.$id.'.fb',$id."\t".$user[0]."\t".$user[1]."\t".$gr_id."\t");
			}
	}
	function startGroup($my_id,$other_id,$my_mes,$title){
		$my_mes = preg_replace("/\n/s",'<br/>',htmlspecialchars($my_mes));
		$title = preg_replace("/\n/s",' ',htmlspecialchars($title));
		$fp = fopen($this->dirOfBase.'groups/maxgr.fb','r+');
		flock($fp,LOCK_EX);
		$gr_id = fgets($fp);
		fseek($fp,0);
		fputs($fp,++$gr_id);
		fclose($fp);
		$new_group = $my_id.'|1 '.$other_id.'|0 ';
		foreach(array($my_id,$other_id) as $id){
			$this->sys_addUserOrGr($id,$gr_id);
		}
		mkdir($this->dirOfBase.'groups/'.$gr_id);
		$fp = fopen($this->dirOfBase.'groups/'.$gr_id.'/gr.fb','a');
		$pos = filesize($this->dirOfBase.'groups/'.$gr_id.'/gr.fb');
		fputs($fp,$my_mes."\n");
		fclose($fp);
		$fp = fopen($this->dirOfBase.'groups/'.$gr_id.'/m_index.bin','wb');
		$data_bin = pack('N3',$my_id,time(),$pos);
		fputs($fp,$data_bin);
		fclose($fp);
		file_put_contents($this->dirOfBase.'groups/'.$gr_id.'/users.fb',$new_group);
		file_put_contents($this->dirOfBase.'groups/'.$gr_id.'/title.fb',$title);
	}
	function inviteUser($id,$gr){
		$this->sys_addUserOrGr($id,$gr);
		$fp = fopen($this->dirOfBase.'groups/'.$gr.'/users.fb','a');
		fputs($fp,$id.'|0 ');
		fclose($fp);
	}
	function writeMessage($id,$gr,$mes){
		$mes = preg_replace("/\n/s",'<br/>',htmlspecialchars($mes));
		$fp = fopen($this->dirOfBase.'groups/'.$gr.'/gr.fb','a');
		flock($fp,LOCK_EX);
		$pos = filesize($this->dirOfBase.'groups/'.$gr.'/gr.fb');
		fputs($fp,$mes."\n");
		fclose($fp);
		$data_bin = pack('N3',$id,time(),$pos);
		$fp = fopen($this->dirOfBase.'groups/'.$gr.'/m_index.bin','ab');
		flock($fp,LOCK_EX);
		fputs($fp,$data_bin);
		fclose($fp);
	}
	function getDialogArr($gr,$from=0,$howmany=0){
		$all = 0-filesize($this->dirOfBase.'groups/'.$gr.'/m_index.bin');
		$fp = fopen($this->dirOfBase.'groups/'.$gr.'/m_index.bin','rb');
		flock($fp,LOCK_EX);
		$from = $from=='all'?$all:$from = 0 - $from*12;
		$howmany = 0 - $howmany*12;
		for($i=$from;$i<$howmany;$i+=12){
			fseek($fp,$i,SEEK_END);
			$index_arr[] = unpack('N3',fread($fp,12));
		}
		fclose($fp);
		$fp =fopen($this->dirOfBase.'groups/'.$gr.'/gr.fb','r');
		foreach($index_arr as $mes_x){
			fseek($fp,$mes_x[3]);
			$mes = fgets($fp);
			if(time()-$mes_x[2]>86400)$df = "d.m.Y H:i";
			else $df = "H:i";
			preg_match("/^.*?\t(.*?)\t(.*?)\t/i",file_get_contents($this->dirOfBase.'users/'.$mes_x[1].'.fb'),$user);
			$disp_arr[] = array($user[1],$user[2],date($df,$mes_x[2]),$mes);
		}
		fclose($fp);
		return $disp_arr;
	}
	function groupDelUser($id,$gr){
		$gr_users = file_get_contents($this->dirOfBase.'groups/'.$gr.'/users.fb');
		$gr_users = explode(" ",$gr_users);
		foreach($gr_users as $user){
			$u_arr = explode('|',$user);
			if($u_arr[0]!=$id  && $u_arr[0]!=''){
				$allowed.=$user.' ';
			}
		}
		file_put_contents($this->dirOfBase.'groups/'.$gr.'/users.fb',$allowed);
		$user = file_get_contents($this->dirOfBase.'users/'.$id.'.fb');
		$user = explode("\t",$user);
		$allowed = $user[0]."\t".$user[1]."\t".$user[2]."\t";
		for($i=3;$i<count($user);$i++){
			if($user[$i]!=$gr && $user[$i]!=''){
				$allowed.=$user[$i]."\t";
			}
		}
		file_put_contents($this->dirOfBase.'users/'.$id.'.fb',$allowed);
	}
	function renameGroup($gr,$title){
		$title = preg_replace("/\n/s",' ',htmlspecialchars($title));
		file_put_contents($this->dirOfBase.'groups/'.$gr.'/title.fb',$title);
	}
}
 

vovanium

Новичок
Ну я до этого иногда замерял скорость запросов в базу
:) для начала какую именно, базы как бы разные бывают, во вторых запросы тоже бывают разные, могут быть запросы которые и секунды выполняются, а могут и доли секунды, так что на глаз обычно ничего не делается, когда идет речь о сравнении производительности.

Ну делается 500 раз, по 50 одновременно чего им перемножаться

Имел ввиду что из базы в 10-50 тыс сообщений, вытаскивать порцию сообщений, пусть будет по 20. Причем насколько я понимаю если у тебя предполагается какая-то история сообщений, то нужно проверить чтение куска из 20 сообщений со случайной позиции в файле,
 

workOnFood

Новичок
:) для начала какую именно, базы как бы разные бывают, во вторых запросы тоже бывают разные, могут быть запросы которые и секунды выполняются, а могут и доли секунды, так что на глаз обычно ничего не делается, когда идет речь о сравнении производительности.
Абсолютно согласен что разные запросы разная скорость. Имел ввиду mysql. Подчеркну, исключительно субъективное впечатление.

Ну делается 500 раз, по 50 одновременно чего им перемножаться
Я наверное не совсем точно объяснил как я это понимаю - я вижу это как эмуляцию того что 50 людей заходят с разных машин и делают одновременно или почти одновременно запрос, и так 500 раз. Должно перемножаться.

Имел ввиду что из базы в 10-50 тыс сообщений, вытаскивать порцию сообщений, пусть будет по 20. Причем насколько я понимаю если у тебя предполагается какая-то история сообщений, то нужно проверить чтение куска из 20 сообщений со случайной позиции в файле,
Не вижу честно говоря почему должно работать медленнее но для очистки совести проверю.

P.S. Из всего выше сказанного я вынес для себя одну простую истину - надо учить ассемблер и процессы протекающие в памяти. Точнее не из того что было сказано именно в этом топике, но вот почему-то именно сейчас у меня появилась стойкая уверенность в том что если этого не сделать, хорошего программиста из меня не получится.
 

workOnFood

Новичок
Для чтения тебе требуется не exclusive lock, а shared (LOCK_SH).
С этими параметрами не до конца разобрался. Не понимаю LOCK_SH закрывает файл от чтения или применяется при чтении но закрвывает от записи и от чтения или что-то еще?
 

Вурдалак

Продвинутый новичок
LOCK_SH не позволяет залочить файл эксклюзивно (LOCK_EX). Короче, закрывает файл от записи, но позволяет читать. «Не позволяет» означает, что скрипт с LOCK_EX будет ждать, пока все скрипты с LOCK_SH/LOCK_EX не закроют файл или не снимут блокировку.
 

vovanium

Новичок
Имел ввиду mysql. Подчеркну, исключительно субъективное впечатление.
ну собственно это типичная ошибка новичков, типа да зачем мне эти базы я сам лучше в 100 раз напишу, когда-то давно я тоже такое проходил :) кстати тебе еще говорили про SQLite ;)
надо учить ассемблер и процессы протекающие в памяти
нужно не ассемблер учить, а документацию читать по тому языку который учишь, если ты будешь ассемблер учить, так же как php, то ничего особо хорошего не будет.
Не вижу честно говоря почему должно работать
ты много чего не видишь, чтобы видеть нужен опыт, опыта без практики не бывает.
 

workOnFood

Новичок
LOCK_SH не позволяет залочить файл эксклюзивно (LOCK_EX). Короче, закрывает файл от записи, но позволяет читать. «Не позволяет» означает, что скрипт с LOCK_EX будет ждать, пока все скрипты с LOCK_SH не закроют файл или не снимут блокировку.
Ага. То есть если оставить LOCK_ЕХ, все обратившиеся за чтением будут ждать, явное упущение в производительности. Спасибо.
 

workOnFood

Новичок
ну собственно это типичная ошибка новичков, типа да зачем мне эти базы я сам лучше в 100 раз напишу, когда-то давно я тоже такое проходил :) кстати тебе еще говорили про SQLite ;)
Да нет, просто мне кажется вроде неплохие результаты.

нужно не ассемблер учить, а документацию читать по тому языку который учишь, если ты будешь ассемблер учить, так же как php, то ничего особо хорошего не будет.
Если ты о том что я буду писать за деньги дрова операционки и компиляторы, то нет я этого делать не собираюсь=) Просто хочу лучше понимать процессы протекающие в программе которую пишу, мне кажется тогда будет значительно меньше глупых ошибок, и глупых вопросов на форумах.

ты много чего не видишь, чтобы видеть нужен опыт, опыта без практики не бывает.
Провел тест с выборкой сообщений. Размер базы 200мб размер каждого файла с сообщениями 1300кб размер индекса 12кб. По 1000 сообщений в каждом.

Также протестил отдельно с размером файла сообщений 20мб идекс 206кб, с рандомной выборкой по 20 сообщений. Здесь около 20к сообщений.

Результат тот же.
 

Techmind

Новичок
Поддержу автора в благом начинании =)
Построит свою велосипедную базу данных поймет, что и как делается в нормальных реляционных\нереляционных базах данных и получит профит)

Хотя почитать книжку все же легче)
 

vovanium

Новичок
мне кажется тогда будет значительно меньше глупых ошибок, и глупых вопросов на форумах
ассемблер тут абсолютно не причем, нужно читать книги и документацию, методом тыка нормально язык программирования не выучишь, или ты думаешь все здесь дававшие советы поголовно на ассемблере пишут? :)
 

workOnFood

Новичок
ассемблер тут абсолютно не причем, нужно читать книги и документацию, методом тыка нормально язык программирования не выучишь, или ты думаешь все здесь дававшие советы поголовно на ассемблере пишут? :)
Не знаю пишет ли кто-то из комментаторов на ассемблере, но некоторые точно его знают. Я думаю знание ассемблера, это плюс для любого программиста. Меньше будет заблуждений, а следовательно выше качество кода.
 

vovanium

Новичок
какая связь между качеством кода и ассемблером, ты думаешь на ассемблере нельзя говнокод написать? :)
и с чего это на ассемблере будет меньше заблуждений? заблуждений может быть меньше, если будешь читать документацию по функциям, а не наугад использовать (с формулировкой, я думал/предполагал и т.п. что эта функция так работает), не уверен в том что делает функция почитай доки, тут как бы никакой магии да и доки не засекречены.
а ассемблер нужен будет если какие-то микроконтроллеры будет программировать, и нужно будет за каждый байт программы воевать.
 
Сверху