[MDEV-15213] UPDATEs are slow after instant ADD COLUMN Created: 2018-02-05 Updated: 2023-12-14 Resolved: 2018-04-19 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Data Definition - Alter Table, Data Manipulation - Update |
| Affects Version/s: | 10.3.5 |
| Fix Version/s: | 10.3.6 |
| Type: | Bug | Priority: | Major |
| Reporter: | Valerii Kravchuk | Assignee: | Axel Schwenke |
| Resolution: | Won't Fix | Votes: | 0 |
| Labels: | regression | ||
| Environment: |
Ubuntu 14.04, Fedora 27 |
||
| Attachments: |
|
||||||||
| Issue Links: |
|
||||||||
| Description |
|
Consider the following test case, test_instant_alter.sql (that was created mostly to illustrate how fast instant ADD COLUMN is relative to other statements against the table):
To prove the point (how fast is instant ALTER TABLE ... ADD COLUMN is and how much it influeces later changes to the rows of the table), I run it under MariaDB 10.3.5, 10.2.12 and Percona Server 5.7.20. It turned out that while ALTER TABLE itself is indeed instant in 10.3.5, subsequent updates to all rows of the table that set non-default value to newly added column (marked with 1, 2 and 3 in the comments) are executed unexpectedly slow, both comparing to similar updates after full table rebuild (ALTER TABLE t FORCE, marked as 5 and 6 in the comments). Moreover, total time to run the entire test case is much worse on 10.35 comparing to 10.2.12 and other versions the same scenario was tested against. Results are as follows, for example on my Ubuntu 14.04 netbook:
or the following on Fedroa 27 quadcore box:
In all cases servers where freshly installed and started with --no-defaults, like this:
Relative times of individual statements were as follows, for example:
In case of 10.2.12 we see:
So, on 10.3.5 the time to execute UPDATE varies a lot, but is larger than in 10.2.12 after normal ALTER, and also those executed immediately after ALTER are usually slower. |
| Comments |
| Comment by Valerii Kravchuk [ 2018-02-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
This is what I see in perf top when slow updates:
were running (some samples):
Later after table rebuild updates are faster:
In perf top I see:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-04-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
In the perf top, I do not see any obvious InnoDB functions. The btr_search_guess_on_hash() is for the adaptive hash index. The adaptive hash index is only built on key columns, and instantly added columns have special format in the clustered index only. I would be interested in call graphs of what got more expensive. That would be perf top -g or preferably perf record --call-graph (for the duration of the UPDATE) followed by some perf report.
The benchmark seems to involve UPDATE of an instantly added column from a non-default value to another. The initial update from default value to non-default is faster than an update from non-default to non-default. Did I get that correctly? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Axel Schwenke [ 2018-04-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Here some numbers from my tests. I tried first with data on hard disk and small (default) buffers. The numbers were wildly varying. Then I retried with datadir on SSD and a buffer pool large enough to hold the data set. Now the numbers are fairly stable. I tested 3 cases: 1. create + fill the table with 3 columns, update the third column in all (1 mio) rows. 2. create table with 2 columns, fill it, add third column, update as above. 3. create table with 2 columns, fill it, add third column, alter table force, update as above. Conlcusions:
I also observe that the UPDATE overhead after instant ADD COLUMN is higher than the time for ALTER TABLE FORCE (or ADD COLUMN in 10.2). The ALTER TABLE takes ~ 2.5 sec. Yet the time for updating all rows grows from 2.5 sec to nearly 12 sec. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-04-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
axel, thank you. There seem to be some noise in your numbers; cases 1 and 3 should be identical in 10.3, and cases 1,2,3 should be identical in 10.2. Did you test updating the column back to its default value (whatever SELECT would return immediately after the ALTER TABLE…ADD COLUMN? The time to execute a table-rebuilding ALTER should be proportional to the number of rows, the number of indexes, and the size of the indexed columns, while the time to execute instant ADD COLUMN should be pretty much instantaneous (provided that there is no DML operation that would prevent the instant ALTER from acquiring an exclusive lock). If It looks like we have to find out why the UPDATE in the general case has become slower. Can you post some call graphs from perf, comparing 10.2 and 10.3? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Valerii Kravchuk [ 2018-04-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
All kinds of noise aside, the first UPDATE to a non-default value for new column added with instant ALTER is unexpectedly slow in all my tests so far. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-04-08 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
valerii, that the first UPDATE to a non-default value of an instantly added column is slower is a design feature and cannot be addressed. The underlying reason ought to be that pages will have to be split as the records grow bigger. I do not think we can do much about this case. It should be similar to first inserting a number of records with a VARCHAR field being the empty string, and then updating the values to longer strings. Updating back to an empty string (or rolling back the original update) should be faster, because the sparsely filled pages would not be merged immediately. Can you confirm this? What is not expected is that other forms of UPDATE are slower in MariaDB 10.3 than in 10.2, especially when no instant ADD was used. That we can and should do something about. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Axel Schwenke [ 2018-04-10 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
More numbers, this time from 100 executions of the UPDATE.
marko I hope case 1 and 3 are now close enough? I'm also very satisfied that case 2 is just as close (except the first execution of the update, of course). Here the numbers for MariaDB-10.2.14:
Here are the SQL scripts I used. test.sql:
and the procedure from benchmark.sql:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-04-16 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thanks. One more thing that could be tested just to validate my hypothesis is:
I believe that the first update to a longer value will be slower, because it will result in page splits. Later, when the long record values are updated to be shorter again, the pages would not be merged, and subsequent updates (to any value not exceeding the length of the first updated value) should be fast. Please file a separate ticket for the UPDATE performance regression compared to MariaDB 10.2. I think that analyzing it will require some use of perf record with call graph collection enabled. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Axel Schwenke [ 2018-04-16 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Here you go marko:
and this is the test case:
I repeated everything 100 times. The unexpected outcome is, that updating the rows to a longer row image (causing page splits) is actually faster than the second update where the row image shrinks. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Axel Schwenke [ 2018-04-16 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Here is another interesting result. Above I mentioned that ALTER TABLE ... FORCE after the instantly added column runs 5x faster than the full table update while both had the same effect of adding the new column to each rows image. I was curious if that holds for different table sizes, so I cooked a test case where I create the table and fill it with some rows, then INSTANT ADD a column with default "foobar" and measure the time for ALTER TABLE FORCE. Then I do the same, but instead of ALTER TABLE FORCE I do a full table update, setting the new column to "snafu!" (not the default value, but the same number of chars). The second variant I call "expand", the first "force". Here are the execution times for different table sizes:
"sacle" is ld(rownum). So the line with scale=20 corresponds to the test case I used before. Now the funny thing is that ALTER TABLE FORCE scales better than linear where "expand" grows about linear. Execution time for "expand" doubles when the number of rows doubles (scale increases by 1). But "force" is faster than that. I put both series in a log-log diagram and added a regression line. ALTER TABLE is not exactly on that line, but "expand" very much. See attached image scaling.png. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-04-19 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
axel, you could also test rebuilding the table with ALTER TABLE t1 ALGORITHM=COPY. That should be slower than ALTER TABLE t1 FORCE (when it is executed by ALGORITHM=INPLACE). The reason is that with MySQL 5.7’s WL#7277 in MariaDB 10.2, the table is being built one page at a time, rather than one row at a time. With ALGORITHM=COPY for large tables, the results may be skewed due to the InnoDB change buffer. In any case, there is not much that can be done to improve the performance of the UPDATE instantly added columns from the initially default values to something else, and I think that this issue should be closed as "Won’t fix". The generic UPDATE performance regression in 10.3 will be covered by | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Axel Schwenke [ 2018-04-19 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The observed behavior of the first UPDATE being slow is expected. It's also not slower than expected. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-12-14 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|