Assuming InnoDB 8.0 or later, which is the currently supported default storage engine.
Creating a new record version does not necessarily create a new copy of a LOB.
Refer to this worklog for MySQL 8.0: https://dev.mysql.com/worklog/task/?id=8960
After this worklog, many versions of clustered index record can point to
the same LOB.
I experimented to see if updating a record with a large JSON LOB used more space.
I created a table with a 1MB JSON LOB.
CREATE TABLE mytable (
id SERIAL,
counter INT DEFAULT 0,
data JSON
);
INSERT INTO mytable (data) VALUES (CONCAT('"', REPEAT('A', 1024*1024), '"'));
I used Jeremy Cole's innodb_ruby tool to inspect the tablespace for a test table.
innodb_space -f mytable.ibd space-page-type-regions
start end count type
0 0 1 FSP_HDR
1 1 1 IBUF_BITMAP
2 2 1 INODE
3 3 1 SDI
4 4 1 INDEX
5 5 1 LOB_FIRST
6 15 10 LOB_DATA
16 16 1 LOB_INDEX
17 36 20 LOB_DATA
37 63 27 FREE (ALLOCATED)
64 97 34 LOB_DATA
98 127 30 FREE (ALLOCATED)
I updated the counter column in mytable repeatedly, but the number of pages allocated for LOB_DATA did not change.
mysql> update mytable set counter = counter + 1;
...repeat many times...
But as soon as I changed the JSON LOB:
mysql> update mytable set data = concat('"', repeat('B', 1024*1024), '"');
Then new space was allocated for LOB data, and the original LOB data pages were now free.
innodb_space -f mytable.ibd space-page-type-regions
start end count type
0 0 1 FSP_HDR
1 1 1 IBUF_BITMAP
2 2 1 INODE
3 3 1 SDI
4 4 1 INDEX
5 5 1 FREE (LOB_FIRST)
6 15 10 FREE (LOB_DATA)
16 16 1 FREE (LOB_INDEX)
17 36 20 FREE (LOB_DATA)
37 63 27 FREE (ALLOCATED)
64 97 34 FREE (LOB_DATA)
98 98 1 LOB_FIRST
99 108 10 LOB_DATA
109 109 1 LOB_INDEX
110 163 54 LOB_DATA
164 383 220 FREE (ALLOCATED)
This shows that LOB pages are not copied if you update some other column in the row, but they are copied (and old row versions are delete-marked) when you update the LOB.
show create table yourtablenamedata_jsonin a separate table.