MySQL: delete… where..in () vs delete..from..join ve alt seçimle silme sırasında kilitli tablolar


9

Feragatname: Lütfen veritabanı dahili bilgileriyle ilgili bilgi eksikliğimi affedin. İşte gidiyor:

Veritabanındaki periyodik bir temizlik işinde büyük bir performans sorunu olan bir uygulama (bizim tarafımızdan yazılmayan) çalıştırıyoruz. Sorgu şuna benzer:

delete from VARIABLE_SUBSTITUTION where BUILDRESULTSUMMARY_ID in (
       select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
       where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");

Basit, okunması kolay ve standart SQL. Ama ne yazık ki çok yavaş. Sorguyu açıklamak, var olan dizinin VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_IDkullanılmadığını gösterir:

mysql> explain delete from VARIABLE_SUBSTITUTION where BUILDRESULTSUMMARY_ID in (
    ->        select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
    ->        where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");
| id | select_type        | table                 | type            | possible_keys                    | key     | key_len | ref  | rows    | Extra       |
+----+--------------------+-----------------------+-----------------+----------------------------------+---------+---------+------+---------+-------------+
|  1 | PRIMARY            | VARIABLE_SUBSTITUTION | ALL             | NULL                             | NULL    | NULL    | NULL | 7300039 | Using where |
|  2 | DEPENDENT SUBQUERY | BUILDRESULTSUMMARY    | unique_subquery | PRIMARY,key_number_results_index | PRIMARY | 8       | func |       1 | Using where |

Bu çok yavaş yapar (120 saniye ve daha fazla). Buna ek olarak, içine BUILDRESULTSUMMARYçıktı eklemeye çalışan sorguları engelliyor gibi görünüyor show engine innodb status:

---TRANSACTION 68603695, ACTIVE 157 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 127964, OS thread handle 0x7facd0670700, query id 956555826 localhost 127.0.0.1 bamboosrv updating
update BUILDRESULTSUMMARY set CREATED_DATE='2015-06-18 09:22:05', UPDATED_DATE='2015-06-18 09:22:32', BUILD_KEY='BLA-RELEASE1-JOB1', BUILD_NUMBER=8, BUILD_STATE='Unknown', LIFE_CYCLE_STATE='InProgress', BUILD_DATE='2015-06-18 09:22:31.792', BUILD_CANCELLED_DATE=null, BUILD_COMPLETED_DATE='2015-06-18 09:52:02.483', DURATION=1770691, PROCESSING_DURATION=1770691, TIME_TO_FIX=null, TRIGGER_REASON='com.atlassian.bamboo.plugin.system.triggerReason:CodeChangedTriggerReason', DELTA_STATE=null, BUILD_AGENT_ID=199688199, STAGERESULT_ID=230943366, RESTART_COUNT=0, QUEUE_TIME='2015-06-18 09:22:04.52
------- TRX HAS BEEN WAITING 157 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 38 page no 30140 n bits 112 index `PRIMARY` of table `bamboong`.`BUILDRESULTSUMMARY` trx id 68603695 lock_mode X locks rec but not gap waiting
------------------
---TRANSACTION 68594818, ACTIVE 378 sec starting index read
mysql tables in use 2, locked 2
646590 lock struct(s), heap size 63993384, 3775190 row lock(s), undo log entries 117
MySQL thread id 127845, OS thread handle 0x7facc6bf8700, query id 956652201 localhost 127.0.0.1 bamboosrv preparing
delete from VARIABLE_SUBSTITUTION  where BUILDRESULTSUMMARY_ID in   (select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY where BUILDRESULTSUMMARY.BUILD_KEY = 'BLA-BLUBB10-SON')

Bu sistemi yavaşlatır ve bizi artırmaya zorlar innodb_lock_wait_timeout.

MySQL'i çalıştırdığımızda, "birleştirme işleminden sil" kullanmak için silme sorgusunu yeniden yazdık:

delete VARIABLE_SUBSTITUTION from VARIABLE_SUBSTITUTION join BUILDRESULTSUMMARY
   on VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID = BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID
   where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1";

Bu okunması biraz daha kolay, ne yazık ki standart bir SQL yok (bulabildiğim kadarıyla), ancak indeksi kullandığı için çok daha hızlı (0.02 saniye kadar):

mysql> explain delete VARIABLE_SUBSTITUTION from VARIABLE_SUBSTITUTION join BUILDRESULTSUMMARY
    ->    on VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID = BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID
    ->    where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1";
| id | select_type | table                 | type | possible_keys                    | key                      | key_len | ref                                                    | rows | Extra                    |
+----+-------------+-----------------------+------+----------------------------------+--------------------------+---------+--------------------------------------------------------+------+--------------------------+
|  1 | SIMPLE      | BUILDRESULTSUMMARY    | ref  | PRIMARY,key_number_results_index | key_number_results_index | 768     | const                                                  |    1 | Using where; Using index |
|  1 | SIMPLE      | VARIABLE_SUBSTITUTION | ref  | var_subst_result_idx             | var_subst_result_idx     | 8       | bamboo_latest.BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID |   26 | NULL                     |

İlave bilgi:

mysql> SHOW CREATE TABLE VARIABLE_SUBSTITUTION;
| Table                 | Create Table |
| VARIABLE_SUBSTITUTION | CREATE TABLE `VARIABLE_SUBSTITUTION` (
  `VARIABLE_SUBSTITUTION_ID` bigint(20) NOT NULL,
  `VARIABLE_KEY` varchar(255) COLLATE utf8_bin NOT NULL,
  `VARIABLE_VALUE` varchar(4000) COLLATE utf8_bin DEFAULT NULL,
  `VARIABLE_TYPE` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `BUILDRESULTSUMMARY_ID` bigint(20) NOT NULL,
  PRIMARY KEY (`VARIABLE_SUBSTITUTION_ID`),
  KEY `var_subst_result_idx` (`BUILDRESULTSUMMARY_ID`),
  KEY `var_subst_type_idx` (`VARIABLE_TYPE`),
  CONSTRAINT `FK684A7BE0A958B29F` FOREIGN KEY (`BUILDRESULTSUMMARY_ID`) REFERENCES `BUILDRESULTSUMMARY` (`BUILDRESULTSUMMARY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

mysql> SHOW CREATE TABLE BUILDRESULTSUMMARY;
| Table              | Create Table |
| BUILDRESULTSUMMARY | CREATE TABLE `BUILDRESULTSUMMARY` (
  `BUILDRESULTSUMMARY_ID` bigint(20) NOT NULL,
....
  `SKIPPED_TEST_COUNT` int(11) DEFAULT NULL,
  PRIMARY KEY (`BUILDRESULTSUMMARY_ID`),
  KEY `FK26506D3B9E6537B` (`CHAIN_RESULT`),
  KEY `FK26506D3BCCACF65` (`MERGERESULT_ID`),
  KEY `key_number_delta_state` (`DELTA_STATE`),
  KEY `brs_build_state_idx` (`BUILD_STATE`),
  KEY `brs_life_cycle_state_idx` (`LIFE_CYCLE_STATE`),
  KEY `brs_deletion_idx` (`MARKED_FOR_DELETION`),
  KEY `brs_stage_result_id_idx` (`STAGERESULT_ID`),
  KEY `key_number_results_index` (`BUILD_KEY`,`BUILD_NUMBER`),
  KEY `brs_agent_idx` (`BUILD_AGENT_ID`),
  KEY `rs_ctx_baseline_idx` (`VARIABLE_CONTEXT_BASELINE_ID`),
  KEY `brs_chain_result_summary_idx` (`CHAIN_RESULT`),
  KEY `brs_log_size_idx` (`LOG_SIZE`),
  CONSTRAINT `FK26506D3B9E6537B` FOREIGN KEY (`CHAIN_RESULT`) REFERENCES `BUILDRESULTSUMMARY` (`BUILDRESULTSUMMARY_ID`),
  CONSTRAINT `FK26506D3BCCACF65` FOREIGN KEY (`MERGERESULT_ID`) REFERENCES `MERGE_RESULT` (`MERGERESULT_ID`),
  CONSTRAINT `FK26506D3BCEDEEF5F` FOREIGN KEY (`STAGERESULT_ID`) REFERENCES `CHAIN_STAGE_RESULT` (`STAGERESULT_ID`),
  CONSTRAINT `FK26506D3BE3B5B062` FOREIGN KEY (`VARIABLE_CONTEXT_BASELINE_ID`) REFERENCES `VARIABLE_CONTEXT_BASELINE` (`VARIABLE_CONTEXT_BASELINE_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

(bazı şeyler atlandı, oldukça geniş bir tablo).

Bu konuda birkaç sorum var:

  • sorgu iyileştirici neden birleştirme sürümünü kullanırken alt sorgu sürümünde silmek için dizini kullanamıyor?
  • endeksi kullanmak için kandırmak için herhangi bir (ideal standartlara uygun) yolu var mı? veya
  • yazmak için taşınabilir bir yolu var delete from joinmı? Uygulama jdbc ve Hibernate ile kullanılan PostgreSQL, MySQL, Oracle ve Microsoft SQL Server'ı destekler.
  • neden yalnızca alt seçimde kullanılan VARIABLE_SUBSTITUTIONengelleme eklerinden silme BUILDRESULTSUMMARYkullanılıyor?

Percona Server 5.6.24-72.2-1.jessie resp 5.6.24-72.2-1.wheezy (test sisteminde).
0x89

Evet, tüm veritabanı innodb kullanır.
0x89

Görünüşe göre 5.6'nın iyileştiriciyi geliştirmeye fazla ilgisi yok. 5.7'yi beklemeniz gerekecek (ancak eğer yapabiliyorsanız MariaDB'yi deneyin. Optimize edici iyileştirmeleri 5.3 ve 5.5 sürümlerinde tekrar yapıldı.)
ypercubeᵀᴹ

@ypercube AFAIK çatal hiçbir silme alt sorgusu optimize 5.7 yapmak için bir geliştirme vardır. Silmeler, SELECT ifadelerinden farklı şekilde optimize edilir.
Morgan Tocker

Yanıtlar:


7
  • sorgu iyileştirici neden birleştirme sürümünü kullanırken alt sorgu sürümünde silmek için dizini kullanamıyor?

Çünkü optimize edici bu konuda biraz aptaldı. İçin Sadece DELETEve UPDATEancak için SELECTde ifadeleri, böyle bir şey WHERE column IN (SELECT ...)tam olarak optimize edilmedi. Yürütme planı genellikle dış tablonun her satırı için alt sorgunun çalıştırılmasını içeriyordu ( VARIABLE_SUBSTITUTIONbu durumda). Bu masa küçükse, her şey yolunda. Eğer büyükse, umut yok. Daha eski sürümlerde bile, alt-alt sorgulu INbir INalt sorgu, EXPLAINasırlarca çalışmasını bile sağlar .

Yapabileceğiniz şey - bu sorguyu korumak istiyorsanız, birkaç optimizasyon uygulayan en son sürümleri kullanmak ve tekrar test etmektir. En son sürümlerin anlamı: MySQL 5.6 (ve beta çıktığında 5.7) ve MariaDB 5.5 / 10.0

(güncelleme) Optimizasyon geliştirmeleri olan 5.6'yı zaten kullanıyorsunuz ve bu ilgili: Yarı Bağlantı Dönüşümleri ile Alt Sorguları Optimize Etme Tek başına
bir dizin eklemenizi öneririm (BUILD_KEY). Bileşik bir tane var ama bu sorgu için çok kullanışlı değil.

  • endeksi kullanmak için kandırmak için herhangi bir (ideal standartlara uygun) yolu var mı?

Aklıma gelen hiçbir şey yok. Kanımca, standart SQL kullanmaya çalışmanın pek bir değeri yok. Her DBMS'nin sahip olduğu pek çok farklılık ve küçük tuhaflıklar var ( UPDATEve DELETEifadeler bu farklılıkların iyi örnekleridir), her yerde çalışan bir şey kullanmaya çalıştığınızda, sonuç çok sınırlı bir SQL alt kümesidir.

  • katılmak silmek silmek için taşınabilir bir yolu var mı? Uygulama jdbc ve Hibernate ile kullanılan PostgreSQL, MySQL, Oracle ve Microsoft SQL Server'ı destekler.

Önceki soru ile aynı cevap.

  • VARIABLE_SUBSTITUTION engellemesinden silme neden yalnızca alt seçimlerde kullanılan BUILDRESULTSUMMARY öğesine ekleniyor?

% 100 emin değilim ama ben alt sorgu birden çok kez ve tablo üzerinde hangi kilitler alıyor türü ile ilgili olduğunu düşünüyorum.


İnnodb_status dosyasından (silme işleminin) "3775190 satır kilidi (kilitleri)" son derece düşündürücüdür. Ama aynı zamanda "mysql tabloları kullanım 2, kilitli 2" bana çok iyi görünmüyor ..
0x89

2

işte iki sorunuzun cevapları

  • Optimizer, her satır için where cümlesi değiştiğinden dizin kullanamıyor. Silme ifadesi, optimize ediciyi geçtikten sonra şöyle görünecektir

    delete from VARIABLE_SUBSTITUTION where EXISTS (
    select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
    where BUILDRESULTSUMMARY.BUILD_KEY = BUILDRESULTSUMMARY_ID AND BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");

ancak birleştirme işlemini gerçekleştirdiğinizde, Sunucu silmeye tabi olan satırları belirleyebilir.

  • hile, değişkeni tutmak BUILDRESULTSUMMARY_IDve değişkeni sorgu yerine kullanmaktır. Hem değişken başlatma hem de silme sorgusunun bir oturum içinde çalışması gerektiğini unutmayın. Böyle bir şey.

    SET @ids = (SELECT GROUP_CONCAT(BUILDRESULTSUMMARY_ID) 
            from BUILDRESULTSUMMARY where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1" ); 
    delete from VARIABLE_SUBSTITUTION where FIND_IN_SET(BUILDRESULTSUMMARY_ID,@ids) > 0;

    sorgu çok fazla kimlik döndürürse bu sorunla karşılaşabilirsiniz ve bu standart bir yol değildir. Bu sadece bir çözüm.

    Ve diğer iki sorunuz için bir cevabım yok :)


Tamam, ne demek istediğimi kaçırdın. Ben ne olarak görmediği VARIABLE_SUBSTITUTION ve BUILDRESULTSUMMARY hem olması gerektiği, böylece BUILDRESULTSUMMARY_ID adlı bir sütun olması düşünüyorum: 'MEVCUT VARIABLE_SUBSTITUTION silmek (BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID = VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID VE BUILDRESULTSUMMARY.BUILD_KEY = "BAM BUILDRESULTSUMMARY dan BUILDRESULTSUMMARY_ID seçin -1" );. Sonra mantıklı ve her iki sorgu da aynı şeyi yapıyor.
0x89

1
evet sadece dış tabloya bir referans eksik. Ama mesele bu değil. Bu sadece optimize edicide nasıl ele alınacağının bir örneğidir.
Mesud

Optimize edicinin eşdeğer bir sorgu üreteceği küçük farkla.
0x89
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.