MySQL - как оптимизировать запрос для подсчета голосов - PullRequest
6 голосов
/ 04 сентября 2010

Просто после некоторых мнений о наилучшем способе достижения следующего результата:

Я хотел бы сохранить в своей базе данных MySQL продукты, за которые могут голосовать пользователи (каждый голос стоит +1).Я также хочу видеть, сколько раз в общей сложности проголосовал пользователь.

На мой взгляд, следующая структура таблицы была бы идеальной:

  table: product          table: user            table: user_product_vote       
+----+-------------+    +----+-------------+    +----+------------+---------+ 
| id |   product   |    | id | username    |    | id | product_id | user_id |
+----+-------------+    +----+-------------+    +----+------------+---------+
| 1  | bananas     |    | 1  | matthew     |    | 1  | 1          | 2       |
| 2  | apples      |    | 2  | mark        |    | 2  | 2          | 2       |
| .. | ..          |    | .. | ..          |    | .. | ..         | ..      |

Таким образом, я могусделать COUNT таблицы user_product_vote для каждого продукта или пользователя.

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

SELECT p.product AS product, COUNT( v.id ) as votes
FROM product p
LEFT JOIN user_product_vote v ON p.id = v.product_id
WHERE p.id =1

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

  table: product               
+----+-------------+-------+
| id |   product   | votes |
+----+-------------+-------+
| 1  | bananas     | 2     |
| 2  | apples      | 5     |
| .. | ..          | ..    |

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

Мои вопросы:
i) Я слишком обеспокоен ресурсами сервера и должен просто придерживаться варианта трех таблиц?(т.е. нужно ли мне больше верить в способность базы данных обрабатывать большие запросы)
ii) это более эффективный способ достижения результата без потери информации

Ответы [ 5 ]

6 голосов
/ 04 сентября 2010

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

Что касается системы голосования, лично я бы оставил голоса вот так:

table: product          table: user             table: user_product_vote       
+----+-------------+    +----+-------------+    +----+------------+---------+ 
| id |   product   |    | id | username    |    | id | product_id | user_id |
+----+-------------+    +----+-------------+    +----+------------+---------+
| 1  | bananas     |    | 1  | matthew     |    | 1  | 1          | 2       |
| 2  | apples      |    | 2  | mark        |    | 2  | 2          | 2       |
| .. | ..          |    | .. | ..          |    | .. | ..         | ..      |

Причины:

Во-первых, user_product_vote не содержит текста, каплии т. д., оно чисто целочисленное, поэтому в любом случае оно потребляет меньше ресурсов.

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

Возьмем, к примеру, этот пример:

table: user_product_vote       
+----+------------+---------+-----------+------+ 
| id | product_id | user_id | vote_type | time |
+----+------------+---------+-----------+------+
| 1  | 1          | 2       | product   |224.. |
| 2  | 2          | 2       | page      |218.. |
| .. | ..         | ..      | ..        | ..   |

И простой запрос:

SELECT COUNT(id) as total FROM user_product_vote WHERE vote_type = 'product' AND time BETWEEN(....) ORDER BY time DESC LIMIT 20

Другое дело, если пользователь проголосовал за 1AM, а затемЕсли вы попытаетесь проголосовать снова на 2PM, вы можете легко проверить, когда они голосовали в последний раз, и нужно ли им разрешить голосовать снова.

Существует так много возможностей, которыевас не хватит, если вы будете придерживаться своего возрастающего примера.


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

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

2 голосов
/ 04 сентября 2010

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

Не тратьте свое время на решение мнимых проблем. mysql прекрасно способен обрабатывать тысячи записей за доли секунды - это то, для чего нужны базы данных. Чистая и простая база данных и структура кода гораздо важнее мифической «оптимизации», которая никому не нужна.

1 голос
/ 04 сентября 2010

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

Edit: Чтобы объяснить это немного дальше, таблица продуктов и пользователей будет иметь столбец с названием «голоса». Каждый раз, когда вставка завершается успешно в user_product_vote, увеличивайте соответствующие записи пользователя и продукта. Это позволит избежать двойного голосования, и вам также не придется каждый раз запускать сложный запрос.

Edit: Также я предполагаю, что вы создали уникальный индекс для product_id и user_id, в этом случае любая попытка дублирования автоматически завершится неудачей, и вам не нужно проверять таблицу перед вставкой. Вы просто убедитесь, что запрос на вставку был выполнен, и вы получили правильное значение для «id» в форме на insert_id

0 голосов
/ 04 сентября 2010

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

Предполагая, что не все запросы продукта должны включать столбец голосов, вы можете сохранить отдельную таблицу productvotes, в которой будут храниться промежуточные итоги, и сохранить вашу таблицу userproductvote в качестве средства для принудительного голосования пользователей по бизнес-правилам продукта / и аудита .

0 голосов
/ 04 сентября 2010

Вы должны сбалансировать желание вашего сайта работать быстро (в котором вторая схема будет лучшей) и возможность подсчитывать голоса для конкретных пользователей и предотвращать двойное голосование (для которого я выбрал бы первую схему). Поскольку вы используете только целочисленные столбцы для таблицы user_product_vote, я не вижу, как производительность может сильно пострадать. Отношения «многие ко многим» являются общими, как вы реализовали с помощью user_product_vote. Если вы действительно хотите подсчитать голоса для конкретных пользователей и предотвратить двойное голосование, user_product_vote - это единственный clean способ, которым я могу представить его реализацию, поскольку любой другой может привести к разреженным записям, дублирующимся записям и всевозможным плохим результатам. вещи.

...