Делаю сайт знакомств - соответсвенно одна из основных нагрузок будет использование поиска по людям (это вообще на главной странице будет) - по этому очень важно максимально наладить индексы т.к. пользователи будут добавляться в 1000 раз реже чем будут использоваться возможностью поиска. Я сделал такую структуру - site_users - информация логинах пользователей, в основном для авторизации. но тут есть три важных для сортировки поля - Дата регистрации, Последняя активность, и ID person - анкета о пользователе - вес, рост, ореентация ☻ и т.д. Привязанна к пользователю по ID Что нужно реализовать - 1) возможность фильтров по всем полям. 2) сортировка - по последнему посещени, регистрации, возрасту. 3) Пагинация Site_users Код (Text): CREATE TABLE `site_users` ( `blocked_by_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `id` INT(9) UNSIGNED NOT NULL AUTO_INCREMENT, `password` CHAR(255) NOT NULL COLLATE 'utf8_bin', `user_name` VARCHAR(100) NOT NULL, `user_email` VARCHAR(255) NOT NULL, `isactivated` TINYINT(1) NOT NULL DEFAULT '1', `ban_reason` VARCHAR(100) NULL DEFAULT NULL, `ibanned` TINYINT(1) NOT NULL DEFAULT '0', `user_contacts_skype` VARCHAR(100) NOT NULL, `user_adress` VARCHAR(255) NOT NULL, `user_adress_city` VARCHAR(100) NOT NULL, `user_adress_region` VARCHAR(100) NOT NULL, `user_adress_country` VARCHAR(100) NOT NULL, `user_adress_state` VARCHAR(100) NOT NULL, `user_registration_ip` VARCHAR(50) NOT NULL, `user_registration_ip_if_proxy` VARCHAR(50) NOT NULL, `user_carma` INT(11) NOT NULL, `user_activation_request` VARCHAR(100) NOT NULL, `user_change_password_request` VARCHAR(100) NOT NULL, `user_contacts_phone` VARCHAR(100) NOT NULL, `user_contacts_phone2` VARCHAR(100) NOT NULL, `user_contacts_phone3` VARCHAR(100) NOT NULL, `user_contacts_icq` VARCHAR(100) NOT NULL, `user_contacts_jabber` VARCHAR(100) NOT NULL, `user_contacts_facebook` VARCHAR(100) NOT NULL, `user_contacts_instagram` VARCHAR(100) NOT NULL, `user_contacts_twitter` VARCHAR(100) NOT NULL, `user_contacts_addit_info` TEXT NOT NULL, `user_contacts_www` TEXT NOT NULL, `user_bio` TEXT NOT NULL, `user_img` VARCHAR(255) NOT NULL, `user_ip_creation` VARCHAR(255) NOT NULL, `user_ip_last_active` VARCHAR(50) NOT NULL, `user_last_active_date` TIMESTAMP NOT NULL, `user_registration_date` TIMESTAMP NOT NULL, `group_id` INT(9) UNSIGNED NOT NULL, `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `tries_with_wrong_password` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `last_time_wrong_pass_unix_tsmp` INT(10) UNSIGNED NOT NULL DEFAULT '0', `blocked_up_to_date` INT(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE INDEX `user_name` (`user_name`), UNIQUE INDEX `user_email` (`user_email`), INDEX `FK_site_users_user_groups` (`group_id`), CONSTRAINT `FK_site_users_user_groups` FOREIGN KEY (`group_id`) REFERENCES `user_groups` (`id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=9144 ; person Она может потом немного поменяться дальше в процессе разработки - но суть все равно такая же. Код (Text): CREATE TABLE `person` ( `id` INT(9) UNSIGNED NOT NULL, `height` TINYINT(3) UNSIGNED NOT NULL COMMENT 'Height of a person', `weight` TINYINT(3) UNSIGNED NOT NULL COMMENT 'weight of a person', `children` TINYINT(3) UNSIGNED NOT NULL COMMENT 'amount of children the person currently have', `sex` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sex of a person', `sexual_orientation` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `relationship` TINYINT(3) UNSIGNED NOT NULL COMMENT 'the relationship a person currently have', `education` TINYINT(3) UNSIGNED NOT NULL COMMENT 'education level of a person', `employment` TINYINT(3) UNSIGNED NOT NULL COMMENT 'employment status of a person', `smoke` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `alcohol` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `sport` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `health` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `virus_hiv` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', `virus_hepatitis_c` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', PRIMARY KEY (`id`), CONSTRAINT `FK_person_user_id` FOREIGN KEY (`id`) REFERENCES `site_users` (`id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB ; На текущий момент использую такой вот запрос для доставания данных Код (Text): select * from (SELECT person.id FROM person left join site_users on person.id=site_users.id where sex =1 and sexual_orientation =1 and relationship =1 and employment = 1 and smoke = 1 and alcohol =1 and sport = 1 and health = 1 and virus_hiv =1 and virus_hepatitis_c = 1 and (height BETWEEN 110 and 180) and (weight BETWEEN 50 and 250) and education > 1 order by site_users.user_registration_date Limit 50 offset 0) as t join person on t.id=person.id join site_users on t.id = site_users.id; Вот в чем вопрос - как правильно сделать индексы? Просто тут особенность в том, что насколько я понимаю MYSQL очень плохо работает с индексами которые в RANGE (как например - between) Что я понял - что в начале нужно сделать отбор по полям по которым нам нужно только равно. Особенно важен SEX - так как это сразу половину полей отсекает. Но как правильно сделать ORDER BY и пагинацию? Есть ли смысл их вынести вообще в отдельную таблицу Например отдельная таблица - для user_register_date - user_last_activity? Как себя ведет индекс после join? (он вообще работает в таких случаях?) Данные по результатам EXPLAIN скину чуть позже как доберусь на компе на котором это все тестирую, а то у меня там добавляется 300.000 записей достаточно долго.
Код (Text): `virus_hiv` TINYINT(3) UNSIGNED NOT NULL COMMENT 'sexual orientation of a person', Проиграл. Я не читал что вам там нужно и в чем дилемма, но не могу не заметить какие толстенькие и неуклюжие у вас таблички.
Ну да очепятка☻ В процессе разработке ☻ Ну а как их меньше сделать?) Данных то меньше ведь никак не сделаешь если они нужны)
И нужно ли ставить в конце композитного индекса ID? И как заставить MYSQL использовать индекс выражениях BETWEE или var >1 and var <10 и уж темболее в когда граница не определена - как например в var >10 --- Добавлено --- И как одним запросом получить число найденных строк (и при этом используя limit)?
Спойлер: Картинка - список полей для индекса Поигравшись немного с индексами я понял одно - что если их делать подряд как в запросе - то получается шикарная картинка Код (Text): explain select SQL_NO_CACHE * from (SELECT person.id FROM person where sex =1 and sexual_orientation =1 and relationship =1 and employment =1 and smoke =1 and alcohol =1 and sport =1 order by person.user_registration_date Limit 50 offset 0) as t join person on t.id=person.id join site_users on t.id = site_users.id; Const- const - шикарное зрелище☻ радует глаз Также понял что в 10000 раз быстрее если добавить в таблицу PESON - дату создания пользователя и дату последней активности, а не джоинить другую таблицу Правда я так и не понял - работает ли индекс для сортировки? Особенно для DESC ??? Однако все ломается если добавить хотябы один знак - < > BETWEEN Код (Text): explain select SQL_NO_CACHE * from (SELECT person.id FROM person where sex =1 and sexual_orientation =1 and relationship =1 and employment =1 and smoke =1 and alcohol =1 and sport >1 and weight > 150 and (height BETWEEN 110 and 180) order by person.user_registration_date Limit 50 offset 0) as t join person on t.id=person.id join site_users on t.id = site_users.id; и мы сразу получаем значительное ухудшение использования индекса! Как можно заставить MYSQL использовать индекс для BETWEEN? или только делать поиск по =?
Я не специалист в базах, но читаю разные умные книжки в целях разобраться, таблицы у тебя жуть просто! Почему бы не разнести все по отдельным таблицам, к примеру у тебя в таблице есть контакты (телефоны) : PHP: `user_contacts_phone` VARCHAR(100) NOT NULL, `user_contacts_phone2` VARCHAR(100) NOT NULL, `user_contacts_phone3` VARCHAR(100) NOT NULL, Почему бы все это логически связанное не разнести по разным таблицам? Тебе даже не придется писать _phone, _phone2,_phone3, можно хоть 100500 телефонов заносить, привязка телефона будет к ключу пользователя. Также можно сделать таблицу социальных контактов и со всем остальным так, мне кажется то что ты описал в таблицах, это рассматривается в любой более менее адекватной книге по реляционным БД и называется нормализация.
то что он не будет сканировать всю таблицу если у тебя сортировка по индексу и запрос с диапазоном его значений
Сортировка у меня по - user_registration_date --- Добавлено --- А само поле по которому идет сортировка должно быть в индексе? --- Добавлено --- Логически так и нужно как ты говоришь, но по идеи на практике это привет к большим лагам если придется выводить много данных сразу (так как придется джоинить кучу таблиц).
А почему это привет к лагам? Разве к каждому запросу который будет делать юзер, не будет привязана определенная функция, в которой заложен запрос на выборку данных? А что ты будешь делать, когда таблицу нужно будет дополнить другими данными? Конечно нужно чтоб на это ответили бывалые специалисты, но думаю, что целью всегда является нормализация, причем там их несколько степеней. Я предполагаю, что БД нужно делать настолько удобно, чтоб в дальнейшем при добавлении чего либо это обходилось удобством, а не перекопкой всего ранее написанного кода.