LINUX.ORG.RU

MySQL vs. PostgreSQL holy war


2

1

Часто возникают споры какая из DBMS лучше. Однако обе они работают достаточно быстро с малыми объёмами данных. Тестирование с большими объёмами данных затруднительно т.к. приходится искусственно создавать данные.

Предлагаю сторонникам одной из этим DBMS посоветовать оптимизации которые только можно применить к этой таблице. Таким образом вы мне очень поможете и будет видно какая DBMS всё таки лучше для больших объёмов данных.

Выслушаю также предложения по другим DBMS в том числе NoSQL.

Имеется таблица

в MySQL:

CREATE TABLE `videos` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `site` varchar(255) NOT NULL,
  `site_id` bigint(20) unsigned NOT NULL,
  `site_url` varchar(255) NOT NULL,
  `title` text NOT NULL,
  `thumbnails` text NOT NULL,
  `duration_txt` varchar(32) NOT NULL,
  `duration` bigint(20) unsigned NOT NULL,
  `embed` text NOT NULL,
  `created_on` datetime NOT NULL,
  `status` enum('active','deleted') NOT NULL DEFAULT 'active',
  `views` bigint(20) unsigned NOT NULL DEFAULT '0',
  `rating` bigint(20) unsigned NOT NULL DEFAULT '0',
  `voted` bigint(20) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `site` (`site`),
  KEY `site_id` (`site_id`),
  KEY `site_url` (`site_url`),
  KEY `status` (`status`),
  KEY `created_on_id` (`created_on`,`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1797645 DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED

в PostgreSQL:

                                      Table "public.videos"
    Column    |            Type             |                      Modifiers                      
--------------+-----------------------------+-----------------------------------------------------
 id           | bigint                      | not null default nextval('videos_id_seq'::regclass)
 site         | character varying           | not null
 site_id      | bigint                      | not null
 site_url     | character varying           | not null
 title        | character varying           | not null
 thumbnails   | character varying           | not null
 duration_txt | character varying           | not null
 duration     | bigint                      | not null
 embed        | character varying           | not null
 created_on   | timestamp without time zone | not null
 views        | bigint                      | not null
 rating       | bigint                      | not null
 voted        | bigint                      | not null
 status       | status                      | not null default 'active'::status
Indexes:
    "videos_pkey" PRIMARY KEY, btree (id)
    "videos_created_on" btree (created_on)
    "videos_created_on_id" btree (created_on DESC, id DESC)

Задача как можно лучше оптимизировать запрос «SELECT * FROM videos WHERE status = 'active' ORDER BY created_on DESC, id DESC OFFSET 1050050 LIMIT 30». (OFFSET должен быть большим, таблица содержит 1,782,614 записей. Если это сильно увеличит производительность WHERE status = 'active' можно выкинуть и сделать хотя бы без него.


Ответ на: комментарий от pi11

Ну так я не про случай с LIKE, а случай когда order by по полю с индексом.

даже с ORDER BY ИМХО невозможно найти десятый эл-т без выборки всех десяти. Т.е. получить их в отсортированном порядке можно. Можно выбрать с какого-то конкретного (например с id==1234567), но вот ДЕСЯТЫЙ — нельзя.

drBatty ★★
()
Ответ на: комментарий от drBatty

даже с ORDER BY ИМХО невозможно найти десятый эл-т без выборки всех десяти. Т.е. получить их в отсортированном порядке можно. Можно выбрать с какого-то конкретного (например с id==1234567), но вот ДЕСЯТЫЙ — нельзя.

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

tyler19
() автор топика
Ответ на: комментарий от tyler19

Почему же если они заранее отсортированы? Например, если на в файле сохранён список индексов один за другим в нужной последовательности.

B-tree индекс это не массив, а дерево.

Eshkin_kot ★★
()
Ответ на: комментарий от Eshkin_kot

Я не знаю подробностей, но в конце концов можно написать простейшую программу которая будет хранить массив, а потом уже выбирать по ID из базы данных раз уж ни один из DB серверов не может это реализовать самостоятельно.

tyler19
() автор топика
Ответ на: комментарий от namezys

ну по запросу я вижу, что и 50 хватит на один запрос. Но больше лучше

А разве при нескольких параллельных запросах придётся загружать несколько копий этих индексов?

tyler19
() автор топика
Ответ на: комментарий от Eshkin_kot

А что обойти дерево в порядке следования сложно?

namezys ★★★★
()
Ответ на: комментарий от tyler19

во первых, постгря не берет на себя задачи ос и очень мало что кэширует из файловых структур.

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

namezys ★★★★
()
Ответ на: комментарий от namezys

пользуйтесь explain analyze. иногда еще надо опции добавить, чтоб понять, почему

Я пользуюсь, только там не пишут почему.

pi11 ★★★★★
()
Ответ на: комментарий от namezys

Слушай, я знаю что там больше пишут и пользуюсь им вовсю. В том сообщении, которое я привёл это было не нужно.
И почему постгрес на больших оффсетах делает sort по полю с индексом там не пишут, хоть с analyze хоть c verbose.

pi11 ★★★★★
()
Ответ на: комментарий от pi11

зато видно, почему у него sort выполняется так долго. Я не уверен, а проверять сейчас нет особого желания, но если индекс влезет в память, то может он и не будет делать sort

namezys ★★★★
()
Ответ на: комментарий от tyler19

Почему же если они заранее отсортированы?

потому-что «индекс» это НЕ отсортированный список. Да даже если-бы это было списком, то сработал-бы такой LIMIT OFFSET исключительно без WHERE. Безусловно конечно можно по списку найти нужный №. Даже по древовидному индексу, если конечно его слегка подпилить тоже можно. Но вот с WHERE только перебором получится.

drBatty ★★
()
Ответ на: комментарий от tyler19

Я не знаю подробностей, но в конце концов можно написать простейшую программу которая будет хранить массив, а потом уже выбирать по ID из базы данных раз уж ни один из DB серверов не может это реализовать самостоятельно.

можно и уже сделано. Вот только для 1005000050го элемента такой массив попросту не влезет ни в какую память. Причём не важно, какая у тебя СУБД.

drBatty ★★
()
Ответ на: комментарий от namezys

А что обойти дерево в порядке следования сложно?

сложно. В общем случае O(N*N*log(N)). Потому-что поиск одного узла в общем случае O(N*log(N)), а N узлов очевидно в N раз больше.

drBatty ★★
()
Ответ на: комментарий от drBatty

расскажи — как, если IRL у тебя есть WHERE, о котором в твоём индексе ни слова?

Ну если под таким углом смотреть, то даже если есть информация в индексе о том, что у тебя есть в where, не факт, что она поможет.

Я автору предлагаю делать partial index - вроде так это зовется

namezys ★★★★
()
Ответ на: комментарий от drBatty

Чего? Дойти до первого листа - log N, второй узел уже ищем относительно первого.

Предположу что получится O(N), но правда это предположение скорей на интуиции основано.

namezys ★★★★
()
Ответ на: комментарий от namezys

Чего? Дойти до первого листа - log N, второй узел уже ищем относительно первого.

ага. А с чего ты взял, что относительно узла X путь будет короче, чем от корня? В общем случае, путь от X1 до X2 имеет сложность O(log(N)). С N² я конечно перестарался, но вот O(N*log(N)) ты получишь вполне себе реально. А если решишь это всё сохранять, то и памяти тебе понадобится соответственно. Подробный анализ см. у Кнута.

Вообще говоря, имея индекс, ты можешь найти всё, что ты хочешь, но вот посчитать _не найденное_ тебе ни один индекс не поможет. Например, ты можешь найти все записи за посл. 24 часа, но вот найти все записи №№ ≥ 10050050 ты не можешь, не просмотрев этих записей.

В принципе, путём усложнения индекса, ты можешь искать по номеру индекса. Но это обычно не то, что нужно IRL. Вот пример: твои посты на LOR'е, начиная с №2354(номер для тебя, т.е. это твой 2354й пост). Ты их можешь найти сразу, но тогда, и только тогда, когда лично для тебя сделают свой персональный индекс. А в общем индексе такой информации нет. Можно просто пост №2354 найти. Можно найти твои посты, начиная с поста, с общим №2354. Можно найти все твои посты, и взять №2354.

drBatty ★★
()
Ответ на: комментарий от drBatty

это же 2 узла рядом. пошел вверх, потом опять в вниз. Хотя похоже я с утра туплю, надо же еще вернутся назад, в худшем случае log N. Ну не суть, индекс я бы сделал так, чтоб он влез в память - тогда пусть сколько угодно бегает. Все равно не дороже сортировки.

А вот то, что индекс не поможет тебе считать количество, позволь не согласится. Количество записей он так же поможет посчитать.

И да, если у тебя сложно where, то тут тебе индекс не поможет (ну может только совсем что нить сложно, но я с ходу даже структуру этого представить не могу).

а вот если where у тебя дает ВЕСЬ индекс сразу - то тогда можно посчитать по нему.

namezys ★★★★
()
Ответ на: комментарий от namezys

надо же еще вернутся назад, в худшем случае log N

не в худшем, а в среднем. Никто не гарантирует близость соседнего узла. Как раз наоборот.

Ну не суть, индекс я бы сделал так, чтоб он влез в память

тогда получишь нобелевскую премию. Может это ты весь интернет на одну флешку решил запаковать своим суперархиватором?

А вот то, что индекс не поможет тебе считать количество, позволь не согласится. Количество записей он так же поможет посчитать.

пишу-же по-русски более-менее: может. Своих записей. Т.е. можно взять запись №123 В ИНДЕКСЕ. Но нужно-то не в индексе! Не будешь-же ты делать по индексу на каждый запрос?

И да, если у тебя сложно where

да просто WHERE username='namezys', что тут сложного?

а вот если where у тебя дает ВЕСЬ индекс сразу

где ты такие запросы видел? Зачем они могут пригодиться?

drBatty ★★
()
Ответ на: комментарий от drBatty

не в худшем, а в среднем. Никто не гарантирует близость соседнего узла. Как раз наоборот.

Там же не бинарное дерево. ТАк что надо считать, константа оптимизации может быть мала.

Может это ты весь интернет на одну флешку решил запаковать своим суперархиватором?

Не пори чушь. Если у тебя индекс занимает десяток метров, то он влезет в память и там сортировать будет. Если же в память не лезем, то операции будут идти через диск. Мы иногда даже запросы разбиваем так, чтоб они не шли через диск, и получаем разницу на порядок или два.

Т.е. можно взять запись №123 В ИНДЕКСЕ. Но нужно-то не в индексе! Не будешь-же ты делать по индексу на каждый запрос?

ТС как раз вполне обойдется одним индексом и может сделать индекс под такой запрос. Но это не ОБЩЕЕ решение, а частное, но подходит под это.

да просто WHERE username='namezys', что тут сложного?

То, что username очень много. А вот status_id = 1 простое. И тут для каждого status можно сделать индекс.

где ты такие запросы видел? Зачем они могут пригодиться?

Да у ТС. У него where фактически status_id = 1. в индексе будут только записи со status_id = 1. Может ты просто не читал про partial index? Хороший инстурмент для ускорения НЕКОТОРЫХ запросов в очень ЧАСТНЫХ случаях.

namezys ★★★★
()
Ответ на: комментарий от namezys

Там же не бинарное дерево. ТАк что надо считать, константа оптимизации может быть мала.

в небинарном эта константа ещё больше, и намного. Но дело тут не в константе, если у тебя БОЛЬШОЙ offset.

Не пори чушь. Если у тебя индекс занимает десяток метров, то он влезет в память и там сортировать будет. Если же в память не лезем, то операции будут идти через диск. Мы иногда даже запросы разбиваем так, чтоб они не шли через диск, и получаем разницу на порядок или два.

я тут при чём? Это ТС рассматривает бредовый случай. Типа «что будет, если биться головой апстену?».

ТС как раз вполне обойдется одним индексом и может сделать индекс под такой запрос. Но это не ОБЩЕЕ решение, а частное, но подходит под это.

у ТСа два _независимых_ индекса: статус и created_on_id. Ему это не подходит. А если сделает один общий, да ещё на 1,782,614 записей… Думаешь сколько ему памяти на ТАКОЙ индекс надо будет? Вы тоже ТАК делаете?

То, что username очень много. А вот status_id = 1 простое. И тут для каждого status можно сделать индекс.

в принципе - да. Если у ТСа действительно enum на 2 значения, то теоретически для такого запроса можно двумя отдельными индексами обойтись. Но ТС такого сделать даже не попытался. Сомневаюсь, что есть СУБД, которая до такого догадается. (она же не знает, что будут за запросы). К тому-же, накладные расходы на поддержание такого индекса будут ОЧЕНЬ велики, и я не вижу, как этого можно избежать.

Да у ТС. У него where фактически status_id = 1. в индексе будут только записи со status_id = 1. Может ты просто не читал про partial index? Хороший инстурмент для ускорения НЕКОТОРЫХ запросов в очень ЧАСТНЫХ случаях.

читал. Только, как я понял ТС, это у него тест такой. В его RL запросы сложнее.

drBatty ★★
()
Ответ на: комментарий от drBatty

в небинарном эта константа ещё больше, и намного.

с чего это?

А если сделает один общий, да ещё на 1,782,614 записей… Думаешь сколько ему памяти на ТАКОЙ индекс надо будет? Вы тоже ТАК делаете?

делаем. и у нас записей больше на парядок

К тому-же, накладные расходы на поддержание такого индекса будут ОЧЕНЬ велики, и я не вижу, как этого можно избежать.

почему?

К тому-же, накладные расходы на поддержание такого индекса будут ОЧЕНЬ велики, и я не вижу, как этого можно избежать.

если так - то да

namezys ★★★★
()
Ответ на: комментарий от namezys

с чего это?

с того, что Nарное дерево эквивалентно бинарному, только там узлы «совмещённые». Пруф см. у Кнута. IRL получается больше константа, причём обычно сильно больше, чем профит от меньшего количества узлов. Зато с памятью получше.

делаем. и у нас записей больше на парядок

с таким OFFSET'ом?

К тому-же, накладные расходы на поддержание такого индекса будут ОЧЕНЬ велики, и я не вижу, как этого можно избежать.

почему?

ну потому, что если у тебя status на 10 состояний, то тебе надо 10 индексов поддерживать. Ну либо «один», который получится в 10 раз больше(и дольше). Причём в WHERE ты можешь поставить _только_ этот статус, но никак не что-то другое. Например всё сломается, если ты допишешь AND username='xyz'. Ну если конечно ты ещё и не умножишь число индексов на количество username. Размер и время таких индексов растёт как произведение числа альтернатив (количество разных username) на число этих username, status и так далее.

drBatty ★★
()
Ответ на: комментарий от drBatty

с того, что Nарное дерево эквивалентно бинарному, только там узлы «совмещённые». Пруф см. у Кнута. IRL получается больше константа, причём обычно сильно больше, чем профит от меньшего количества узлов. Зато с памятью получше.

Если мы будем его обходить, то тоже профит будет. Да и если с диском работаем больше - тоже.

с таким OFFSET'ом?

без него конечно. Но индексы есть. Причем иногда по гигабайтам

а время обновленеи индексом останется практически такое же. Правда если будет меняться status - то увеличится раза в 2

namezys ★★★★
()
Ответ на: комментарий от namezys

Если мы будем его обходить, то тоже профит будет. Да и если с диском работаем больше - тоже.

в одном узле Nарного дерева храняться очень _разные_ и далёкие узлы. Это нужно для того, что-бы разделить область поиска на _разные_ подмножества. Что-бы быстрее было _искать_. А вот _обходить_ получается намного дольше. Но СУБД и не заточена на обход, её задача — сделать выборку, т.е. _найти_ все записи, по определённому критерию. Всё остальное - это уже вспомогательные задачи.

без него конечно. Но индексы есть. Причем иногда по гигабайтам

я разве против индексов? Просто есть задачи, для которых они предназначены, а есть задачи, для которых — не предназначены. Например такие сумасшедшие оффсеты.

drBatty ★★
()
Ответ на: комментарий от namezys

Правда если будет меняться status - то увеличится раза в 2

если будет меняться статус, то очевидно тебе надо будет удалить из одного индекса, и вставить в другой. Ну а индексы для _поиска_ оптимизированы, а не для вставки/удаления. Потому, тебе придётся полиндекса перестраивать. Про эффект домино слышал? Вот он у тебя будет постоянно выскакивать, если ты такую хрень в продакшен встроишь.

drBatty ★★
()
Ответ на: комментарий от namezys

но я бы на практики попробовал

а я пробовал. Жрёт кучу памяти и тормозит. Может что-то не так конечно делал.

drBatty ★★
()
Ответ на: комментарий от tyler19

Я тут на работе поспрашивал. Походу это самое нормальное решение.

dizza ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.