2013-11-08 6 views
0

У меня был массивный запрос, который использовался для выполнения UNION ALL на множестве таблиц (каждый с тысячами строк), а затем выводит их во временную таблицу перед ее возвратом.Объединить множественные Выборы без союза или союза Все

Старая форма:

SELECT * 
FROM (SELECT `a` AS `Human readable A`, 
       `b` AS `Human readable B`, 
       `c` AS `Human readable C`, 
     FROM `table1` 
     UNION ALL 
     SELECT 
       `a` AS `Human readable A`, 
       `b` AS `Human readable B`, 
       `c` AS `Human readable C`, 
     FROM `table2` 
     UNION ALL 
     SELECT 
       `a` AS `Human readable A`, 
       `b` AS `Human readable B`, 
       `c` AS `Human readable C`, 

     FROM `table3` 
) AS temp_table 

Этот запрос довольно много убил базу данных (запрос занимает где-то от 20 минут до 61 минут), в течение которого времени процессор полностью максимизирован.

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

$result1 = SELECT 
         `a` AS `Human readable A`, 
         `b` AS `Human readable B`, 
         `c` AS `Human readable C`, 

       FROM `table1` 

    $result2 = SELECT 
         `a` AS `Human readable A`, 
         `b` AS `Human readable B`, 
         `c` AS `Human readable C`, 

       FROM `table2` 

    $result3 = SELECT 
         `a` AS `Human readable A`, 
         `b` AS `Human readable B`, 
         `c` AS `Human readable C`, 

       FROM `table3` 

$result4 = merge($result1, $result2, $result3) 

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

Дополнительная информация

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

  • Всех столов имеет точно такую ​​же структуру
  • Пожалуйста, обратите внимание, что около 34 из a AS Human readable A в таблицу данные были разнесены в разные таблицы, потому что они относятся к разным проектам.
  • В этом конкретном запросе имеется 20 союзов (21 таблица).
  • Использование таблиц InnoDB для данных. Я знаю, что это более интенсивно для процессора, чем MyIsam, но, прочитав о различных недостатках MyIsam, я не хочу переключать механизмы хранения.
  • Там нет ИНЕКЕ (данные уже «предварительно сгруппированы» по будучи разделены на таблицы)
+0

Покажите нам (а урезывание версия) запрос – Bohemian

+0

Есть ли у вас какие-либо фильтрации или заказ, перейдя? Если это так, это может быть проблемой, поскольку MySQL должен заказывать итоговые результаты, не имея возможности использовать какие-либо индексы.Если вы просто объединяете все таблицы с помощью 'union all', это должно быть очень быстро, если сортировка или фильтрация не происходит. – GolezTrol

+0

@GolezTrol Я не верю, что у меня есть фильтрация или заказ. Я старался использовать UNION ALL вместо UNION, поскольку я верю в него без ВСЕХ, чтобы он выполнял сортировку/поиск дубликатов. – Programster

ответ

0

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

SET autocommit=0; -- optional, but this is where and how you must start the transaction if you need one 
LOCK TABLES t1 READ, t2 READ, t3 READ; 
SELECT a FROM t1; 
SELECT a FROM t2; 
SELECT a FROM t3; 
UNLOCK TABLES; -- beware: implicit COMMIT 

Если есть какое-то юридическое требование хранить эти данные в нескольких таблицах, вы действительно должны настаивать на том, чтобы слить все эти таблицы в одну таблицу.

+0

Я думаю, что это может быть ответ (в то время как таблицы разделены). Не могли бы вы прояснить, почему следует избегать неявного фиксации. Не было бы безопаснее только добавить COMMIT перед этим заявлением? Кроме того, почему начало транзакции необязательно, конечно, это необходимо? – Programster

+0

1. 'UNLOCK TABLES' фиксирует текущую транзакцию (если она запущена), поэтому вы можете совершать транзакцию неосознанно. 2. Если ваша транзакция не считывается из какой-либо другой таблицы, то реальная транзакция является сверхплотной, потому что 'LOCK TABLES' фактически обеспечивает * больше * изоляции, чем обычная транзакция (вся таблица заблокирована). 3. Я допустил ошибку в рабочем процессе, пожалуйста, проверьте мое обновление. – RandomSeed

+1

Чтение с http://dev.mysql.com/doc/refman/5.0/en/commit.html похоже на то, что я действительно хочу: НАЧАТЬ СДЕЛКУ С СОГЛАСОВАННЫМ СНАПАТОРОМ; (только innodb), затем выбирают запросы, за которыми следует COMMIT, в самом конце без блокировки таблицы. Хотя в этом уникальном случае, поскольку я хочу всю таблицу в каждой таблице, блокировка всей таблицы, вероятно, быстрее (предотвратите блокировку блокировки каждой строки). – Programster

0

Я думал, что предоставил бы два возможных решения и их различные преимущества через образец кода. Одним из решений является «украден» из ответа RandomSeed в:

if ($READING_ONLY_INNODB_TABLES) 
{ 
    /** 
    * - Since these tables are innodb, we can make use of its 'REPEATABLE READ' 
    * isolation level 
    * - Locking the entire tables directly is slightly faster, but this method 
    * allows us to have a consistent view of the database without implementing 
    * ANY locks (which would block other processes). 
    * It may be easier to think of them as locking as this results in the same 
    * result in terms of consistency (innodb even handles phantom reads at this level) 
    * - MyIsam does not support REPEATABLE READ, hence this cannot be used for it 
    */ 
    $query = 
     'START TRANSACTION WITH CONSISTENT SNAPSHOT;'. # --auto sets "SET autocommit=0" 
     $queries_string . # --This is the series of selects 
     'COMMIT;'; 
} 
else 
{ 
    /** 
    * This is a lower resource intensive, 'generic' way (works with MyISAM) that will wait until it can read lock 
    * all the tables before reading. This way we should 'force' a 
    * 'repeatable read'/consitent view. 
    */ 
    $query = 
     'SET autocommit=0;'. # starts the transaction 
     'LOCK TABLES ' . $lock_tables_string . ';' . # Automatically commits anything before this 
     $queries_string . # This is the series of selects from the tables we just locked 
     'UNLOCK TABLES;'; # commits a transaction if any tables currently have been locked with LOCK TABLES  
} 
Смежные вопросы