InnoDB Row Storage: Half-Page Rule Explained
InnoDB Row Storage: Half-Page Rule Explained
- MySQL global row limit for a row: 65,535 bytes - MySQL throws error when
combined char , varchar goes beyond this limit. Text, blob , json columns do not
contribute to this limit.
- MySQL Innodb row limit: 2 rows per page , MySQL default page size: 16KB , so
each row max size 8KB. So, 32KB size will allow rows for 16KB size , however
that’s the cap at max rowsize. So, 64KB page will not allow 32KB row size, it will
be 16KB. Additionally , increase in page size has diminishing returns so it needs
to be tested.
- If a MySQL row goes beyond 8KB , InnoDB finds the largest column and stores
the data in off-page storage/overflow page leaving behind a 20 byte pointer
in-line in the leaf page.
- The row format used determines how much is placed in the overflow pages.
1. Compact/Redundant (legacy - filesystem antelope) - 767 bytes in leaf page
plus 20 bytes pointer - rest in overflow pages - one or multiple pages in a linked
list.
2. Dynamic/Compress (current - filesystem burracuda) - 20 bytes pointer - rest in
overflow page.
Blob,text & json columns with less than 40 bytes are stored inline in the leaf
page.
<Summary end>
The statement from the MySQL documentation—"When a row is too long, the longest
columns are chosen for off-page storage"—begs a critical question for any database
architect or administrator: precisely how much is "too long"? The answer is not a
single, static value across all MySQL instances but is instead governed by a
fundamental architectural principle of the InnoDB storage engine known as the
"half-page" rule. This rule dictates that the data for a single row stored locally within a
B-tree page cannot exceed a certain fraction of that page's size. Understanding this
threshold is paramount for effective schema design, performance tuning, and
preventing unexpected storage errors in production environments.
For the vast majority of MySQL installations, which use the default innodb_page_size
of 16KB, this translates to a maximum in-page row size of approximately 8000 bytes.1
More specifically, when this limit is breached, MySQL often reports
ERROR 1118 (42000): Row size too large (> 8126), explicitly citing a hard limit of 8126
bytes for the data that can be stored within the B-tree page itself.6 This value
represents the usable space on a 16KB page after accounting for various page-level
headers, trailers, and other metadata structures that are essential for InnoDB's
operation.
The half-page rule is not an arbitrary limitation but a deliberate design choice rooted
in the fundamental mechanics of B-tree data structures, which InnoDB uses for all its
indexes. The structural integrity and performance of a B-tree depend on its nodes
(pages) containing multiple keys, allowing for efficient traversal and searching. To
enforce this, InnoDB mandates that at least two records must be able to fit on any
single B-tree leaf page.8
The phrasing "slightly less than half a page" directly accounts for the non-data
overhead present on every InnoDB page. Each page contains a file header, page
header, file trailer, and other internal structures that consume a portion of the total
page size.5 The remaining space is what is available for row data, and this available
space is what is effectively divided by two to arrive at the maximum permissible size
for a single row's in-page content.
The specific byte value that defines "too long" is a direct function of the
innodb_page_size system variable. This configuration parameter determines the size
of the fundamental unit of I/O and storage for all InnoDB tablespaces within a MySQL
instance.1 While the default value has long been 16KB, which is considered a good
balance for a wide range of workloads 10, MySQL 8.0 also supports page sizes of 4KB,
8KB, 32KB, and 64KB.11
The following table provides a clear reference for how the innodb_page_size
configuration directly impacts the physical row size limit, as well as the related limit for
index key prefixes.
innodb_page_size Approximate Max Local Row Notes
Size
16KB (Default) ~8KB (e.g., 8126 bytes) The default setting. Maximum
index key prefix length is 3072
bytes for DYNAMIC row
format.3
This direct, causal relationship between the configured page size and the row size
limit underscores why a definitive answer to "how much is too long?" must always
begin with an examination of the specific innodb_page_size of the target MySQL
instance.
To accurately predict when a row will exceed the half-page limit, one must understand
how InnoDB calculates its in-page size. This is not as simple as summing the maximum
lengths of columns defined in the CREATE TABLE statement. The actual storage
consumed is a combination of the true length of the data being inserted and a
significant amount of structural metadata, or overhead, that is attached to every row.
This complexity is often the reason why a schema that appears valid on paper can fail
with a "Row size too large" error during a production INSERT or UPDATE operation.
The most intuitive component of a row's size is the data itself. However, the way this
data is stored varies significantly by data type and character set.
● Variable-Length Columns (VARCHAR, VARBINARY): These types are stored
efficiently, consuming only the space required by the actual data plus a small
prefix to record the length. The storage requirement is L+N bytes, where L is the
actual length of the string in bytes, and N is the number of bytes for the length
prefix. N is 1 if the column's maximum defined length is 255 bytes or less; N
becomes 2 if the maximum length can exceed 255 bytes.2
● TEXT/BLOB Types: When a row is small enough to fit entirely on the page, these
"large object" types contribute their full size to the in-page calculation. Similar to
VARCHAR, their storage is L+N bytes, where L is the data length. Here, N depends
on the specific type: 1 for TINYTEXT/TINYBLOB, 2 for TEXT/BLOB, 3 for
MEDIUMTEXT/MEDIUMBLOB, and 4 for LONGTEXT/LONGBLOB.13
● Fixed-Length Columns (CHAR, INT, DATE, etc.): These types traditionally
consume a fixed amount of space regardless of the value stored. However,
InnoDB's COMPACT and newer row formats optimize the storage of CHAR
columns when using variable-length character sets, effectively treating them like
VARCHAR columns in terms of storage.13
● The Critical Impact of Character Sets: A frequent source of underestimation in
row size calculation is the character set. A column defined as VARCHAR(255)
using the latin1 character set (one byte per character) will consume a maximum of
255+1=256 bytes. The same column defined with utf8mb4 (which can use up to
four bytes per character) can consume a maximum of (255×4)+2=1022 bytes.13
This four-fold difference can rapidly consume the available space on a page,
especially in wide tables with many text-based columns.
Beyond the data payload, every InnoDB row carries mandatory overhead that
contributes to its total in-page size. These "hidden costs" are often overlooked during
schema design but are critical to the final calculation.
● Record Header: Every single record in an InnoDB index contains a 5-byte header.
This header stores control information, including pointers that link consecutive
records on the page, which is essential for ordered scans and row-level locking
mechanisms.14
● System Columns: In a clustered index (the primary key), every record includes
two hidden system columns: a 6-byte transaction ID (TXID) field, which identifies
the transaction that last modified the row, and a 7-byte roll pointer field, which
points to the undo log entry for that modification. This amounts to a
non-negotiable 13 bytes of overhead on every row, essential for InnoDB's MVCC
(Multi-Version Concurrency Control) and transactional capabilities.14 Furthermore,
if a table is created without an explicit
PRIMARY KEY, InnoDB adds an additional 6-byte hidden row ID column to serve as
the clustered key.
● NULL Bitmap: For any columns in the index that are defined as NULL-able, the
record header contains a variable-length bit vector. Each bit in this vector
corresponds to a NULL-able column, indicating whether its value is NULL. The
size of this bitmap is CEILING(N/8) bytes, where N is the number of NULL-able
columns. For example, a table with 1 to 8 NULL-able columns adds 1 byte of
overhead, while a table with 9 to 16 NULL-able columns adds 2 bytes. Columns
that are actually NULL consume no further space beyond this single bit.14
● Variable-Length Pointers: In addition to the data itself, the record header must
also store the actual length of each non-NULL variable-length column. This
pointer costs 1 or 2 bytes for each such column, with 2 bytes being required if the
column's maximum length exceeds 255 bytes or if part of the column is stored
off-page.14
The cumulative effect of this overhead can be substantial. A wide table with many
NULL-able VARCHAR columns will accrue significant metadata costs before a single
byte of user data is even considered. This detailed accounting explains why a
developer, having calculated the sum of their column data types to be under the
8126-byte limit, might still encounter a "Row size too large" error. The failure arises
from not accounting for this complex and mandatory structural overhead. This reality
has a direct design implication: for very wide tables, choosing NOT NULL where
feasible and preferring fixed-length data types (when data patterns permit) can
tangibly reduce row overhead, potentially allowing more columns to fit before
triggering the off-page storage mechanism.
Section 3: The ROW_FORMAT Dichotomy: DYNAMIC vs.
COMPACT
Once InnoDB determines that a row is "too long" and must spill data off-page, the
specific strategy it employs is dictated by the table's ROW_FORMAT. The two most
relevant formats in this context are DYNAMIC and COMPACT. While they share the
same underlying trigger—the half-page rule—their methods for handling overflow
data are fundamentally different, with significant consequences for performance,
storage efficiency, and B-tree health.
In modern MySQL versions (including 8.0), DYNAMIC is the default and recommended
row format.16 Its design philosophy prioritizes the efficiency and density of the B-tree
index itself.
When a row using the DYNAMIC format exceeds the half-page limit, InnoDB moves
the entire content of the selected long variable-length columns (VARCHAR, BLOB,
TEXT) to a separate set of overflow pages.17 In place of the data within the B-tree leaf
page, it leaves only a
20-byte pointer.6 This compact pointer stores the metadata required to locate the
off-page data, including its true length and the address of the singly-linked list of
overflow pages where the content resides.5
The COMPACT row format, which was the default in older versions of MySQL, employs
a different strategy for overflow data.18 Instead of moving the entire column off-page,
COMPACT stores the first 768 bytes of a variable-length column's value directly
within the B-tree index record.14 Only the remainder of the data, if any, is pushed to
overflow pages. The in-page record then contains this 768-byte prefix plus a 20-byte
pointer to the rest of the data.5
This design can offer a performance benefit in a niche scenario: if a query only needs
to access a prefix of the large column (e.g., using LEFT(my_text_col, 500)), the data
can be retrieved directly from the B-tree page without incurring the additional I/O
operation required to fetch an overflow page.19
However, this potential advantage comes at a steep cost to overall index efficiency.
The 768-byte prefixes can significantly "bloat" the B-tree pages, filling them with
large chunks of data instead of key values. This drastically reduces the number of
rows that can fit on a page, leading to a deeper, less efficient B-tree structure. The
result is increased I/O for most queries (especially range scans), and a much larger
memory footprint in the buffer pool, as pages are filled with data that may not be
relevant to most queries.17 This is why, for the vast majority of modern workloads, the
The following table provides a concise comparison of the off-page storage behaviors
of these two row formats.
Off-Page Trigger Row's in-page size > ~half Row's in-page size > ~half
page limit page limit
In-Page Data Stored Entire column is moved First 768 bytes of column data
off-page remain in-page
Primary Use Case General purpose; ensures Niche cases where frequent,
lean, efficient B-tree indexes fast access to the first 768
for optimal overall bytes of large columns is
performance. critical and outweighs the
cost of reduced index
efficiency.
InnoDB's process for handling oversized rows is not a static configuration but a
dynamic, deterministic algorithm executed during data modification operations.
Understanding this step-by-step process is key to demystifying why certain rows in a
table might have columns stored off-page while others do not, and how InnoDB makes
these decisions in real-time.
For the row being inserted or updated, InnoDB performs a precise calculation of the
total in-page storage it would require if all data were stored locally. This calculation
includes the actual byte length of all data in variable-length columns, the size of all
fixed-length columns, and the full structural overhead as detailed in Section 2 (record
header, system columns, NULL bitmap, and variable-length pointers).
InnoDB compares the calculated size from the previous step against the maximum
in-page row size limit, which is dictated by the half-page rule for the instance's
configured innodb_page_size. For a default 16KB page, this check is against the ~8126
byte limit. If the calculated size is less than or equal to this limit, the entire row is
stored locally within the B-tree page, and the process concludes successfully.
If the row's calculated size exceeds the half-page limit, InnoDB initiates an iterative
process to reduce the row's in-page footprint until it fits.
● Selection: InnoDB first identifies all of the variable-length columns in the row
(VARCHAR, VARBINARY, TEXT, BLOB). From this set, it selects the single column
that is the longest based on its actual data length for that specific row.7 This
is a critical point: the decision is based on the current data, not the maximum
potential size defined in the schema. A
TEXT column containing "hello" is shorter than a VARCHAR(255) column
containing 200 bytes of data.
● Threshold Check: Before moving the selected column, InnoDB may apply a
minimum size threshold. For instance, columns smaller than 40 bytes are
generally not considered for off-page storage, as the overhead of creating an
overflow page (a 20-byte pointer plus the page itself) would not be efficient for
such a small amount of data.7
● Execution: The chosen column's data is moved to one or more external overflow
pages. The data within the B-tree record is then replaced. If the table's
ROW_FORMAT is DYNAMIC, the data is replaced with a 20-byte pointer. If the
format is COMPACT, it is replaced with the 768-byte prefix of the data plus a
20-byte pointer.
● Re-evaluation: After moving the column, InnoDB recalculates the row's new,
smaller in-page size.
● Iteration: If the recalculated size still exceeds the half-page limit, the loop
repeats. InnoDB again identifies the longest remaining variable-length column
and moves it off-page. This process continues, moving columns one by one in
descending order of their actual length, until the row's in-page size finally fits
within the limit.17
In some cases, even after moving all eligible variable-length columns off-page, the
row may still be too large. This can happen in tables with a very large number of
fixed-length columns, or in tables using ROW_FORMAT=COMPACT where the
cumulative size of the 768-byte prefixes and fixed-length columns still exceeds the
limit. If the row cannot be made to fit, the INSERT or UPDATE operation fails, and
InnoDB returns ERROR 1118 (42000): Row size too large to the client.6
This entire mechanism highlights that off-page storage is a dynamic, per-row, and
per-operation behavior. A single table can contain a mix of rows: some with all data
stored compactly in-page, and others with one or more columns stored on overflow
pages. The state of any given row depends entirely on the specific data it contained at
the time it was last written. This has direct implications for performance analysis, as
the I/O cost of retrieving a row can vary depending on whether its large columns were
pushed off-page.
At the highest level, the MySQL server imposes a logical maximum row size of 65,535
bytes for all storage engines.2 This limit is a protocol-level and file-format-level
constraint.
This check is performed during Data Definition Language (DDL) operations, primarily
CREATE TABLE and ALTER TABLE. The server calculates a theoretical maximum size
for a row based on the schema definition. It sums the maximum possible lengths of all
columns, including their length-byte overhead. For example, a VARCHAR(255) using a
single-byte character set contributes 256 bytes to this calculation.
The crucial behavior here concerns BLOB and TEXT column types. For the purpose of
this 65,535-byte check, the server does not count the full potential size of these
columns. Instead, it assumes their content will be stored separately from the main row
record and counts only a small contribution of 9 to 12 bytes for each BLOB or TEXT
column, representing the space needed for an internal pointer.2
This explains a common scenario: a CREATE TABLE statement with numerous large
VARCHAR columns (e.g., seven VARCHAR(10000) columns) will fail with the
65,535-byte error. However, if one of those columns is changed to a TEXT type, the
statement succeeds. This is because the server's calculation changes dramatically:
the TEXT column's contribution to the total drops from over 10,000 bytes to just 9-12
bytes, bringing the theoretical total below the 65,535-byte limit and allowing the table
to be created.2
5.2. The InnoDB Page Limit: The ~8KB Physical Reality Check
While a table schema might successfully pass the server's 65,535-byte logical check,
it must still contend with the physical storage constraints of the InnoDB engine. This is
where the half-page rule (~8126 bytes for a 16KB page) comes into play.
This limit is not enforced at CREATE TABLE time but during Data Manipulation
Language (DML) operations like INSERT and UPDATE.5 It governs the physical reality
of how much data can be laid out
within a single B-tree leaf page. It is a hard constraint imposed by the storage
engine's internal data structures.
A table can easily be defined with a schema that logically allows for rows far larger
than 8KB (e.g., a table with several TEXT columns). This table will be created without
issue. However, when an application attempts to INSERT a row into this table, InnoDB
performs the physical check. If the actual data for the fixed-length columns,
VARCHAR columns, and any other in-page content exceeds the half-page limit,
InnoDB will trigger the off-page storage mechanism described in Section 4. If, even
after that process, the remaining in-page portion of the row is still too large, the DML
operation will fail with the > 8126 error.
The interaction between these two limits can be understood as a two-stage validation
system, reflecting the separation of concerns between the MySQL server layer and
the InnoDB storage engine layer.
● Stage 1 (Logical Schema Validation): This occurs during CREATE TABLE or
ALTER TABLE. The MySQL server acts as a gatekeeper, enforcing the 65,535-byte
rule. It performs a theoretical calculation based on the schema's maximum
potential size, giving TEXT/BLOB types a "pass" by counting them as small
pointers. A schema must pass this gate to even exist.
● Stage 2 (Physical Layout Validation): This occurs during INSERT or UPDATE.
InnoDB, the storage engine, acts as the physical architect. It does not care about
the theoretical maximums from the schema; it cares only about the actual bytes
of data for the specific row it needs to place on a page right now. It enforces the
half-page rule, triggering off-page storage or failing the operation if the physical
layout is impossible.
A database administrator must therefore design schemas that can pass both gates.
The schema must be logically valid according to the server's rules, and the expected
data patterns must not consistently produce rows that violate InnoDB's physical page
limits after the off-page mechanism has been applied. This two-gate system is the key
to understanding why a syntactically valid table can be created successfully but still
fail during routine operation in a production environment.
● A row is deemed "too long" by InnoDB when the data intended for local storage
within a B-tree page exceeds the "half-page" limit. For a standard MySQL
instance with a 16KB innodb_page_size, this physical limit is approximately 8KB
(specifically, 8126 bytes).
● This limit is not arbitrary but is a direct consequence of InnoDB's B-tree
architecture, which mandates that at least two records must fit on a page to
ensure structural integrity and prevent performance degradation.
● The ROW_FORMAT=DYNAMIC is the modern, efficient default in MySQL 8.0. When
a row is too long, it moves entire variable-length columns to external overflow
pages, leaving only a compact 20-byte pointer in the B-tree page. This keeps
indexes lean and cache-friendly.
● The ROW_FORMAT=COMPACT is a legacy approach that keeps a 768-byte prefix
of overflowed columns in the B-tree page. While this can benefit niche
prefix-based queries, it generally leads to index bloat and reduced overall
performance.
● InnoDB uses a deterministic, iterative process to handle oversized rows: it
identifies the longest variable-length column based on its actual data size and
moves it off-page, repeating this process until the row fits within the half-page
limit.
● Administrators must design for two distinct limits: the MySQL server's
65,535-byte logical limit enforced at CREATE TABLE time, and InnoDB's ~8KB
physical limit enforced at INSERT/UPDATE time.
Based on this analysis, several best practices emerge for effective schema design:
● Embrace ROW_FORMAT=DYNAMIC: For all new tables created on MySQL 5.7
and later, DYNAMIC should be the explicit or default choice. It provides the most
efficient, predictable, and performant behavior for nearly all workloads, especially
those involving tables with potentially large or numerous variable-length
columns.16
● Strategic Use of TEXT/BLOB: For columns that are expected to hold large
amounts of data (e.g., user-generated content, JSON documents, long
descriptions), proactively define them as TEXT or BLOB types from the outset.
This correctly signals intent to the MySQL server, helping the schema pass the
65,535-byte logical check at creation time. It also ensures these columns are the
primary candidates for off-page storage by InnoDB, keeping the core row record
small.2
● Mind Your Character Sets: Be acutely aware of the storage implications of
multi-byte character sets. When designing tables that will store international text,
utf8mb4 is necessary, but its potential 4-byte-per-character storage cost must
be factored into all row size calculations to avoid surprises.
● Consider Normalization or Alternative Structures: If a single table grows
excessively wide with dozens of large, optional VARCHAR columns, it may be an
indicator of a design flaw. In such cases, consider normalizing the schema by
splitting the data into a primary table and one or more related secondary tables.
Alternatively, for unstructured or semi-structured data, consolidating many
sparse columns into a single JSON column (stored in a TEXT or BLOB type) can be
a more manageable and scalable approach.7
While it is technically possible to increase the physical row size limit by increasing the
innodb_page_size to 32KB (yielding a ~16KB row limit), this should be considered a
measure of last resort.
● Architectural Impact: Changing the page size is a major architectural decision
that affects the entire MySQL instance, not just one table. It has complex
performance trade-offs; smaller page sizes can be more efficient for SSD storage
and OLTP workloads with many small, random I/O operations, as less data needs
to be read from and written to disk for point updates.11
● Operational Cost: The process is operationally expensive and requires
significant downtime. It necessitates a full logical backup of all data, a complete
re-initialization of the InnoDB data directory, and a full data restore.10
Question : Since there should be two rows in a mysql page , can a row be more than 8
KB and the next row is small enough to meet the 2 rows per page rule , considering the
Page size is 16KB?
That's an excellent question, and it gets to the very core of how InnoDB's storage works.
The answer is no, a single row cannot store more than ~8KB of data on the page, even if the
next row is tiny.
Here’s the crucial distinction: The "two rows per page" rule is the cause of the ~8KB limit, not a
flexible guideline.
1. It's a Per-Row Limit, Not a Page Average: To guarantee that at least two rows can
always fit on a page (which is necessary for the B-Tree page-splitting algorithm to work),
InnoDB enforces a strict maximum size for the data from any single row that can be
stored on the page itself. This limit is approximately 8126 bytes (slightly less than half a
16KB page, accounting for page headers/trailers).
2. The Overflow Mechanism is a Result: This limit is what triggers the overflow
mechanism you mentioned.
○ Your Scenario: You try to insert a row whose total data is 10KB.
○ What InnoDB Does: InnoDB will not try to store all 10KB on the main page. It
sees that the data exceeds the ~8KB on-page limit. Before writing the row, it
automatically:
1. Takes the largest variable-length columns (like TEXT, BLOB, or large
VARCHARs).
2. Moves their data to separate "overflow pages".
3. Leaves only a 20-byte pointer on the main page for each column it
moved.
The Result:
Your "10KB row" doesn't actually exist on the main page as a 10KB block. Its on-page footprint
might only be 500 bytes (for its smaller columns) plus a 20-byte pointer.
Therefore, you will never have this situation:
● Main Page (16KB): [Row A (On-page data + 20-byte pointer) - Total: 520 bytes] [Row B
(1KB)]
● Overflow Page(s): [The rest of Row A's data (9.5KB)]
So, the ~8KB limit is a hard ceiling for on-page data for any single row, precisely to ensure that
the "two rows per page" rule is always satisfied.
Works cited
1. MySQL 8.4 Reference Manual :: 17.21 InnoDB Limits, accessed July 17, 2025,
[Link]
2. 10.4.7 Limits on Table Column Count and Row Size - MySQL :: Developer Zone,
accessed July 17, 2025,
[Link]
3. 17.22 InnoDB Limits, accessed July 17, 2025,
[Link]
4. 14.23 InnoDB Limits, accessed July 17, 2025,
[Link]
5. Innodb row size limitation - Percona, accessed July 17, 2025,
[Link]
6. Tips for Custom Fields and InnoDB Row Size Limit - Clay's Blog - CalemEAM,
accessed July 17, 2025,
[Link]
db-row-size-limit
7. Mysql Innodb Row/Page Size Limits - Database Administrators Stack Exchange,
accessed July 17, 2025,
[Link]
imits
8. In what cases are BLOB and TEXT stored in-line on InnoDB?, accessed July 17,
2025,
[Link]
ext-stored-in-line-on-innodb
9. why innodb index length limit to 3072 when page_size is 16k? - Stack Overflow,
accessed July 17, 2025,
[Link]
-3072-when-page-size-is-16k
10.MySql 5.6: Ignores innodb_page_size setting in [Link] - Database Administrators
Stack Exchange, accessed July 17, 2025,
[Link]
e-size-setting-in-my-ini
11. MySQL 8 InnoDB 32KB and 64KB page sizes benefits for HDD - Stack Overflow,
accessed July 17, 2025,
[Link]
page-sizes-benefits-for-hdd
12.Innodb page size setting - mysql - Stack Overflow, accessed July 17, 2025,
[Link]
13.MySQL 8.4 Reference Manual :: 13.7 Data Type Storage Requirements, accessed
July 17, 2025,
[Link]
14.MySQL 8.4 Reference Manual :: 17.10 InnoDB Row Formats, accessed July 17,
2025, [Link]
15.12.5 Limits on Table Column Count and Row Size - MySQL :: Developer Zone,
accessed July 17, 2025,
[Link]
ml
16.mysql - Is COMPACT a better format for fixed length rows than DYNAMIC?,
accessed July 17, 2025,
[Link]
or-fixed-length-rows-than-dynamic
17.MySQL 8.4 Reference Manual :: 17.10 InnoDB Row Formats - MySQL, accessed
July 17, 2025, [Link]
18.Anyone running their InnoDB tables with ROW_FORMAT=COMPRESSED? -
XenForo, accessed July 17, 2025,
[Link]
h-row_format-compressed.99606/
19.In which case would MySQL's InnoDB's COMPACT row_format would be
faster/better than REDUNDANT? - Stack Overflow, accessed July 17, 2025,
[Link]
dbs-compact-row-format-would-be-faster-better-t
20.InnoDB file formats: Here is one pitfall to avoid - Percona, accessed July 17, 2025,
[Link]