Как разделить очень большую таблицу сообщений в mysql - PullRequest
0 голосов
/ 15 января 2020

У меня есть таблица, которая содержит сообщения от пользователей. Проект будет хранить одну копию сообщения как для отправителя, так и для получателя (каждый человек имеет свои собственные флаги чтения / удаления для сообщения).

CREATE TABLE cloob_msg.cl_inbox (
  id int(11) NOT NULL AUTO_INCREMENT,
  `user` int(11) NOT NULL,
  contact int(11) NOT NULL,
  sdate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  body text NOT NULL,
  userstatus tinyint(4) NOT NULL DEFAULT 1 COMMENT '0: new, 1:read, 2: deleted',
  contactstatus tinyint(4) NOT NULL DEFAULT 0,
  class tinyint(4) NOT NULL DEFAULT 0,
  attachtype tinyint(4) NOT NULL DEFAULT 0,
  attachsrc varchar(255) DEFAULT NULL,
  PRIMARY KEY (id),
  INDEX i_class (class),
  INDEX i_contact_user (contact, user),
  INDEX i_contactstatus (contactstatus),
  INDEX i_user_contact (user, contact),
  INDEX i_userstatus (userstatus)
)

Чтобы составить список бесед между двумя людьми, мы используйте следующий запрос:

select * from cl_inbox
    where (user=user1 and contact=user2 and userstatus<>2)
       or (user=user2 and contact=user1 and contactstatus<>2)
    order by id limit ?,?

Теперь, как я могу разбить таблицу (у нас есть миллиарды сообщений, поэтому ее действительно нужно разбить)? Какой уникальный ключ и какие поля разбиения выбрать?

Спасибо.

1 Ответ

2 голосов
/ 16 января 2020

Почему раздел? По своей сути он не обеспечит никакой производительности.

Чтобы ускорить этот запрос, измените or на union:

( select * from cl_inbox
    where (user=user1 and contact=user2 and userstatus<>2)
    order by id limit ?,?
) UNION ALL
( select * from cl_inbox
    where (user=user2 and contact=user1 and contactstatus<>2)
    order by id limit ?,?
)

Теперь каждая часть может независимо использовать i_contact_user или i_user_contact. (Ваша версия должна была выполнить полное сканирование таблицы.) Это будет работать намного быстрее. Кстати, для этого запроса эти два индекса одинаково хороши. Если вам не нужны оба для других запросов, я предлагаю отказаться от одного из них. Индексы *status (и другие «флаги»), вероятно, бесполезны, безусловно, бесполезны для этого запроса.

Следующая проблема: использование OFFSET для разбивки на страницы проблематично c. А при переключении на UNION он теперь не работает.

Итак, вместо этого «запомните, где вы остановились». Поскольку вы говорите order by id, я предполагаю, что идентификаторы в порядке, желаемом пользовательского интерфейса? Удалите OFFSET и используйте id:

( select * from cl_inbox
    where (user=user1 and contact=user2 and userstatus<>2)
      AND id < $left_off
    ORDER BY id DESC
    LIMIT ?
) UNION ALL
( select * from cl_inbox
    where (user=user2 and contact=user1 and contactstatus<>2)
      AND id < $left_off
    ORDER BY id DESC
    LIMIT ?
)
ORDER BY id DESC
LIMIT ?

(Да, я намеренно повторяю ORDER BY и LIMIT.) И я взял на себя смелость перевернуть вещи - не надо Вы хотите последние сообщения первыми?

Дополнительные обсуждения: http://mysql.rjweb.org/doc.php/pagination

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

...