Member
Статус: Не в сети Регистрация: 30.03.2009 Откуда: Екатеринбург
у меня такая проблема: компонент сайта (банлист сервера) не показывает русский язык. изначальнов базе кодирвока была utf8 я перекодировал в cl1251 но проблема осталась, как это решить?)
помогите плиз с этим скиптом разобраться - из-за него в выпадающем меню сайиата пункты меняют цвет с определенного в ЦСС на какой-то серый градиент, что не нужно ниразу. Где тут это определяется?
_________________ ASUS A4M88T-M, Athlon II X3 425, 2GB NCP, video - internal ATI 4250, 200GB Samsung HDD, Win7 x64
member+
Статус: Не в сети Регистрация: 16.01.2004 Откуда: Estonia,Tallinn
AzaZeo Это не скрипт а хлам какой-то, ни один вменяемый программист в нём не разберётся (просто не станет) выпадающее меню проще делается и в наше время даже без JS просто на CSS
это файл для входа в админскую зону через пароль. Но он не работает. Выдает ошибки:
Код:
Warning: Cannot modify header information - headers already sent by (output started at Z:\home\localhost\www\phpsite\admin\index.php:2) in Z:\home\localhost\www\phpsite\admin\lock.php on line 6
Warning: Cannot modify header information - headers already sent by (output started at Z:\home\localhost\www\phpsite\admin\index.php:2) in Z:\home\localhost\www\phpsite\admin\lock.php on line 7
Ругаеться на первых 2 хидера В чем может быть проблема? файлик писал не я.
member+
Статус: Не в сети Регистрация: 16.01.2004 Откуда: Estonia,Tallinn
Могут, и легко если включен magic_quotes
mysql_escape_string надо делать в любом случае, а если включен magic_quotes то сначала надо его убрать (stripslash) ибо он не полностью защищает как mysql_escape_string, а только "от дурака"
(так-же это нужно для "логики" скрипта, ведь в $_SERVER['PHP_AUTH_USER'] у нас должен лежать "чистый" ник без "magic_quotes" и "mysql_escape_string", мало ли ниже в скрипте нам понадобится вывести его на экран, или отправить на мыло, или даже нарисовать картинку с таковым...)
Да и проверку на логин лучше вынести сразу в SQL
И пароль в базе хранить не принято, лучше хранить хеш на всякий случай
Т.е лучше типа такого
Код:
if (get_magic_quotes_gpc()) { $_SERVER['PHP_AUTH_USER'] = stripslashes($_SERVER['PHP_AUTH_USER']); $_SERVER['PHP_AUTH_PW'] = stripslashes($_SERVER['PHP_AUTH_PW']); } $username_sql = mysql_escape_string($_SERVER['PHP_AUTH_USER']); $password_sql = md5($_SERVER['PHP_AUTH_PW']); $query = "SELECT * FROM person WHERE login='$username_sql' AND pass = '$password_sql'";
Добавлено спустя 10 часов 52 минуты 38 секунд: И от собак в коде лучше избавиться, это не тот случай когда они нужны (и даже не тот когда допустимы)
Добавлено спустя 1 минуту 36 секунд: А вообще лучше учись сразу с PDO уже на 99% хостеров стоит, удобно просто ОЧЕНЬ
Добавлено спустя 23 минуты 8 секунд: Вот небольшой пример.
Код:
<?php /* сама наша база CREATE TABLE `users` ( `username` varchar(64) NOT NULL, `password` varchar(192) NOT NULL, UNIQUE KEY `username` (`username`), KEY `password` (`password`) ) ENGINE=MyISAM;
INSERT INTO `users` (`username`, `password`) VALUES ('Vladson', '123456'); */
// тут всё логично if (!isset($_SERVER['PHP_AUTH_USER'])) { Header ("WWW-Authenticate: Basic realm=\"Admin Page\""); Header ("HTTP/1.0 401 Unauthorized"); exit(); }
// тут посылаем нафик кавычки if (get_magic_quotes_gpc()) { $_SERVER['PHP_AUTH_USER'] = stripslashes($_SERVER['PHP_AUTH_USER']); $_SERVER['PHP_AUTH_PW'] = stripslashes($_SERVER['PHP_AUTH_PW']); }
// конект к базе (как я понимаю у тебя в скрипте он раньше) $dbh = new PDO('mysql:dbname=test;host=localhost', 'root', '');
// готовим запрос $sth = $dbh->prepare('SELECT * FROM `users` WHERE `username` = :username AND `password` = :password;');
// Получаем ответ $user_data = $sth->fetch(PDO::FETCH_ASSOC);
// false в случае если юзера нет или пароль неверный if ($user_data == false || !is_array($user_data)) { Header ("WWW-Authenticate: Basic realm=\"Admin Page\""); Header ("HTTP/1.0 401 Unauthorized"); exit(); }
// или вся информация в $user_data если всё удачно var_dump($user_data); ?>
Вопрос скорее чисто по MySQL. Есть таблица geoip с ~4M записей, состоящая из 3ёх столбцов: start_ip, end_ip и loc_id. IP хранятся в числовом формате. Нужно проверить, к какому loc_id относится определённый IP.
Делаю запрос:
Код:
SELECT loc_id FROM geoip_cities_mem WHERE (2999999999 BETWEEN start_ip AND end_ip) LIMIT 1;
Выполняется он примерно 0,2-0,4 секунды, а это долго. Как можно оптимизировать этот запрос?
Я пробовал таблицы MYISAM и MEMORY - разница между ними не очень большая (про InnoDB я молчу - там запрос занимает около 30 секунд). Пробовал секционирование для обоих типов таблиц, но то ли я чего-то не так кромсаю, то ли секционирование просто неэффективно. Думаю, что первое - где-то я недорыл, так что буду признателен за совет с примером в этом направлении. Пробовал индексы и первичные ключи - толку никакого, а на MYISAM сплошной вред - время запроса возрастает.
Есть вариант костыля - создать отдельную БД под geoip, а в ней 1000 таблиц типа MEMORY с названиями geoip_[первый_start_ip]_[последний_end_ip], а потом сначала определять имя таблицы, а затем уже искать в этой таблице. Но это самый крайний вариант. Есть ещё совершенно адское решение - проиндексировать вообще все IP адреса из таблицы, чтобы таблица имела вид |ip|loc_id|. Но в таком случае таблица станет занимать несколько гигабайт, а в памяти её можно будет размещать только на очень дорогом сервере.
Версия MySQL: 5.1.48 ОС: Windows Vista/7 Процессор: AMD Athlon 6000+/Intel Core2Duo P8700 Память: 2GB/4GB (на обеих системах результаты примерно одинаковые)
Добавлено спустя 11 часов 39 минут 6 секунд: Нашёл на Хабре решение, правда оно было в одном-единственном посте (не про MySQL) в одном из комментариев.
Решение: Удалить из таблицы столбец end_ip (а можно, в принципе и не удалять - разница по скорости где-то 20%), оставив только start_ip и loc_id. Затем добавить простой индекс на start_ip. Код таблицы:
Код:
CREATE TABLE geoip_city_blocks( start_ip INT(10) UNSIGNED NOT NULL, loc_id INT(5) UNSIGNED NOT NULL, UNIQUE INDEX UK_geoip_city_blocks_start_ip (start_ip) ) ENGINE = MYISAM AVG_ROW_LENGTH = 9;
Запрос должен выглядеть так:
Код:
SELECT loc_id FROM geoip_city where start_ip <= @ip order by start_ip desc limit 1;
Запрос открыт за 0,001c [0,001c выполнение, < 0,001c выборка]
Единственная проблема: если нужной группы IP изначально не было в базе, loc_id определится неправильно, но это меньшее зло, на мой взгляд.
Ещё нужно иметь ввиду, что запрос "SELECT SQL_SMALL_RESULT DISTINCT loc_id FROM geoip_city_blocks WHERE @ip BETWEEN start_ip AND end_ip LIMIT 1;" (если оставить end_ip, конечно) будет выполняться в таком случае уже секунды.
Теоретически, при правильном ручном секционировании, с равномерным распределением значений start_ip по секциям, можно ещё сильнее увеличить производительность. Но, как я ни старался, ничего путного с секционированием у меня не выходит
Добавлено спустя 4 минуты 45 секунд: PS: Спасибо товарищу Wott с Хабра.
Добавлено спустя 13 минут 56 секунд: PPS: Наилучшие типы таблиц: для geoip_blocks - MYISAM, а для geoip_locations(в которой страны, города, широта и долгота)- InnoDB. Сейчас ещё поиграюсь с секционированием таблицы geoip_locations.
Последний раз редактировалось Cruel_Crow 13.07.2010 15:32, всего редактировалось 1 раз.
member+
Статус: Не в сети Регистрация: 16.01.2004 Откуда: Estonia,Tallinn
Я бы предложил поискать GeoIP на файлах, есть одна очень быстрая реализация, но я совершенно не помню где я её видел, то-ли на phpclub то-ли на ru-board...
Так-же если действительно
Cruel_Crow писал(а):
Запрос открыт за 0,001c
против
Cruel_Crow писал(а):
0,2-0,4 секунды
То
Cruel_Crow писал(а):
можно, в принципе и не удалять
И делать два запроса, один чтоб проверить нижнюю границу второй чтоб верхнюю чтоб избежать
Я бы предложил поискать GeoIP на файлах, есть одна очень быстрая реализация, но я совершенно не помню где я её видел, то-ли на phpclub то-ли на ru-board...
Нужно именно SQL-решение. Решение MaxMind быстрое, но где-то в тех же пределах, что и оптимизированный MySQL запрос.
Цитата:
И делать два запроса, один чтоб проверить нижнюю границу второй чтоб верхнюю чтоб избежать
Хе-хе, уже так и сделал, как раз хотел здесь отпостить Таблица:
Код:
CREATE TABLE geoip_city_blocks( start_ip INT(10) UNSIGNED NOT NULL, end_ip INT(10) UNSIGNED NOT NULL, loc_id INT(5) UNSIGNED NOT NULL, UNIQUE INDEX UK_geoip_city_blocks_end_ip (end_ip), UNIQUE INDEX UK_geoip_city_blocks_start_ip (start_ip) ) ENGINE = MYISAM;
Запрос с одиночной проверкой:
Код:
SET @ip = INET_ATON('12.4.48.249'); SELECT loc_id FROM geoip_city_blocks WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 1;
Запрос открыт за 0,002c [0,001c выполнение, 0,001c выборка]
Запрос с двойной проверкой:
Код:
SET @ip = INET_ATON('12.4.48.249'); SELECT t.loc_id FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= @ip ORDER BY end_ip ASC LIMIT 1) t WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 1;
Запрос открыт за 0,003c [0,002c выполнение, 0,001c выборка]
Если задать несуществующий IP, например "SET @ip = INET_ATON('192.168.0.1');", то второй запрос верёнт NULL.
Теперь протестируем всё тоже самое, но с бОльшим LIMIT (практического толку никакого -чисто для более точной оценки производительности): На существующем IP:
Код:
SET @ip = INET_ATON('121.120.220.220');
Код:
SELECT loc_id FROM geoip_city_blocks WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 100000;
Запрос открыт за 0,922c [0,290c выполнение, 0,632c выборка]
Код:
SELECT t.loc_id FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= @ip ORDER BY end_ip ASC LIMIT 100000) t WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 1;
Запрос открыт за 0,795c [0,794c выполнение, 0,001c выборка]
На несуществующем IP:
Код:
SET @ip = INET_ATON('127.0.0.1');
Код:
SELECT loc_id FROM geoip_city_blocks WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 100000;
Запрос открыт за 0,969c [0,287c выполнение, 0,682c выборка]
Код:
SELECT t.loc_id FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= @ip ORDER BY end_ip ASC LIMIT 100000) t WHERE start_ip <= @ip ORDER BY start_ip DESC LIMIT 1;
Запрос открыт за 0,802c [0,801c выполнение, 0,001c выборка]
Добавлено спустя 6 минут 44 секунды: То есть, на один запрос при одиночной проверке уходит где-то 0,00000922 сек. (9-10 usec), а при двойной - 0,00000795 сек. (8-9 usec).
Результаты тестирования меня озадачили, если честно. Каким образом двойная проверка выполняется быстрее одиночной при большом количестве результатов - загадка.
Последний раз редактировалось Cruel_Crow 13.07.2010 15:32, всего редактировалось 1 раз.
Member
Статус: Не в сети Регистрация: 03.04.2010 Откуда: Полоцк
Cruel_Crow писал(а):
То есть, на один запрос при одиночной проверке уходит где-то 0,00000922 сек. (9-10 usec), а при двойной - 0,00000795 сек. (8-9 usec).
Обратите внимание на это:
Cruel_Crow писал(а):
0,922c [0,290c выполнение, 0,632c выборка]
Cruel_Crow писал(а):
0,795c [0,794c выполнение, 0,001c выборка]
У вас выполнение первого запроса идет в 2,7 раза быстрее чем второго, но куча времени уходит на выборку. Вот вам и разница получается из-за выборки. Нужно учитывать не только время выполнения запроса, но и объемы возвращаемых данных.
AlimAlex Да, но для того, кто будет общаться с MySQL (например, php), важно всё-таки общее время.
---
Написал вот несколько функций для работы с GeoIpCity в MySQL. Для начала, таблицы:
Код:
CREATE TABLE geoip_city_blocks( start_ip INT(10) UNSIGNED NOT NULL, end_ip INT(10) UNSIGNED NOT NULL, loc_id INT(5) UNSIGNED NOT NULL, UNIQUE INDEX UK_geoip_city_blocks_end_ip (end_ip), UNIQUE INDEX UK_geoip_city_blocks_start_ip (start_ip) ) ENGINE = MYISAM AVG_ROW_LENGTH = 13 CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE geoip_city_location( loc_id INT(11) UNSIGNED NOT NULL, country VARCHAR(2) NOT NULL, region VARCHAR(2) DEFAULT NULL, city VARCHAR(255) DEFAULT NULL, postal_code VARCHAR(255) DEFAULT NULL, latitude FLOAT(8, 4) DEFAULT NULL, longitude FLOAT(8, 4) DEFAULT NULL, area_code INT(5) DEFAULT NULL, PRIMARY KEY (loc_id) ) ENGINE = INNODB AVG_ROW_LENGTH = 57 CHARACTER SET utf8 COLLATE utf8_general_ci;
Сами функции:
Код:
CREATE FUNCTION determine_reserved_ip(ip_numeric INT(10) UNSIGNED) RETURNS bit(1) DETERMINISTIC SQL SECURITY INVOKER NO SQL COMMENT 'Returns NULL if IP is reserved, 0 when IP is local. 1 when ip ok' BEGIN IF ((ip_numeric < 0) OR (ip_numeric > 4294967295)) THEN RETURN NULL; END IF;
#determine reserved ip addresses /* v0.0.0.0/8 v10.0.0.0/8 v14.0.0.0/8 v192.0.2.0/24 v192.88.99.0/24 v198.18.0.0/15 v224.0.0.0/4 v240.0.0.0/4 - multicast and other
#locals: v127.0.0.0/8 - loopback v169.254.0.0/16 v172.16.0.0/12 192.168.0.0/16 */ IF ( (ip_numeric & 4278190080 = 0)/*0.0.0.0/8*/ OR (ip_numeric & 4278190080 = 167772160)/*10.0.0.0/8*/ OR (ip_numeric & 4278190080 = 234881024)/*14.0.0.0/8*/ OR (ip_numeric & 4294967040 = 3221225984)/*192.0.2.0/24*/ OR (ip_numeric & 4294967040 = 3227017984)/*192.88.99.0/24*/ OR (ip_numeric & 4294836224 = 3323068416)/*198.18.0.0/15*/ OR (ip_numeric & 3758096384 = 3758096384)/*224.0.0.0/4*/ OR (ip_numeric & 4026531840 = 4026531840)/*240.0.0.0/4*/ ) THEN RETURN NULL; END IF;
IF ( (ip_numeric & 4278190080 = 2130706432)/*127.0.0.0/8*/ OR (ip_numeric & 4294901760 = 2851995648)/*169.254.0.0/16*/ OR (ip_numeric & 4293918720 = 2886729728)/*172.16.0.0/12*/ OR (ip_numeric & 4294901760 = 3232235520)/*192.168.0.0/16*/ ) THEN RETURN 0; END IF;
RETURN 1; END
Код:
CREATE FUNCTION get_loc_id_from_ip(ip VARCHAR(255)) RETURNS int(7) DETERMINISTIC SQL SECURITY INVOKER READS SQL DATA COMMENT 'Returns NULL when address is unproper and 0 when local address' BEGIN DECLARE ip_check BIT DEFAULT NULL; DECLARE ip_numeric INT(10) UNSIGNED; SET ip_numeric = INET_ATON(ip); IF (ip_numeric IS NULL) THEN RETURN NULL; END IF;
SET ip_check = determine_reserved_ip(ip_numeric); IF (ip_check IS NULL) THEN RETURN NULL; END IF; IF (ip_check = 0) THEN RETURN 0; END IF;
RETURN (SELECT t.loc_id FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= ip_numeric ORDER BY end_ip ASC LIMIT 1) t WHERE start_ip <= ip_numeric ORDER BY start_ip DESC LIMIT 1);
RETURN NULL; END
Код:
CREATE FUNCTION get_location_from_loc_id(loc_id INT) RETURNS varchar(255) CHARSET utf8 DETERMINISTIC SQL SECURITY INVOKER READS SQL DATA COMMENT 'Reads locations table and returns country/region/city' BEGIN IF (loc_id = NULL) THEN RETURN NULL; END IF; IF (loc_id = 0) THEN RETURN 'LAN'; END IF; #ip is in local network
RETURN (SELECT CONCAT(`country`, '/', `region`, '/', `city`) FROM geoip_city_location WHERE geoip_city_location.loc_id = loc_id ORDER BY geoip_city_location.loc_id ASC LIMIT 1); END
Код:
CREATE FUNCTION get_location_from_ip(ip VARCHAR(255)) RETURNS varchar(255) CHARSET utf8 DETERMINISTIC SQL SECURITY INVOKER READS SQL DATA COMMENT 'Determines geographic location by ip address' BEGIN DECLARE ip_check BIT DEFAULT NULL; DECLARE ip_numeric INT(10) UNSIGNED; SET ip_numeric = INET_ATON(ip); IF (ip_numeric IS NULL) THEN RETURN NULL; END IF;
SET ip_check = determine_reserved_ip(ip_numeric); IF (ip_check IS NULL) THEN RETURN NULL; END IF; IF (ip_check = 0) THEN RETURN 'LAN'; END IF;
RETURN (SELECT CONCAT(`country`, '/', `region`, '/', `city`) FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= ip_numeric ORDER BY end_ip ASC LIMIT 1) t INNER JOIN geoip_city_location ON t.loc_id = geoip_city_location.loc_id WHERE start_ip <= ip_numeric ORDER BY start_ip DESC LIMIT 1); END
Добавлено спустя 7 минут 27 секунд: BIT determine_reserved_ip(ip_numeric INT(10) UNSIGNED) - вспомогательная функция. Принимает IP в числовом формате (можно воспользоваться INET_ATON()) Возвращает NULL, если IP был некорректный или зарезервированный, '0' - если IP из локалки и '1', если всё в порядке.
INT get_loc_id_from_ip(ip VARCHAR(255)) - определяет по IP-адресу ID местоположения, который можно получить из таблицы geoip_city_location.
VARCHAR get_location_from_loc_id(loc_id INT) - если есть ID, полученный из предыдущей функции, можно воспользоваться этой для определения местоположения. Оно вернётся в формате Страна/Регион/Город (NULL - если такого ID нету и 'LAN', если адрес из локальной сети).
VARCHAR get_location_from_ip(ip VARCHAR(255)) - объединяет две предыдущие функции, возвращая Страну/Регион/Город прямо из IP.
а попробуй сделать такой индекс: UNIQUE INDEX geoip_ip_range (start_ip desc, end_ip ask)
и в запросе писать where @ip >= start_ip and @ip <= end_ip
по-идее оно тогда должно прошуршать по первой колонке индекса, потом по второй и быстро-быстро найти
да, ещё попробуй explain plan посмотреть, очень сильно помогает при таких задачах. секционировать имхо смысла никакого, если будет активно использоваться индекс, т.к. проблем с извлечением данных у тебя вроде как нету
тоже попробовал вчера вечером. тоже не проканало приучил меня оракл ко всяким вкусностям... но меня всё равно не покидает мысль, что надо как-то заставить его юзать составной индекс. ну или если он совсем не хочет, то, в теории, шустрее всего будет запрос SELECT t.loc_id FROM (SELECT loc_id, start_ip FROM geoip_city_blocks WHERE end_ip >= ip_numeric ORDER BY end_ip ASC LIMIT 1) t WHERE start_ip <= ip_numeric ORDER BY start_ip DESC LIMIT 1
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения