Opened 5 years ago
Closed 5 years ago
#31275 closed Cleanup/optimization (fixed)
Optimize MariaDB/MySQL sql_flush
Reported by: | Adam Johnson | Owned by: | Masashi SHIBATA |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Simon Charette | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
This was suggested as an extra feature for Django-MySQL by @jonatron in https://github.com/adamchainz/django-mysql/pull/611/files , but it would make more sense to make it in Django core.
Currently MySQL's sql_flush emits TRUNCATE TABLE statements. These are relatively slow on small tables since they force recreation of the table on disk. The alternative, DELETE FROM
, is faster. I can see a diference even for a single empty table locally (MariaDB 10.4):
chainz@localhost [21]> truncate t2; Query OK, 0 rows affected (0.005 sec) chainz@localhost [22]> delete from t2; Query OK, 0 rows affected (0.000 sec)
TransactionTestCase
uses sql_flush, via the flush management command, to reset the database. Most test suites don't use very large tables, so using DELETE FROM
should normally be faster in this case. Optimizing it MySQL to use DELETE FROM
for flushing, at least for tables up to a certain size (1000 rows or similar heuristic) could save lot of time when using TransactionTestCase
.
Change History (13)
comment:1 by , 5 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 5 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:3 by , 5 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
If I recall correctly, TRUNCATE will reset AUTO_INCREMENT but DELETE FROM won't.
comment:4 by , 5 years ago
If that's the case we'd want to ALTER TABLE tablename AUTO_INCREMENT = 1
for those tables with auto-increment keys.
comment:8 by , 5 years ago
Has patch: | set |
---|
comment:10 by , 5 years ago
Cc: | added |
---|
If that's the case we'd want to
ALTER TABLE tablename AUTO_INCREMENT = 1
for those tables with auto-increment keys.
I decided to guide the patch submitter towards avoiding these queries and suggesting the use of TransactionTestCase.reset_sequences
instead while documenting the behaviour changed in Django 3.1 and that this feature should be explicitly enabled to achieve this previously implicit reset.
It made more sense to me since the goal of the ticket is to make this operation faster and these sequence alteration queries took longer than plain TRUNCATE
calls from my local testing which completely defeats the purpose of this ticket. The fact we have an explicit feature to achieve this behaviour only makes it slightly backward incompatible for users relying on this undefined behaviour.
Based on the fact ALTER TABLE tablename AUTO_INCREMENT = 1
is slower than TRUNCATE tablename
I guess MySQL's sql_flush
could be optimized further to only issue the latter when asked to flush both a table and its sequence. Given TransactionTestCase
calls the flush command with reset_sequences=False
that would allow us to favour DELETE
over TRUNCATE
when no sequences are meant to be flushed and grasp the benefit in the test suite without relying on the 1000 rows heuristics which is a bit arbitrary and require a schema introspection query which isn't cheap either.
I'll add that the benefits for Django's test suite and any suite that heavily uses TransactionTestCase.available_apps
will likely be minimal as this ms difference between DELETE
and TRUNCATE
is only apparent when the number flushed tables after every test is large and the available_apps
feature greatly reduces it this number in large Django project. In summary projects that will benefits from this patch would benefit way more by defining available_apps
on their tests.
comment:11 by , 5 years ago
I decided to guide the patch submitter towards avoiding these queries and suggesting the use of
TransactionTestCase.reset_sequences
instead while documenting the behaviour changed in Django 3.1 and that this feature should be explicitly enabled to achieve this previously implicit reset.
Very sensible. I forget about this option!
In summary projects that will benefits from this patch would benefit way more by defining
available_apps
on their tests.
Yes indeed, although it can be tedious to define and keep correct.
comment:12 by , 5 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
I confirm that
TransactionTestCase
is horrrrribly slow on MySQL/MariaDB! So a big +1 from me.