Skip to content

Commit e0fac9b

Browse files
committed
publish 2024-07-23-mysql-upgrading-old-mysql-instances.md
1 parent 353f4fc commit e0fac9b

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
author: isotopp
3+
title: "MySQL: Upgrading old MySQL instances"
4+
description: |
5+
Converting a large database to a newer version of MySQL can be done in place,
6+
using binary database files.
7+
Or it can be done by dumping the database and loading the dump into a newer version.
8+
What are the considerations?
9+
date: "2024-07-23T05:06:07Z"
10+
feature-img: assets/img/background/mysql.jpg
11+
toc: true
12+
tags:
13+
- lang_en
14+
- database
15+
- mysql
16+
---
17+
18+
> Dear Kris,
19+
>
20+
> We have a number of old MySQL instances, version 5.5 and 5.6.
21+
> We want to upgrade them to a current version of MySQL.
22+
> The databases are between 0.5 TB to 8 TB in size.
23+
>
24+
> Unfortunately, using rsync on a stopped MySQL instance is not an option because the versions are too different,
25+
> and the new version seems unable to read the binary data.
26+
> I could use a dump (multi-threaded), but importing a single file with mysql takes forever
27+
> (it would need to be single-threaded).
28+
>
29+
> Would you simply create a dump per table and then import them in parallel?
30+
> Or has MySQL improved, allowing for better import methods now?
31+
32+
That is a lot to unpack.
33+
34+
# A consistent backup and a binlog position
35+
36+
First off, a backup needs to be consistent.
37+
You cannot make a backup of individual tables, one after the other, and expect the target database to be consistent.
38+
A backup is a backup of all tables referencing each other, at a known binlog position.
39+
Only then a clean cut-over can be done.
40+
41+
We are discussing the why and how in
42+
[Backups and Replication]({{< relref "2020-11-27-backups-and-replication.md" >}}).
43+
44+
We are discussing the proper `mysqldump` options in
45+
[Ways to run mysqldump]({{< relref "2023-01-03-mysql-ways-to-run-mysqldump.md" >}}).
46+
47+
There are now other, better ways to dump the database, using `mysqlsh`.
48+
This also provides facilities for parallel import.
49+
See
50+
[Parallel Table Import Utility](https://dev.mysql.com/doc/mysql-shell/8.4/en/mysql-shell-utilities-parallel-table.html).
51+
I have never tested that at scale.
52+
According to the documentation, you need to be at MySQL 5.7 or later for the data source for this to work:
53+
[Schema Dump Utility: Requirements and Restrictions](https://dev.mysql.com/doc/mysql-shell/8.4/en/mysql-shell-utilities-dump-instance-schema.html#mysql-shell-utilities-dump-opt-requirements).
54+
55+
That makes it a non-option for the original ask, and we are limited to traditional options for the task.
56+
57+
# Upgrading in place
58+
59+
MySQL's data files have a defined format that is independent of the various operating system facilities,
60+
and from the way the hardware represents certain data types.
61+
It will not change inside one stable major version (except MySQL 8.0,
62+
and that turned out a major source of downtime and problems for a deployment I was in charge of).
63+
64+
An upgrade of libc that changes collations will not affect MySQL indexes,
65+
because MySQL does not use operating system collations.
66+
It defines its own – they are faster, stable across OS vendors, libc upgrades and other changes, and they are immutable.
67+
A collation, even if faulty, will never change.
68+
To fix bugs, a new collation with a new name will be created.
69+
This has not always been the case, and that is a long and sad story.
70+
Read about it in
71+
[UTF8MB4]({{< relref "2022-01-12-utf8mb4.md" >}}).
72+
73+
MySQL does support upgrading binary data files,
74+
but does so only one version step at a time from one major stable version to the next.
75+
76+
That is, you can safely go from
77+
- 5.5 to 5.6
78+
- 5.6 to 5.7
79+
- 5.7 to 8.0
80+
- 8.0 to 8.4
81+
82+
and specifically from the latest version of the OLD major version to the latest version of the new major version.
83+
To go from 5.5 to 8.4, first upgrade to the latest 5.5 build.
84+
Then go to 5.6, 5.7, 8.0 and 8.4 using the respective latest versions of each.
85+
Use the version-specific upgrade process as documented in the manual,
86+
and test each upgrade before proceeding to the next.
87+
88+
MySQL 8.1, 8.2 and 8.3 have been innovation releases and are not "major stable versions".
89+
Don't use them.
90+
91+
Upgrading in place changes the on-disk format of the database,
92+
the valid syntax of some queries you may use, and the behavior of some data types.
93+
It is a tricky thing and usually requires more than one attempt to get right.
94+
95+
Thus, it is very much recommended you create a new instance from a valid backup,
96+
and make this instance a replica of the primary, old server.
97+
98+
You then stop the replica, perform the upgrade as outlined in the relevant manual
99+
(and the procedure's details change from version to version,
100+
because each major version does have other special concerns),
101+
and then try to restart the replica.
102+
103+
If that works, divert a bit of read-load to the replica to see if your queries are still valid SQL under the new version.
104+
If you are confident everything works, you can point the writes from the primary to the replica and be done with it.
105+
106+
Continue with the next version until done.
107+
This sounds tedious, and it is.
108+
Especially if you have put off upgrading.
109+
Don't do that, upgrade early (at least one replica), and it will hurt a lot less.
110+
111+
# Reading a dump
112+
113+
On disk, a database is a collection of files that represent the actual data in the database,
114+
plus additional structures for quick access, the indexes.
115+
A dump will create a representation of the data in the database in SQL syntax.
116+
The index definitions are also generated, but the actual indexes are not part of the dump.
117+
118+
On reading the dump, the new database instance has to read and parse the SQL.
119+
While this is not as fast as loading binary data, it is usually not a bottleneck.
120+
The MySQL SQL parser is quite fast.
121+
122+
What takes time is the recreation of the indexes after reading a table.
123+
To do that, the database will have to extract the indexed columns from the table by reading through the table,
124+
and sort them.
125+
It will then write out the index as tuples
126+
that contain the indexed columns and a copy of the primary key as a pointer to the full row,
127+
in sorted order.
128+
129+
Sorting the data in index order is what takes time and potentially a lot of memory.
130+
If you do not have enough memory, sorting is done in "runs", with intermediate results on disk,
131+
and a merge sort to build the final version of the index.
132+
The full process is documented in
133+
[Sorted Index Builds](https://dev.mysql.com/doc/refman/8.4/en/sorted-index-builds.html).
134+
135+
Run size is controlled by the variable
136+
[innodb_sort_buffer_size](https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html#sysvar_innodb_sort_buffer_size).
137+
Note that this is different from
138+
[sort_buffer_size](https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_sort_buffer_size)
139+
(a variable used in ORDER BY optimization, but not in InnoDB bulk index builds).
140+
141+
# TL;DR
142+
143+
- If your MySQL instance does not have at least one replica more than you are going to need, you are holding it wrong.
144+
- Upgrade MySQL from one major version to the next one, using in-place upgrades – on a replica.
145+
- When done, behead the replication tree. That is, point the writes from the old version primary to the new database,
146+
promoting the replica to the new primary.
147+
- Make sure to build new replicas using the new version as needed, in time.
148+
- If you must upgrade using a dump, plan this.
149+
- Dumps are slow because they rebuild indexes.
150+
- Try this out and time it, optimize it by playing with `innodb_sort_buffer_size`.

0 commit comments

Comments
 (0)