Mysql
 sql >> база данни >  >> RDS >> Mysql

Оптимизиране ORDER BY

Това е много интересно запитване. По време на неговата оптимизация може да откриете и разберете много нова информация за това как работи MySQL. Не съм сигурен, че ще имам време да напиша всичко подробно наведнъж, но мога постепенно да актуализирам.

Защо е бавно

Основно има два сценария:бърз и бавен .

В бързо сценарий, вие вървите в някакъв предварително дефиниран ред над таблица и вероятно в същото време бързо извличате някои данни по идентификатор за всеки ред от други таблици. В този случай спирате да ходите веднага щом имате достатъчно редове, посочени от вашата LIMIT клауза. Откъде идва поръчката? От индекс на b-дърво, който имате в таблицата, или реда на набор от резултати в подзаявка.

В бавен сценарий нямате този предварително дефиниран ред и MySQL трябва имплицитно да постави всички данни във временна таблица, да сортира таблицата в някакво поле и да върне n редове от вашата LIMIT клауза. Ако някое от полетата, които сте поставили в тази временна таблица, е от тип TEXT (не VARCHAR), MySQL дори не се опитва да запази тази таблица в RAM и я изтрива и сортира на диска (оттук и допълнителна IO обработка).

Първо нещо, което трябва да поправите

Има много ситуации, когато не можете да изградите индекс, който ще ви позволи да следвате неговия ред (когато ПОРЪЧИТЕ ПО колони от различни таблици, например), така че основното правило в такива ситуации е да минимизирате данните, които MySQL ще постави във временната таблица. Как можеш да го направиш? Избирате само идентификатори на редовете в подзаявка и след като имате идентификаторите, присъединявате идентификаторите към самата таблица и други таблици, за да извлечете съдържанието. Това означава, че правите малка маса с поръчка и след това използвате бързия сценарий. (Това леко противоречи на SQL като цяло, но всеки вариант на SQL има свои собствени средства за оптимизиране на заявките по този начин).

По съвпадение вашият SELECT -- everything is ok here изглежда смешно, тъй като това е първото място, където не е наред.

SELECT p.*
    , u.name user_name, u.status user_status
    , c.name city_name, t.name town_name, d.name dist_name
    , pm.meta_name, pm.meta_email, pm.meta_phone
    , (SELECT concat("{", 
        '"id":"', pc.id, '",', 
        '"content":"', replace(pc.content, '"', '\\"'), '",', 
        '"date":"', pc.date, '",', 
        '"user_id":"', pcu.id, '",', 
        '"user_name":"', pcu.name, '"}"') last_comment_json 
        FROM post_comments pc 
        LEFT JOIN users pcu ON (pcu.id = pc.user_id) 
        WHERE pc.post_id = p.id
        ORDER BY pc.id DESC LIMIT 1) AS last_comment
FROM (
    SELECT id
    FROM posts p
    WHERE p.status = 'published'
    ORDER BY 
        (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
            THEN +p.reputation ELSE NULL END) DESC, 
        p.id DESC
    LIMIT 0,10
) ids
JOIN posts p ON ids.id = p.id  -- mind the join for the p data
LEFT JOIN users u ON (u.id = p.user_id)
LEFT JOIN citys c ON (c.id = p.city_id)
LEFT JOIN towns t ON (t.id = p.town_id)
LEFT JOIN dists d ON (d.id = p.dist_id)
LEFT JOIN post_metas pm ON (pm.post_id = p.id)
;

Това е първата стъпка, но дори сега можете да видите, че не е необходимо да правите тези безполезни LEFT JOINS и json сериализации за редовете, от които не се нуждаете. (Пропуснах GROUP BY p.id , тъй като не виждам кое LEFT JOIN може да доведе до няколко реда, не правите агрегиране).

тепърва ще пиша за:

  • индекси
  • преформулирайте клаузата CASE (използвайте UNION ALL)
  • вероятно принудително въвеждане на индекс


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL срещу MariaDB:какво трябва да знаете

  2. Как да получите редов ранг?

  3. Как да замените MySQL с Percona на Plesk CentOS 7

  4. Използвайте едноредова заявка с MySQL и PHP

  5. Вмъкване на PDO масив с помощта на ключ като име на колона