Должен ли я индексировать логическое поле с низкой мощностью "Истина" MySQL? - PullRequest
2 голосов
/ 17 июня 2020

У меня есть таблица MESSAGE с 1 млн строк (и растет). Каждый запрос сообщений включает выбор строк WHERE isRequest = True или WHERE isRequest = False, но не обоих одновременно. Подавляющее большинство моих запросов ищут isRequest = False. В эту таблицу пишут очень часто, и мне нужно поддерживать быструю запись (поскольку пользователи любят отправлять сообщения друг другу с малой задержкой). Также обратите внимание, что таблица MESSAGE в настоящее время не имеет индексов столбцов, кроме первичного ключа.

95% строк имеют isRequest = False и только 5% строк имеют isRequest = True. Является ли индексирование логического поля isRequest более производительным в таком сценарии?

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

Обновление:

После дальнейшего анализа с @Rick James мы придумали новую схему таблицы ( обратите внимание, что все PK являются автоматическими c, так что относительность времени заметна ):

MESSAGE (id=PK) (sender_id, recipient_id, conversation_id = FKs)
---------------------------------------------------------------
id  sender_id   recipient_id  message            conversation_id
1    1          2            "hows it going"   4
2    2          1            "great! hbu"      4
3    1          8            "hey man"         3
4    9          1            "please respond"  2
5    4          6            "goodnight girl"  1


CONVERSATION (id=PK) (userA_id, userB_id = FKs)
-----------------------------------------------
id  userA_id  userB_id
1   4          6            
2   1          9
3   1          8
4   1          2


USERCONVERSATION (id=PK) (userA/B_id, conver_id, lastMsg_id = FKs)
------------------------------------------------------------------
id   userA_id  userB_id   conver_id  lastMsg_id   isRequest
1    4         6          1          5            False
2    6         4          1          5            False
3    1         9          2          4            True
4    9         1          2          4            True
5    1         8          3          3            False
6    8         1          3          3            False
7    1         2          4          2            False
8    2         1          4          2            False

Индексы:

MESSAGE: index(id),
         index(conversation_id, id)

CONVERSATION: index(id), 

USERCONVERSATION: index(id),
       index(user_id, isRequest),
       index(user_id, lastMessage_id),
       index(conversation_id)

Запросы в приложении:

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

Чтобы получить последние 20 разговоров (включая содержимое последнего сообщения и информацию о другом пользователе) для переменной userID:

SELECT  T4.userB_id, T4.username, T4.profilePic, T4.conver_id,
        T4.message 
    (
        SELECT  T1.userB_id, T2.username, T2.profilePic, T1.conversation_id,
                T1.lastMessage_id
            FROM  
            (
                SELECT  userB_id, conversation_id, lastMessage_id
                    FROM  rage.userconversation
                    WHERE  userA_id = {userID}
                      AND  isRequest=False
            ) AS T1
            LEFT JOIN  rage.user AS T2  ON T1.userB_id = T2.id AS T3
    )
    LEFT JOIN  rage.message AS T4  ON T1.lastMessage_id = T4.id
    ORDER BY  T4.id DESC
    LIMIT  20

Word объяснение: Получите 20 самых последних строк USERCONVERSATION, поскольку там хранится последнее сообщение. Чтобы найти 20 самых последних для данного пользователя, выберите все строки с user_id = userID и отсортируйте их по lastMessage_id DES C. Это верно, потому что message_id увеличивается автоматически. Наряду с последним сообщением нам нужно получить некоторые пользовательские данные (изображение профиля, имя пользователя) другого пользователя в разговоре. Мы достигаем этого путем присоединения слева.

Результат:

RESULT (for userID = 1)
---------------------------------------------------------------
userB_id  username   profilePic  message            conver_id
8         John       8.jpg       "hey man"          3
2         Daisy      2.jpg       "great! hbu"       4

Затем, когда пользователь нажимает на беседу, поскольку у нас есть chat_id, мы просто:

SELECT * FROM rage.message WHERE conversation_id={conver_id} ORDER BY id DESC LIMIT 20

Надеюсь, с тех пор, как мы проиндексировали (dialog_id, id), сортировка стала быстрой.

Ответы [ 3 ]

0 голосов
/ 17 июня 2020

Используйте составной индекс. Давайте рассмотрим предложение WHERE полностью, чтобы дать вам точные сведения.

Пример

WHERE IsRequest = True
  AND UserId = 12345

выиграет от

INDEX(IsRequest, UserId)

(и неважно, в каком порядке вы вводите имена столбцов, и не имеет значения, истинно оно или ложно.)

Ваш пример

  • OR разрушает использование индексов
  • UNION между двумя запросами можно избежать OR.
  • Никакой индекс не полезен для запроса, как вы его написали.
  • Будет два вложенных сканирование таблиц.

Возможно

(я не знаю, делает ли следующее то же самое.)

( SELECT  m1.id, m1.sender_id, m1.recipient_id, m1.message ...
    FROM  myapp_message AS m1
    LEFT JOIN  app_message AS m2
         ON  m1.sender_id = m2.sender_id
        AND  m1.id < m2.id
    WHERE  m2.id IS NULL
      AND  m1.sender_id = {userID}
      AND  m1.isRequest = False
    order by  id desc
    LIMIT  20
) UNION ALL (
SELECT  m1.id, m1.sender_id, m1.recipient_id, m1.message ...
    FROM  myapp_message AS m1
    LEFT JOIN  app_message AS m2
         ON  m1.recipient_id = m2.recipient_id
        AND  m1.id < m2.id
    WHERE  m2.id IS NULL
      AND  m1.recipient_id= {userID}
      AND  m1.isRequest = False
    order by  id desc
    LIMIT  20 
)   ORDER BY id DESC LIMIT 20

Если вы будете разбивать на страницы, посмотрите это: http://mysql.rjweb.org/doc.php/pagination#pagination_and_union

Closer

SELECT  m...
    FROM
      ( SELECT xid, MAX(mid) AS mid
        FROM
        (
          ( SELECT  recipient_id AS xid,
                    MAX(mid) AS mid      -- The last message TO each recipient
                FROM  WHERE sender_id = 1234  -- FROM the user in question
                GROUP BY  recipient_id
                ORDER BY 2 DESC   -- ("2nd column")
                LIMIT  20                        
          )
          UNION ALL
          ( SELECT  sender_id AS xid,
                    MAX(mid) AS mid      -- The last message FROM each sender
                FROM  WHERE recipient_id = 1234  -- TO the user
                GROUP BY  sender_id
                ORDER BY 2 DESC
                LIMIT  20
          )
        ) AS y
        GROUP BY xid       -- yes, repeated
        ORDER BY mid DESC  -- yes, repeated
        LIMIT 20           -- yes, repeated
      ) AS x
    JOIN messages AS m  ON m.mid = x.mid

С обоими из этих индексов:

INDEX(sender_id, recipient_id, mid)
INDEX(recipient_id, sender_id, mid)

Один INDEX для каждого подзапроса. Каждый из них оптимален, плюс "покрытие".

(Я не вижу релевантности isRequest, поэтому я исключил его. Я подозреваю, что если столбец нужен, его можно добавить в индексы без потеря эффективности - при правильном положении.)

0 голосов
/ 18 июня 2020

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

(LEAST(sender_id, recipient_id), GREATEST(recipient_id, sender_id))

Тогда INDEX(conversation_id, id), вероятно, будет ключом к обсуждаемому запросу. На этом этапе мы можем вернуться к обсуждению логического значения. Я подозреваю, что в конечном итоге это будет оптимальный индекс:

INDEX(conversation_id, isRequest, id)

(или, возможно, с заменой первых двух столбцов).

0 голосов
/ 17 июня 2020

У вас есть несколько вариантов. Из того, что вы описываете, подходит один из следующих двух:

  • A кластеризованный индекс, где первый ключ - IsRequest.
  • Схема разделения, которая включает IsRequest.

Другая возможность - две отдельные таблицы.

Однако, поскольку я сомневаюсь, что ваши запросы возвращают 95% строк - или даже 5% - - несомненно, есть и другие фильтры. Может быть важнее создать индексы для этих фильтров, чем для логического флага.

...