How to apply ROW_FORMAT=DYNAMIC to an existing table
Asked Answered
N

3

5

I have a table with a large number of longtext fields (18) along with a number of other various integer and varchar fields. Recently a number of additional longtext fields were added, and have suddenly forced me to learn all about 8K row size limits. The DB is running Mysql 5.6.34, and the table in question is currently Antelope / ROW_FORMAT=COMPACT.

My understanding is that in this format, each column will take up to 768 bytes per row, until overflowing into separate storage. This leads me to this error when too many of the various longtexts get a significant amount of data:

Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. Ignoring the rest of the fields in the table, were all 18 longtexts at 768 bytes, then the primary index would be storing 13,824 bytes for all fields.

I have attempted to update the table to ROW_FORMAT=DYNAMIC with the expectation that this should lower a longtext's overflow threshold from 768 bytes to just 20 bytes, and therefore should lower the maximum primary index storage requirement for all long text fields to 18 * 20 = 360 bytes. I attempted the update per the following:

ALTER TABLE mytable ROW_FORMAT=DYNAMIC;
OPTIMIZE TABLE mytable;

To no errors and the following output:

mydb.mytable optimize note Table does not support optimize, doing recreate + analyze instead mydb.mytable optimize status OK

If I view the CREATE TABLE syntax for the table I can see that the ROW_FORMAT=DYNAMIC is set.

I have then attempted to write a row to fill all longtext columns with around 5.7kb each, however I'm only able to fill 10 of them before I am preventing from saving the row, and 10 * 768 = 7,680 bytes, which when accounting for the other non-longtext required fields is getting pretty close to the 8kb limit, suggesting the ROW_FORMAT=DYNAMIC instruction is not applying to existing rows.

I don't particularly want to have to recreate the database by dumping/importing however as it is particularly large and would represent an extended service downtime that I'm not sure I can justify until other options are exhausted.

Natascha answered 9/7, 2018 at 16:29 Comment(9)
It appears that MySQL 5.7 seems a little more Barracuda orientated, is it worth upgrading to that or is the Barracuda and ROW_FORMAT=DYNAMIC handling identical other than the defaults?Natascha
It's always worth upgrading to the latest version that doesn't break your application.Priscella
Well of course, but regression testing isn't free :)Natascha
What I mean is it's always worth at least trying to upgrade. If testing is that much of a hassle you need more automated tests. The biggest changes in 5.7 are new defaults that make it a lot more particular about aggregate clauses where the results were previously ambiguous.Priscella
DYNAMIC row format is not supported with Antelope, only COMPACT and REDUNDANT are. As you mentioned, Barracuda is now the recommended/default format. If you consider upgrading, you should read this: dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html. Serious compatibility and corruption issues can arise when converting between file formats.Boltonia
There was never any version of MySQL called 3.6.34. Do you mean 5.6.34? Run query SELECT @@version; to check.Surfbird
Note that you may still have blob/text fields take up to 768 bytes per row in the primary page, if they fit within the row. If a blob/text doesn't fit, then the whole blob/text is moved to overflow pages and replaced with a 20-byte pointer to the first overflow page.Surfbird
@BillKarwin yes a typo, 5.6.34 thanksNatascha
@BillKarwin ahh OK, well as long as the net effect is that I no longer see the row limit error I don't mind too much when specific fields are paging. Thanks againNatascha
E
9

(This answer, though focused on indexes, is likely to solve your table problem.)

http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes

There are 5 choices for dealing with the 767 limit. This one seems to be the one you need

   SET GLOBAL innodb_file_format=Barracuda;
   SET GLOBAL innodb_file_per_table=1;
   SET GLOBAL innodb_large_prefix=1;
   logout & login (to get the global values);
   ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

(Upgrading to 5.7.7 or later is another solution -- but that only sets the above things as default; you would still need to do the ALTER, I think.)

Estevan answered 9/7, 2018 at 22:52 Comment(3)
Ahh yes, I think I already had these values set, possibly a default for the RDS MySQL configuration.Natascha
You specified Antelope and COMPACT in your question. If RDS won't let you change them, then this Answer is not available to you.Estevan
@RickJames Thank you for specifying the logout & login step. I was missing that and wondering why it would not work despite setting the global values as specified in answers elsewhere. (v 5.6.33)Recess
N
1

Turns out I needed to set the innodb_file_format=barracuda before the ALTER/OPTIMIZE. For some reason I'd taken it as gospel that setting DYNAMIC would have implicitly set barracuda as well, however this has no longer proven to be the case:

SET GLOBAL innodb_file_format=barracuda
Natascha answered 9/7, 2018 at 19:26 Comment(1)
That's part of the answer.Estevan
M
0

This is the documentation from MySQL. To create tables that use COMPRESSED OR DYNAMIC Barracuda file format needs to be enabled.

https://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_file_format

Meir answered 4/12, 2018 at 3:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.