Запросы к базе

Могли ли эти 2 запроса привести к перегрузке MySQL сервера хостинга? Хостинг mchost
SELECT
c.*,
t.topic_title as topic_title,
t.topic_count_comment as topic_count_comment,
u.user_profile_avatar as user_profile_avatar,
u.user_profile_avatar_type as user_profile_avatar_type,
u.user_login as user_login,
b.blog_title as blog_title,
b.blog_type as blog_type,
b.blog_url as blog_url,
u_owner.user_login as blog_owner_login
FROM
prefix_topic_comment_online as co
JOIN prefix_topic_comment AS c ON co.comment_id=c.comment_id
JOIN prefix_topic AS t ON co.topic_id=t.topic_id
JOIN prefix_user AS u ON c.user_id=u.user_id
JOIN prefix_blog AS b ON t.blog_id=b.blog_id
JOIN prefix_user AS u_owner ON b.user_owner_id=u_owner.user_id
ORDER by co.comment_online_id desc limit N, N


и

SELECT
t_fast.*,
tc.*,
u.user_login as user_login,
IF(tv.topic_id IS NULL,N,N) as user_is_vote,
tv.vote_delta as user_vote_delta,
IF(tqv.topic_id IS NULL,N,N) as user_question_is_vote,
bu.is_moderator as user_is_blog_moderator,
bu.is_administrator as user_is_blog_administrator,
IF(tr.comment_count_last IS NULL,t_fast.topic_count_comment,t_fast.topic_count_comment-tr.comment_count_last) as count_comment_new,
IF(ft.topic_id IS NULL,N,N) as topic_is_favourite
FROM (
SELECT
t.*,
b.blog_title as blog_title,
b.blog_type as blog_type,
b.blog_url as blog_url,
b.user_owner_id as blog_owner_id
FROM
prefix_topic as t,
prefix_blog as b
WHERE
N=N
AND t.topic_publish = N AND ( t.topic_rating >= N or topic_publish_index=N ) AND b.blog_type in ('S','S')
AND
t.blog_id=b.blog_id
ORDER by t.topic_date_add desc
LIMIT N, N
) as t_fast
JOIN prefix_user AS u ON t_fast.user_id=u.user_id
LEFT JOIN (
SELECT
topic_id,
vote_delta
FROM prefix_topic_vote
WHERE user_voter_id = -N
) AS tv ON t_fast.topic_id=tv.topic_id
LEFT JOIN (
SELECT
topic_id
FROM prefix_favourite_topic
WHERE user_id = -N
) AS ft ON t_fast.topic_id=ft.topic_id
LEFT JOIN (
SELECT
topic_id,
comment_count_last
FROM prefix_topic_read
WHERE user_id = -N
) AS tr ON t_fast.topic_id=tr.topic_id
LEFT JOIN (
SELECT
topic_id
FROM prefix_topic_question_vote
WHERE user_voter_id = -N
) AS tqv ON t_fast.topic_id=tqv.topic_id
LEFT JOIN (
SELECT
is_moderator,
is_administrator,
blog_id
FROM prefix_blog_user
WHERE user_id = -N
) AS bu ON t_fast.blog_id=bu.blog_id
JOIN prefix_topic_content AS tc ON t_fast.topic_id=tc.topic_id
order by t_fast.topic_date_add desc

58 комментариев

avatar
Причем раньше проблем не было… Посещаемость порядка 2 тысяч человек.
avatar
Ну что за манера, написать топик и тут же в него самому ответить? Я глянул, увидел, что комментарий есть, и полез внутрь, т.к. понадеялся, вдруг кто-то уже что-то сказал по делу. И что вижу? Два предложения, которые автор почему-то не захотел в топике написать.
avatar
Я написал тему и после этого вспомнил, что не дописал в топике. Мне удобнее написать в комментарии, чем редактировать топик. Если Вам что-то не нравиться, пройдите мимо.
avatar
Это чтоб тебя быстрее заметили, так многие делают… почти все :)
avatar
а почему вы грешите именно на эти два запроса?
avatar
Хостер прислал уведомление, после блокировки базы о том, что именно эти 2 запроса перегрузили их сервер.
avatar
тогда вопрос скорее — что делать?
— подключить мемкешь или увеличить память для мемкеша
— другой тарифный план,
— покопать — переделать запросы
avatar
Я к сожалению, не настолько хорошо разбираюсь в sql-запросах, чтобы еще больше оптимизировать данные запросы.

Вопрос именно в такой форме, потому-что у меня есть сомнения в правдивости перегрузки сервера. Возможно просто хотят впарить тариф по-дороже.
avatar
ну можно выполнить запрос в каком-нибудь phpmyadmin-е и посмотреть время выполнения запроса

да… хостеры, сцуки, такие
avatar
Хостеры не «сцуки», они берегут других клиентов на сервере от «таких» запросов.

Я отмечу, что узкое место в любой веб архитектуре — это субд.
Нельзя сделать «классную» архитектуру с ужасными запросами.
субд — горлышко для архитектур ;)
avatar
если включить логирование скл запросов в конфиге, то там будет прописано время их(запросов) выполнения
  • ort
  • 0
avatar
Да, по дефолту в конфиге было включено только логирование ошибок…
avatar
Макс, а зачем перед каждым sql запросом дергаются эти запросы?

<code>[2009-03-20 15:38:04][19100][DEBUG][set character_set_client='utf8']
[2009-03-20 15:38:04][19100][DEBUG][  -- 0 ms; returned '0']
[2009-03-20 15:38:04][19100][DEBUG][set character_set_results='utf8']
[2009-03-20 15:38:04][19100][DEBUG][  -- 0 ms; returned '0']
[2009-03-20 15:38:04][19100][DEBUG][set collation_connection='utf8_bin']
[2009-03-20 15:38:04][19100][DEBUG][  -- 0 ms; returned '0']</code>


это же тоже в сумме немного грузит машину или нет?
avatar
по идеи они дёргаются только один раз при инициализации БД
avatar
Совершенно верно ваш хостер известил вас, про эти запросы.
Такие запросы обязательно надо кешировать и только.

Посмотрите сами. Каждый join или каждый подзапрос — это фактически запрос.
Плюс ко всему order by и limit (1)
При «приличном» кол-ве записей в таблицах — эти запросы при нагрузке поставят сервер в ступор.

Выход только один — переписать архитектуру, что невозможно.
Установить на свой сервер сайт и правильно настроить кеширование. Я думаю memcached вас спасет.

А запросы и правда ужасные, особенно left join (select…
Это честно говоря ужас на нагруженных проектах. Советую автору архитектуры пересмотреть и оптимизировать запросы.
avatar
А кстати? Кто-то уже занимался бенчмаркингом livestreetа для нагруженных проектов? Просто интересно есть ли вообще достаточно нагруженные проекты на livestreet. Очень заинтересован пообщаться.
avatar
Не то чтобы высоконагруженный, было в районе 10 тысяч в день, сейчас 3-4, скоро будет в районе 20 я думаю… вот теперь думаю, как бы это все дело оптимизировать…
avatar
М… а 10 тысяч чего?

В любом случае мы будем этим заниматься и если что переписывать запросы. Там кстати в маппере для топиков уже есть первые попытки оптимизации. Но детальнее можно сказать только после профилировки. Правда я не силен в этом вопросе. Этим будет заниматься отдельный человек. Думаю, он здесь скоро объявится.
avatar
10 тысяч уникальных посетителей в сутки.
avatar
А поподробнее можно. Какой сервер? Сколько памяти? Какие диски? Какой webserver, чем реализованно FastCGI, есть ли акселератор? База на одном сервере или отдельно? Какая нагрузка на проц?

Понимаю, много вопросов. Можно личным письмом если не хотите публично рассказывать.
avatar
Т.е. приблизительно 10 хитов в сек.
А сколько записей в базе?
avatar
Слушайте, а в широкие массы эти изменения в оптимизации пойдут. Хоть я в этом вообще нуль, но как человеку собирающемуся использовать лайвстрит, очень интересно что да как.
avatar
Если будет интересно другим и стыкуемо — то почему-бы и нет. Всеми наработками поделюсь бесплатно.
avatar
Пока будет join (select…
На сильно нагруженные проекты можно забить.
Не думаю что с таким запросом можно нормально работать — это просто ужас.
Это япривел только один такой запрос, там если пройтись — то будет ужас еще больше.

<code>SELECT 
					t_fast.*, 
					tc.*,
					u.user_login as user_login,
					IF(tv.topic_id IS NULL,0,1) as user_is_vote,
					tv.vote_delta as user_vote_delta,
					IF(tqv.topic_id IS NULL,0,1) as user_question_is_vote,
					bu.is_moderator as user_is_blog_moderator,
					bu.is_administrator as user_is_blog_administrator,
					IF(tr.comment_count_last IS NULL,t_fast.topic_count_comment,t_fast.topic_count_comment-tr.comment_count_last) as count_comment_new 
				FROM (
					SELECT 
						t.*,	
						b.blog_title as blog_title,
						b.blog_type as blog_type,
						b.blog_url as blog_url,
						b.user_owner_id as blog_owner_id									
					FROM 
						prefix_topic as t,	
						prefix_blog as b				
					WHERE 
						1=1
					
						 AND t.topic_publish =  1 AND ( t.topic_rating >= 8  or topic_publish_index=1  )  AND b.blog_type in ('personal','open') 								
					
						AND
						t.blog_id=b.blog_id											
					ORDER by t.topic_date_add desc
					LIMIT 0, 10
				) as t_fast
				JOIN prefix_user AS u ON t_fast.user_id=u.user_id
				LEFT JOIN (
						SELECT
							topic_id,
							vote_delta												
						FROM prefix_topic_vote 
						WHERE user_voter_id = -1
					) AS tv ON t_fast.topic_id=tv.topic_id 
				LEFT JOIN (
						SELECT
							topic_id,
							comment_count_last												
						FROM prefix_topic_read 
						WHERE user_id = -1
					) AS tr ON t_fast.topic_id=tr.topic_id
				LEFT JOIN (
						SELECT
							topic_id																			
						FROM prefix_topic_question_vote 
						WHERE user_voter_id = -1
					) AS tqv ON t_fast.topic_id=tqv.topic_id
				LEFT JOIN (
						SELECT
							is_moderator,
							is_administrator,
							blog_id												
						FROM prefix_blog_user 
						WHERE user_id = -1
					) AS bu ON t_fast.blog_id=bu.blog_id
				JOIN  prefix_topic_content AS tc ON t_fast.topic_id=tc.topic_id</code>


А вот его EXPLAIN — это пиздец (другого слова я не подобрал)

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const table…
6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
5 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
4 DERIVED prefix_topic_read ref user_id user_id 4 1 Using where
3 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 DERIVED NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const table…
avatar
Есть предложение как можно такие запросы оптимизировать?

З.Ы. на смену архитектуры не посылать (конкретное предложение ожидается).
avatar
Вы много хотите :)
Это хороший кусок работы. Рефакторинг запросов, подразумевает под собой косметические изменения архитектуры.

Работы там хватит с головой.
avatar
А вы толковый специалист в этом деле или просто теоретик?
Если вы профи, тогда возможно вы бы могли помочь проекту за вознаграждение? (Если конечно, Ort не будет против)
avatar
Спасибо у меня свой проект есть :) 95% готовности.
avatar
95% не считается :) как известно последние 5-10% можно доделывать половину времени от всего проекта:)
avatar
что именно не нравится в запросе?
этот запрос идёт на не авторизованного юзера, поэтому везде стоит
<code>user_id = -1</code>
согласен, что их можно вообще убрать из запроса, но такие подзапросы выполняются очень быстро, т.к. идут по ключу. В join'ах я вижу только один минус — не возможность кешировать их отдельно от главного запроса. В остальном, что отдельный запрос, что равноценный join. А вообще вот тут писал, что планирую сделать с запросами www.livestreet.ru/blog/985.html#comment10371
avatar
Так что вопрос со скоростью — закрывается.
Нужно менять архитектуру, причем полностью, а автору совет, более детально изучить оптимизации MySQL, а не делать упор на архитектуру php ;)

Узкое место (горлышко) в нагруженных проектах — не php код, а СУБД. Пора это знать.
avatar
Ссылки в студию, где этому учат =)
avatar
В блогах разработчиков MySQL, под рукой к сожалению нет.
Они очень хорошо описывают работу оптимизаторов и т.п. Если проблем с анг. языком нет — через неделю вы будите почти асом в оптимизациях, а после реальных тестов, где-то через пару месяцев — гуру ;)
avatar
Проблем нет, с англ. языком, жду хороших ссылок, как найдете.
avatar
Вот вам ссылочка
avatar
Невидимая какая то ссылочка.
avatar
Парсер слопал зараза…

ссылка такая: www.mysqlperformanceblog.com

ниже уже отписали
avatar
в каком виде ты постил ссылку?
avatar
Точно не помню вроде в таком:
<code><a href="http://www.mysqlperformanceblog.com">
http://www.mysqlperformanceblog.com</a>

Кстати можножно предусмотреть если вставляют ссылку вот так:
<a href="http://www.mysqlperformanceblog.com"></a>
то тоже парсим ее и url в кач-ве имени ссылки идет

т.е так же обработать можно как и просто
 http://www.mysqlperformanceblog.com</code>
avatar
вот одна
http://www.mysqlperformanceblog.com/

отличная книга по оптимизации для Mysql

High Performance MySQL, Second Edition

в формате pdf просто находится через google
очень советую к прочтению.
avatar
Не пойму зачем полностью, вроде основа сделана нормально, все join (select… используются для получения: голосований, ролей, и количества прочтенных комментариев для конкретного пользователя. Наверняка есть варианты, получить это по другому.
avatar
Не обсуждаемо — не годятся и все, поверьте пока на слово :)
avatar
я в курсе, что самое узкое место БД, в этом направлении шла работа и будет вестись
avatar
Не видно пока.
Возьмите для начала все свои запросы explain-ом проверьте ;)
Я уже вверху писал, как можно сделать запрос не использующий совсем индексы?
Вы мне ответите? Это работа? Это знание узкого места? Я наверно резковато, но я знаю о чем говорю. Разработчику главнее конструктивная критика а не дифирамбы…
avatar
если внимательно посмотрите исходники, то там можно найти 2 версии запросов — одна старая и очень медленная, вторая текущая, которая гораздо шустрее.
Вторую часть вашего коммента вообще не понял…
От вас пока услышал, что БД это самое узкое место. Но это и так все знают. Возможно у вас есть конкретное предложение?
avatar
Про индексы?
Я привел explain запроса. Запрос не брал совсем индексов, это правильно? ;)
avatar
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const table…
6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
5 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
4 DERIVED prefix_topic_read ref user_id user_id 4 1 Using where
3 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 DERIVED NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const table…
avatar
ну, я выше уже писал — подзапросы в джойнах возвращают пустой результат, т.к. пользователь не авторизован. Это подзапрос выполняется очень быстро
avatar
На сколько я вижу, у вас подзапросы и при авторизированном пользователе сохраняются

<code>JOIN prefix_user AS u ON t_fast.user_id=u.user_id
				LEFT JOIN (
						SELECT
							topic_id,
							vote_delta</code>
avatar
а кто говорил что они исчезают? =)
при авторизованном они как раз и нужны, а при госте от них можно и избавиться, но я этого не сделал, т.к. при user_id=-1 они выполняются мгновенно
avatar
Смотрим авторизированным, и особенно explain

<code>SELECT t_fast . * , tc . * , u.user_login AS user_login,
IF (
tv.topic_id IS NULL , 0, 1
) AS user_is_vote, tv.vote_delta AS user_vote_delta,
IF (
tqv.topic_id IS NULL , 0, 1
) AS user_question_is_vote, bu.is_moderator AS user_is_blog_moderator, bu.is_administrator AS user_is_blog_administrator,
IF (
tr.comment_count_last IS NULL , t_fast.topic_count_comment, t_fast.topic_count_comment - tr.comment_count_last
) AS count_comment_new
FROM (

SELECT t . * , b.blog_title AS blog_title, b.blog_type AS blog_type, b.blog_url AS blog_url, b.user_owner_id AS blog_owner_id
FROM prefix_topic AS t, prefix_blog AS b
WHERE 1 =1
AND t.topic_publish =1
AND (
t.topic_rating >=8
OR topic_publish_index =1
)
AND b.blog_type
IN (
'personal', 'open'
)
AND t.blog_id = b.blog_id
ORDER BY t.topic_date_add DESC
LIMIT 0 , 10
) AS t_fast
JOIN prefix_user AS u ON t_fast.user_id = u.user_id
LEFT JOIN (

SELECT topic_id, vote_delta
FROM prefix_topic_vote
WHERE user_voter_id =1
) AS tv ON t_fast.topic_id = tv.topic_id
LEFT JOIN (

SELECT topic_id, comment_count_last
FROM prefix_topic_read
WHERE user_id =1
) AS tr ON t_fast.topic_id = tr.topic_id
LEFT JOIN (

SELECT topic_id
FROM prefix_topic_question_vote
WHERE user_voter_id =1
) AS tqv ON t_fast.topic_id = tqv.topic_id
LEFT JOIN (

SELECT is_moderator, is_administrator, blog_id
FROM prefix_blog_user
WHERE user_id =1
) AS bu ON t_fast.blog_id = bu.blog_id
JOIN prefix_topic_content AS tc ON t_fast.topic_id = tc.topic_id</code>


а вот explain этого запроса!

<code>id  	 select_type  	 table  	 type  	 possible_keys  	 key  	 key_len  	 ref  	 rows  	 Extra
1 	PRIMARY 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	Impossible WHERE noticed after reading const table...
6 	DERIVED 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	no matching row in const table
5 	DERIVED 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	no matching row in const table
4 	DERIVED 	prefix_topic_read 	ref 	user_id 	user_id 	4 	  	1 	 
3 	DERIVED 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	no matching row in const table
2 	DERIVED 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	NULL 	Impossible WHERE noticed after reading const table...</code>


Вы мне ответьте, а «где» индексы !?
avatar
Вы посмотрите на этот запрос. Сколько подзапросов и join-ов!
Причем не использующих индексов. Надеимся на кеш ;)?
C такими запросами можно только альфу выпускать
avatar
вот мой эксплейн этого запроса:
<code>1  	PRIMARY  	<derived2>  	ALL  	NULL  	NULL  	NULL  	NULL  	10  	 
1 	PRIMARY 	<derived3> 	ALL 	NULL 	NULL 	NULL 	NULL 	134 	 
1 	PRIMARY 	<derived4> 	ALL 	NULL 	NULL 	NULL 	NULL 	542 	 
1 	PRIMARY 	<derived5> 	ALL 	NULL 	NULL 	NULL 	NULL 	13 	 
1 	PRIMARY 	<derived6> 	ALL 	NULL 	NULL 	NULL 	NULL 	9 	 
1 	PRIMARY 	tc 	eq_ref 	PRIMARY 	PRIMARY 	4 	t_fast.topic_id 	1 	 
1 	PRIMARY 	u 	eq_ref 	PRIMARY 	PRIMARY 	4 	t_fast.user_id 	1 	 
6 	DERIVED 	prefix_blog_user 	ref 	user_id 	user_id 	4 	  	7 	 
5 	DERIVED 	prefix_topic_question_vote 	ref 	user_voter_id 	user_voter_id 	4 	  	13 	Using index
4 	DERIVED 	prefix_topic_read 	ref 	user_id 	user_id 	4 	  	542 	 
3 	DERIVED 	prefix_topic_vote 	ref 	user_voter_id 	user_voter_id 	4 	  	133 	 
2 	DERIVED 	t 	ALL 	blog_id,topic_rating,topic_publish 	NULL 	NULL 	NULL 	893 	Using filesort
2 	DERIVED 	b 	eq_ref 	PRIMARY,blog_type 	PRIMARY 	4 	social.t.blog_id 	1 	Using where</code>
индексы есть. Меня только смущает полей key = null при possible_key = blog_id,topic_rating,topic_publish
avatar
Это значит что перебирается вся таблица.

Оптимизатор пытался выискать оптимальный индекс, но не один не попал под условия выборки, значит запрос составлен неверно или индекс. :\
Да и посмотрите на derived там тоже null :\
Наследованная таблица select — null индекс :\

avatar
Короче, везде, где NULL и ALL или filesort или temp это плохо :)
в двух словах
avatar
Далее, искоренить все OR и IN, особенно когда много OR и в IN «много», из этого и «теряется»? часто, оптимизатор
avatar
Далее, в подзапросе order by и limit? Зачем?
Это один тормоз + шанс потеряться оптимизатору.

Вы все делаете для того чтобы оптимизатор послал вас очень далеко и перебирал всю таблицу

avatar
на счет основного подзапроса
<code>SELECT t . * , b.blog_title AS blog_title, b.blog_type AS blog_type, b.blog_url AS blog_url, b.user_owner_id AS blog_owner_id
FROM prefix_topic AS t, prefix_blog AS b
WHERE 1 =1
AND t.topic_publish =1
AND (
t.topic_rating >=8
OR topic_publish_index =1
)
AND b.blog_type
IN (
'personal', 'open'
)
AND t.blog_id = b.blog_id
ORDER BY t.topic_date_add DESC
LIMIT 0 , 10</code>

его можно попробовать разбить на два через UNION, для того чтоб задействовать ключи topic_rating и topic_publish_index по отдельности, и избавиться от OR
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.