Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

MySQL & MariaDB in Depth  Module 1 – Start here: versions, a lab server and configuration

MySQL and MariaDB today: versions and support

MySQL's LTS and Innovation tracks, calendar versioning from 26.7, when each line's support ends, and the licences MySQL and MariaDB ship under.

  • Beginner
  • 15 minutes
  • Examples run with MySQL 8.4.11 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Explain MySQL's LTS and Innovation release tracks and the move to calendar versioning
  • Choose a version line for a new project and say when its support ends
  • Compare MariaDB's yearly LTS and quarterly rolling releases with MySQL's tracks
  • Name the main MySQL and MariaDB distributions, forks and the licences they ship under
What you’ll learn

What you will learn

  • Explain MySQL's LTS and Innovation release tracks and the move to calendar versioning
  • Choose a version line for a new project and say when its support ends
  • Compare MariaDB's yearly LTS and quarterly rolling releases with MySQL's tracks
  • Name the main MySQL and MariaDB distributions, forks and the licences they ship under
On this page

A new service has to pick a MySQL version before it writes a single row, and that choice sets how long the server keeps getting security fixes and how often the team must plan a major upgrade. MySQL ships on two tracks at once: one that receives only fixes for up to eight years, and one that adds features and may change behaviour every few months. Since version 26.7 the numbers no longer count upwards the way they used to, and MariaDB, a separate server that began as a copy of MySQL, runs its own schedule again.

You will read what a running server reports about itself, tell a long-term line from a feature line, work out when a line’s support ends, follow the supported upgrade path between lines, and name the main distributions and their licences, so you can choose a line on purpose rather than by habit.

Two release tracks: fixes only, or the latest features

Ask a running server what it is, and it answers with a number and an edition, not a track. Here a throwaway MySQL 8.4.11 server reports its build with mysqld --version and then from inside with SELECT VERSION() and a few compile-time variables:

What the running MySQL 8.4.11 server reports about itself

version_session.py

"""What the running MySQL 8.4.11 server says about itself.

A throwaway server is started, then `mysqld --version` prints the build line and version.sql reads the same facts
from inside the server: the release number, the edition in @@version_comment, the platform the build was compiled
for, and the InnoDB version. None of these names the release track (LTS or Innovation): that is a property of the
release, not a value the server stores.
"""

from mysql_session import Server

with Server() as my:
    my.up()
    my.shell("mysqld", "--version", shown=["mysqld", "--version"])
    my.mysql("version.sql")

Output

$ mysqld --version
mysqld  Ver 8.4.11 for macos15 on arm64 (MySQL Community Server - GPL)

$ mysql -t -vvv --skip-comments < version.sql
--------------
SELECT VERSION() AS version, @@version_comment AS edition
--------------

+---------+------------------------------+
| version | edition                      |
+---------+------------------------------+
| 8.4.11  | MySQL Community Server - GPL |
+---------+------------------------------+
1 row in set

--------------
SELECT @@version_compile_os AS compiled_os,
       @@version_compile_machine AS machine,
       @@innodb_version AS innodb
--------------

+-------------+---------+--------+
| compiled_os | machine | innodb |
+-------------+---------+--------+
| macos15     | arm64   | 8.4.11 |
+-------------+---------+--------+
1 row in set

Bye

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 version_session.py

version.sql

-- The release number and the edition the build reports.
SELECT VERSION() AS version, @@version_comment AS edition;

-- The platform the build was compiled for, and the InnoDB version inside it.
SELECT @@version_compile_os AS compiled_os,
       @@version_compile_machine AS machine,
       @@innodb_version AS innodb;

mysql_session.py

"""Throwaway MySQL 8.4.11 servers for the examples of this lesson.

Every session creates a server of its own: `mysqld --initialize-insecure` makes a new data directory inside a new
folder in the examples folder, the server starts on it, the session runs `mysql` client scripts and MySQL's programs
against it and prints what they printed, and then the server is stopped and the folder deleted, also when something
fails. The server is the pinned MySQL 8.4.11 from the mise installations next to the Python that runs this file;
Server(basedir=...) runs the programs of another unpacked MySQL tarball instead (dump_defaults.py uses it).

Safety: the server listens only on 127.0.0.1, an address no other computer can reach, on a port the operating system
reports as unused; the X Protocol listener is off (--mysqlx=OFF); its Unix socket, temporary files, error log and
import/export folder all stay inside the run folder. Right after the first start the session gives root a password made
up for the run (never printed) and connects only with a client option file that holds it, so no password ever appears
on a command line. Every program starts with --no-defaults or --defaults-file, so no option file of the computer
(/etc/my.cnf, ~/.my.cnf) can change a result, and HOME points at the run folder, so ~/.mylogin.cnf is not read either.
A printed mysqld command shows the options that matter to the lesson; the session also passes, on the command line,
the folders it keeps inside the run folder (--basedir, --tmpdir, --log-error, --pid-file, --secure-file-priv) and
the port, which is why performance_schema.variables_info reports those variables as COMMAND_LINE.
learn:verify's run sandbox cannot read the home folder, where mise keeps MySQL, so these sessions are recorded with
LEARN_VERIFY_NETWORK=allow; they read only this folder and the toolchain and connect to nothing but 127.0.0.1.

The output is exactly what the programs printed, with these substitutions so that a record never shows the computer it
ran on and is the same on every run: the run folder is shown as ".", the folder of MySQL's programs is left out of
their paths and MySQL's own folder is shown as <basedir>, the computer's name is shown as "lab-host" and the operating-system user's name as "learner", the time a
statement took (the client's "(0.01 sec)") is left out, the time stamp SET PERSIST writes into mysqld-auto.cnf is
shown as <when it was set>, error-log lines lose their timestamp and thread number, and a session can name more values
that change between runs with my.mask(pattern, replacement), which then prints in their place. A client table whose cells
change is drawn again so that its borders still line up.
The sessions never print the password, and the run's port prints as <port>. A session stops with status 1 when a command that should have worked
fails, or when a script reports a different number of errors than the session expects, so a run that went wrong is
never recorded as if it had worked.
"""

import os
import pwd
import re
import secrets
import shlex
import shutil
import socket
import subprocess
import sys
import tempfile
import textwrap
import time
from pathlib import Path

MYSQL_VERSION = "8.4.11"
# .../installs/python/3.14.8/bin/python3 -> .../installs
INSTALLS = Path(sys.executable).resolve().parents[3]
PINNED = INSTALLS / "mysql" / MYSQL_VERSION
OS_USER = pwd.getpwuid(os.geteuid()).pw_name
HOST = socket.gethostname().split(".")[0]
# 2026-10-17T16:50:01.123456Z 0 [ERROR] ... -> [ERROR] ...
LOG_PREFIX = re.compile(r"^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d+Z \d+ ", re.M)


def free_port():
    """A TCP port on 127.0.0.1 that nothing listens on now."""
    with socket.socket() as probe:
        probe.bind(("127.0.0.1", 0))
        return probe.getsockname()[1]


class Server:
    """One throwaway server: initialise, start; stopped and deleted when the with block ends.

    options: extra mysqld options for every start (for example "--sql-mode=" or "--character-set-server=latin1").
    basedir: the folder of an unpacked MySQL tarball to run instead of the pinned 8.4.11.
    masks: (pattern, replacement) pairs, as for mask().
    """

    def __init__(self, options=(), redo=None, basedir=None, masks=()):
        self.options = [str(o) for o in options]
        self.redo = redo
        self.basedir = Path(basedir).resolve() if basedir else PINNED
        self.bin = self.basedir / "bin"
        self.masks = []
        for pattern, replacement in masks:
            self.mask(pattern, replacement)
        self.proc = None

    def mask(self, pattern, replacement):
        """From now on every printed line shows text matching the regular expression as the replacement: for a value
        that changes from run to run (an uptime, a time stamp), so that the recorded output stays the same."""
        self.masks.append((re.compile(pattern), replacement))

    # ---- the folder ------------------------------------------------------------------------------------------------
    def __enter__(self):
        if not (self.bin / "mysqld").exists():
            sys.exit(f"no MySQL server program in {self.basedir}" if self.basedir != PINNED
                     else f"MySQL {MYSQL_VERSION} was not found next to this Python")
        self.base = Path(tempfile.mkdtemp(prefix="my", dir=".")).resolve()
        for sub in ("tmp", "files"):
            (self.base / sub).mkdir(mode=0o700)
        self.port = free_port()
        self.password = secrets.token_urlsafe(24)
        self.client_cnf = self.base / "client.cnf"
        self._write_client_cnf(password=None)
        self.env = {key: value for key, value in os.environ.items()
                    if not key.startswith("MYSQL") and key not in ("TZ", "HOME", "LANG", "LC_ALL")}
        self.env.update({"HOME": str(self.base), "TZ": "UTC", "LANG": "en_US.UTF-8",
                         "MYSQL_TEST_LOGIN_FILE": str(self.base / "no-login-paths.cnf")})
        for pattern in ("*.sql", "*.cnf", "*.csv", "*.txt"):
            for f in Path(".").glob(pattern):
                shutil.copy(f, self.base / f.name)
        return self

    def __exit__(self, *exc):
        if self.proc and self.proc.poll() is None:
            self.stop(quiet=True)
        shutil.rmtree(self.base, ignore_errors=True)

    def _write_client_cnf(self, password):
        lines = ["[client]", "user=root", "host=127.0.0.1", f"port={self.port}", "protocol=TCP"]
        if password:
            lines.append(f"password={password}")
        self.client_cnf.write_text("\n".join(lines) + "\n")
        self.client_cnf.chmod(0o600)

    def server_options(self):
        b = self.base
        return ["--no-defaults", f"--basedir={self.basedir}", f"--datadir={b / 'data'}", f"--tmpdir={b / 'tmp'}",
                f"--log-error={b / 'mysqld.err'}", f"--pid-file={b / 'mysqld.pid'}",
                f"--secure-file-priv={b / 'files'}",
                *([f"--innodb-redo-log-capacity={self.redo}"] if self.redo else [])]

    # ---- the server ------------------------------------------------------------------------------------------------
    def initialize(self, show=False):
        """mysqld --initialize-insecure: a new data directory whose root account has no password yet."""
        args = ["mysqld", *self.server_options(), "--initialize-insecure"]
        code, out = self._call(args)
        if show:
            _show(["mysqld", "--no-defaults", "--initialize-insecure", "--datadir=./data"], out)
        if code != 0:
            print(self.log())
            sys.exit(f"mysqld --initialize-insecure ended with status {code}")

    def start(self, extra=(), defaults_file=None, expect_fail=False, show=False):
        """Starts mysqld on 127.0.0.1 and waits until it answers. With expect_fail, prints the error-log lines of a
        start that must fail, and returns them."""
        log = self.base / "mysqld.err"
        before = log.read_text() if log.exists() else ""
        opts = self.server_options()
        if defaults_file:
            opts[0] = f"--defaults-file={self.base / defaults_file}"
        args = [str(self.bin / "mysqld"), *opts, "--bind-address=127.0.0.1", f"--port={self.port}",
                "--socket=mysql.sock", "--mysqlx=OFF", *self.options, *[str(e) for e in extra]]
        if show:
            shown = ["mysqld", opts[0].replace(str(self.base) + "/", ""), "--datadir=./data", "--bind-address=127.0.0.1",
                     "--port=$PORT", "--socket=mysql.sock", "--mysqlx=OFF", *self.options, *extra]
            print("$ " + " ".join(_arg(a) if a != "--port=$PORT" else a for a in shown) + " &")
            print()
        with open(self.base / "mysqld.out", "a") as out:
            self.proc = subprocess.Popen(args, stdout=out, stderr=subprocess.STDOUT, cwd=self.base, env=self.env)
        for _ in range(600):
            if self.proc.poll() is not None:
                break
            if self._ping():
                break
            time.sleep(0.1)
        if self.proc.poll() is not None:
            new = (log.read_text() if log.exists() else "")[len(before):]
            lines = [l for l in self.clean(LOG_PREFIX.sub("", new)).splitlines() if "[ERROR]" in l]
            if expect_fail:
                print("\n".join(lines))
                print(f"(mysqld ended with status {self.proc.returncode})")
                print()
                self.proc = None
                return lines
            print("\n".join(lines))
            sys.exit(f"mysqld ended with status {self.proc.returncode} while starting")
        if expect_fail:
            sys.exit("mysqld started, but the session expected it to fail")
        if not self._ping():
            sys.exit("mysqld did not answer within 60 seconds")
        return []

    def _ping(self):
        code, _ = self._call(["mysqladmin", f"--defaults-file={self.client_cnf}", "--connect-timeout=2", "ping"])
        return code == 0

    def up(self, extra=(), defaults_file=None, show=False):
        """Initialise, start (with start()'s options), and give root the run's password, quietly."""
        self.initialize()
        self.start(extra=extra, defaults_file=defaults_file, show=show)
        return self.login()

    def login(self):
        """Gives root the run's password (never printed) on a server started from a new data directory, and makes
        every later client of the session use it."""
        self.silent(f"ALTER USER 'root'@'localhost' IDENTIFIED BY '{self.password}'")
        self._write_client_cnf(self.password)
        return self

    def stop(self, quiet=False):
        """A clean shutdown (mysqladmin shutdown), then waits until the process has ended."""
        code, out = self._call(["mysqladmin", f"--defaults-file={self.client_cnf}", "shutdown"])
        if code != 0 and not quiet:
            sys.exit(f"mysqladmin shutdown failed: {out}")
        try:
            self.proc.wait(timeout=120)
        except subprocess.TimeoutExpired:
            self.proc.terminate()
            self.proc.wait(timeout=60)
        self.proc = None

    def restart(self, extra=(), defaults_file=None, show=False):
        self.stop()
        self.start(extra=extra, defaults_file=defaults_file, show=show)

    # ---- running things --------------------------------------------------------------------------------------------
    def _call(self, args, stdin=None, merge=True):
        """Runs one of MySQL's programs in the run folder: (status, cleaned output). merge=False keeps only what it
        wrote to standard output (its warnings on standard error are dropped)."""
        args = [str(a) for a in args]
        done = subprocess.run([str(self.bin / args[0]), *args[1:]], stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT if merge else subprocess.DEVNULL,
                              text=True, cwd=self.base, env=self.env, input=stdin)
        return done.returncode, self.clean(done.stdout)

    def output(self, *args, connect=False, status=0):
        """Runs one of MySQL's programs without printing anything and returns its standard output, for example the
        SQL that mysql_tzinfo_to_sql writes, which the session saves with write_file(..., show=False) and runs next."""
        args = [str(a) for a in args]
        run = [args[0], f"--defaults-file={self.client_cnf}", *args[1:]] if connect else args
        code, out = self._call(run, merge=False)
        if code != status:
            sys.exit(f"{args[0]} ended with status {code}, not {status}")
        return out

    def clean(self, text):
        """The substitutions of the docstring. A table of the client whose cells change is drawn again with the
        new column widths, so its borders still line up."""
        lines, out, i = text.split("\n"), [], 0
        while i < len(lines):
            if lines[i].startswith("+-"):
                j = i
                while j < len(lines) and lines[j][:1] in ("+", "|"):
                    j += 1
                block = lines[i:j]
                done = [self._clean(l) for l in block]
                out.extend(done if done == block else _redraw(block, self._clean))
                i = j
            else:
                out.append(self._clean(lines[i]))
                i += 1
        return "\n".join(out)

    def _clean(self, text):
        text = text.replace(str(self.bin) + "/", "").replace(str(self.basedir), "<basedir>")
        text = text.replace(str(self.base) + "/", "./").replace(str(self.base), ".")
        text = re.sub(r" \(\d+\.\d\d sec\)", "", text)
        text = re.sub(r'"Timestamp": \d+', '"Timestamp": <when it was set>', text)
        text = re.sub(rf"\b{re.escape(HOST)}\b", "lab-host", text)
        text = re.sub(rf"\b{re.escape(OS_USER)}\b", "learner", text)
        text = re.sub(rf"(?<![\d.]){self.port}(?![\d.])", "<port>", text)
        for pattern, replacement in self.masks:
            text = pattern.sub(replacement, text)
        return text

    def log(self, pattern=None):
        """The error log without timestamps and thread numbers, optionally only the lines matching a pattern."""
        text = self.clean(LOG_PREFIX.sub("", (self.base / "mysqld.err").read_text()))
        if pattern:
            text = "\n".join(l for l in text.splitlines() if re.search(pattern, l))
        return text

    def show_log(self, pattern, note=None):
        """Prints the error-log lines that match a pattern, as grep would."""
        print(f"$ grep -E {_arg(pattern)} mysqld.err" + (f"   # {note}" if note else ""))
        print(self.log(pattern))
        print()

    def query(self, sql, db=None):
        """One value from one query, unprinted (the session uses it to wait or to decide)."""
        args = ["mysql", f"--defaults-file={self.client_cnf}", "-N", "-B", *([db] if db else [])]
        code, out = self._call(args, stdin=sql)
        if code != 0:
            sys.exit(f"the query failed: {out}")
        return out.strip()

    def silent(self, sql):
        """Runs a statement without printing it (used only for the run's password)."""
        self.query(sql)

    def mysql(self, script, errors=0, status=None, db=None, flags=("-t", "-vvv", "--skip-comments"), note=None, force=None):
        """Runs a client script from standard input and prints the command and its output. `errors` is how many
        ERROR lines the script must print; with errors > 0 the client runs on past an error (--force) and then ends
        with status 0, without --force it stops at the first error with status 1. The default flags print each
        statement (-vvv) as a table (-t) without the script's comments (--skip-comments)."""
        force = errors > 0 if force is None else force
        shown = ["mysql", *flags, *(["--force"] if force else []), *([db] if db else [])]
        args = ["mysql", f"--defaults-file={self.client_cnf}", *shown[1:]]
        code, out = self._call(args, stdin=(self.base / script).read_text())
        if note:
            print(f"-- {note}")
        print("$ " + " ".join(_arg(a) for a in shown) + f" < {script}")
        if out.strip():
            print(out.rstrip("\n"))
        print()
        found = sum(1 for line in out.splitlines() if line.startswith("ERROR "))
        expected_status = (1 if errors and not force else 0) if status is None else status
        if found != errors or code != expected_status:
            sys.exit(f"{script}: {found} errors and status {code}; the session expects {errors} and {expected_status}")
        return out

    def shell(self, *args, status=0, shown=None, connect=False):
        """Runs one of MySQL's programs and prints the command and its output. connect=True gives a client program
        (mysqladmin, mysqlshow, mysql) the run's client option file, which the printed command leaves out."""
        args = [str(a) for a in args]
        run = [args[0], f"--defaults-file={self.client_cnf}", *args[1:]] if connect else args
        code, out = self._call(run)
        _show(shown or args, out)
        if code != status:
            sys.exit(f"{args[0]} ended with status {code}, not {status}")
        return out

    def sql(self, statement, flags=("-t",), errors=0, db=None, note=None):
        """Runs one statement with mysql -e and prints the command and its output."""
        shown = ["mysql", *flags, *([db] if db else []), "-e", statement]
        code, out = self._call(["mysql", f"--defaults-file={self.client_cnf}", *shown[1:]])
        if note:
            print(f"-- {note}")
        _show(shown, out)
        found = sum(1 for line in out.splitlines() if line.startswith("ERROR "))
        if found != errors or (code != 0) != (errors > 0):
            sys.exit(f"{statement}: {found} errors and status {code}; the session expects {errors}")
        return out

    def write_file(self, path, text, show=True):
        """Writes a text file into the run folder (an option file, a script) and prints it, as cat would."""
        (self.base / path).write_text(textwrap.dedent(text).lstrip("\n"))
        if show:
            self.show_file(path)

    def show_file(self, path):
        print(f"$ cat {path}")
        print(self.clean((self.base / path).read_text()).rstrip("\n"))
        print()

    def listing(self, folder, prefix=""):
        """Prints the names in a folder of the run (sorted, folders with a slash), as `ls -p` would list them."""
        path = self.base / folder
        names = sorted(n + ("/" if (path / n).is_dir() else "") for n in os.listdir(path) if n.startswith(prefix))
        print(f"$ ls -p {folder}" + (f" | grep '^{prefix}'" if prefix else ""))
        print(textwrap.fill("  ".join(names), width=78, break_on_hyphens=False))
        print()
        return names


def _redraw(block, clean):
    """Draws a client table again after its cells were cleaned. The columns come from the first border line; the row
    after it is the header (left-aligned); a column whose data cells were padded on the left (numbers) stays
    right-aligned (an empty cell says nothing about its column)."""
    cuts = [k for k, ch in enumerate(block[0]) if ch == "+"]
    rows = [None if line.startswith("+") else [line[x + 1:y] for x, y in zip(cuts, cuts[1:])] for line in block]
    header = next(k for k, r in enumerate(rows) if r is not None)
    ncol = len(cuts) - 1
    right = [any(r is not None and k != header and r[c].strip() and r[c].startswith("  ") for k, r in enumerate(rows))
             for c in range(ncol)]
    cells = [None if r is None else [clean(x.strip()) for x in r] for r in rows]
    widths = [max(len(r[c]) for r in cells if r is not None) for c in range(ncol)]
    drawn = []
    for k, r in enumerate(cells):
        if r is None:
            drawn.append("+" + "+".join("-" * (w + 2) for w in widths) + "+")
        else:
            drawn.append("|" + "|".join(" " + (t.rjust(w) if right[c] and k != header else t.ljust(w)) + " "
                                        for c, (t, w) in enumerate(zip(r, widths))) + "|")
    return drawn


def _arg(a):
    """A command-line argument as a reader would type it: quotes only where the shell needs them."""
    a = str(a)
    if re.fullmatch(r"[\w@%+=:,./-]+", a):
        return a
    if not re.search(r"[\"$`\\!]", a):
        return f'"{a}"'
    return shlex.quote(a)


def _show(args, out):
    print("$ " + " ".join(_arg(a) for a in args))
    if out.strip():
        print(out.rstrip("\n"))
    print()

The build line reads Ver 8.4.11 for macos15 on arm64 (MySQL Community Server - GPL), and inside the server VERSION() gives 8.4.11, @@version_comment the edition MySQL Community Server - GPL, and @@innodb_version the same 8.4.11 (this is the macOS arm64 build; the platform words change on other builds). None of these names the release track. The track is decided by Oracle, not stored in the server.

MySQL runs two tracks in parallel (release-model page). A long-term support (LTS) release: A MySQL release line that receives only fixes after its first (.0) release, adding or removing no features, and is supported for five years of Premier plus three years of Extended support. line, such as 8.4 or 9.7, receives only the fixes needed to keep its behaviour steady; features are set in its .0 release, and the manual says there are no removals within an LTS line after that. An Innovation release: A MySQL release that adds features and may change behaviour between releases, and is supported only until the next Innovation release ships; it suits teams that upgrade often and test heavily. adds features and is allowed to change behaviour, because, as the manual puts it, code is refactored, deprecated functionality is removed and MySQL is brought closer to the SQL standard.

Track What it promises How long it is supported Who it suits
LTS Only fixes that keep behaviour stable; features fixed in the .0 release, none added or removed later Five years of Premier plus three years of Extended support, under Oracle’s Lifetime Support policy A service wanting a stable feature set and a long support window
Innovation New features, and behaviour changes such as removed defaults and new reserved words Only until the next Innovation release ships Teams that upgrade often and test heavily

Read the table from the right: the question is not which track is newer, but whether this service would rather absorb a behaviour change every few months or sit still on one feature set for years.

Calendar versions arrive, and the number hides the track

For years a larger MySQL number meant a later release on the same counting system. That changed at the top of the range. MySQL 9.7 was the last line to use the old sequential numbers; from 26.7 the numbers are A version number that encodes when a release was produced rather than a running count: MySQL uses the form YY.M.P, so 26.7.0 is the July release of year 2026, patch 0. in the form YY.M.P, where 26.7.0 is the July release of year 2026, patch 0. The sources confirm it: the release-model page states that a calendar version does not determine whether a release is Innovation or LTS, and Oracle’s own version table labels 26.7.0 an Innovation release.

So the number alone cannot tell you the track. This program reads the recorded variable dumps of three builds and looks for any variable that names the track:

Three builds, and nothing in them that says LTS or Innovation

version_dumps.py

"""Read the recorded variable dumps of three MySQL builds and show what the number does not say.

dump_defaults.py (a noRun tab) made each .tsv by reading every global variable of a brand-new server started with
--no-defaults. This program (standard library only) pulls out the version line of each build, then shows that the
release track — LTS or Innovation — is not among the roughly 600 variables any build reports: the number 26.7 on its
own tells you nothing about whether that release only receives fixes or may also change behaviour.
"""

DUMPS = ("defaults-8.4.11.tsv", "defaults-9.7.2.tsv", "defaults-26.7.0.tsv")
TRACK_WORDS = {"lts", "innovation"}


def read(path):
    values = {}
    with open(path, encoding="utf-8") as handle:
        for line in handle:
            name, _, value = line.rstrip("\n").partition("\t")
            values[name] = value
    return values


print(f"{'build':20} {'version':9} {'version_comment':31} {'compiled for'}")
for path in DUMPS:
    v = read(path)
    platform = f"{v['version_compile_os']} {v['version_compile_machine']}"
    print(f"{path:20} {v['version']:9} {v['version_comment']:31} {platform}")

print()
for path in DUMPS:
    names = list(read(path))
    track = [n for n in names if TRACK_WORDS & set(n.lower().split("_"))]
    print(f"{path:20} {len(names):3} variables; naming the release track: {track or 'none'}")

print()
print("The edition (version_comment) is identical on all three; the track is not a variable at all.")

Output

build                version   version_comment                 compiled for
defaults-8.4.11.tsv  8.4.11    MySQL Community Server - GPL    macos15 arm64
defaults-9.7.2.tsv   9.7.2     MySQL Community Server - GPL    macos15 arm64
defaults-26.7.0.tsv  26.7.0    MySQL Community Server - GPL    macos15 arm64

defaults-8.4.11.tsv  593 variables; naming the release track: none
defaults-9.7.2.tsv   601 variables; naming the release track: none
defaults-26.7.0.tsv  611 variables; naming the release track: none

The edition (version_comment) is identical on all three; the track is not a variable at all.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 version_dumps.py

dump_defaults.py

"""Dumps the compiled defaults of one MySQL build: every global variable of a brand-new server started with
--no-defaults, one "name<TAB>value" line each, sorted by name.

    python3 dump_defaults.py                      # the pinned MySQL 8.4.11
    python3 dump_defaults.py <unpacked tarball>   # any other build, for example the official 9.7.2 or 26.7.0 tarball

The server is a throwaway one from mysql_session.py (127.0.0.1 only, X Protocol off, deleted afterwards). Values that
would differ from one computer or run to the next are replaced: the run folder is ".", MySQL's own folder <basedir>,
the run's port <port>, the server's UUID <uuid> and the host name lab-host. Values a server sizes from the machine
(CPU count, memory, the open-files limit) are kept as this computer gave them.
"""

import sys

from mysql_session import Server

QUERY = "SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_variables ORDER BY VARIABLE_NAME"

with Server(basedir=sys.argv[1] if len(sys.argv) > 1 else None) as my:
    my.up()
    uuid = my.query("SELECT @@server_uuid")
    for line in my.query(QUERY).splitlines():
        name, _, value = line.partition("\t")
        if value == str(my.port):
            value = "<port>"
        print(f"{name}\t{value.replace(uuid, '<uuid>')}")

Across the three builds the edition is identical, MySQL Community Server - GPL, and the search for a variable whose name mentions the track returns none in all of them: 593 variables in 8.4.11, 601 in 9.7.2 and 611 in 26.7.0, and not one of them records whether the release is LTS or Innovation. Walk the lines in order to see where each sits:

Timeline · 5 events in order MySQL release lines from 8.0 to 26.7 Walk through the lines in order, reading the tag on each to see whether it only receives fixes or may also change behaviour.
  1. 8.0

    The long-lived 8.0 line Sustaining Support

    From 8.0.34 this line only received fixes, like an LTS line. It has now moved to Oracle Sustaining Support, so no new bug or security fixes are produced; estates on it are told to move to 8.4 LTS or 9.7 LTS.

  2. 8.4

    MySQL 8.4 LTS LTS

    An LTS line that receives only the fixes needed to keep behaviour stable. Features were set in 8.4.0 and are neither added nor removed in later 8.4.x releases. The lesson records its examples on 8.4.11.

  3. 9.0–9.6

    The 9.x Innovation releases Innovation

    Each Innovation release adds features and may change behaviour, and is supported only until the next Innovation release ships. They suit teams that upgrade often and test heavily.

  4. 9.7

    MySQL 9.7 LTS LTS

    The last line to use the old sequential version numbers, and an LTS line, so it receives fixes for years. It is also the only sequential line that can upgrade straight into the first calendar-versioned releases.

  5. 26.7

    MySQL 26.7 Innovation

    The first calendar version, in the form year-dot-month-dot-patch. It is an Innovation release, as Oracle's own version table labels it, even though nothing in the number itself says so.

The timeline has 5 events, from 8.0 to 26.7.

8.4 and 9.7 are LTS lines; the 9.x releases between them and 26.7 are Innovation releases. The jump from 9 to 26 is not fifteen missed versions; it is the switch to counting by year.

Checkpoint

Check: LTS and Innovation tracks · Calendar versions and the track

  1. Question 1 of 2 What does a MySQL LTS line such as 8.4 promise about its behaviour after the first release?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Only fixes; no features are added or removed within the line

    An LTS line trades new features for stability: its behaviour is set in the .0 release, and later releases in the line carry only the fixes needed to keep it running.

  2. Question 2 of 2 A colleague says 26.7.0 must be an LTS release because the number is so high. What is wrong with that?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The number is a calendar version and does not say the track; 26.7.0 is in fact an Innovation release

    From 26.7 the version is a calendar version in the form YY.M.P. The number says when the release was produced, not whether it only receives fixes (LTS) or may change behaviour (Innovation).

How long each line is supported, and picking one

Support length is the half of the choice that a demo never shows. An LTS line follows Oracle’s Lifetime Support policy: five years of Premier Support and three of Extended Support measured from the line’s first release. An Innovation release is supported only until the next Innovation release ships, which can be a matter of months. And a line eventually leaves support altogether: MySQL 8.0 has moved to Oracle Sustaining Support (EOL announcements), which produces no new bug or security fixes, and the notice tells users to move to 8.4 LTS or 9.7 LTS.

Line Track Where its support stands
8.0 fixes-only Moved to Oracle Sustaining Support: no new fixes; the notice says move to 8.4 or 9.7
8.4 LTS Five years of Premier plus three of Extended support from its first release
9.7 LTS Five years of Premier plus three of Extended support from its first release
26.7 Innovation Supported only until the next Innovation release

The downloads page offers 26.7.0, 9.7.2 LTS, 8.4.11 LTS and 8.0.46 (downloads), so all four are still a click away even though they are at very different points of their lives. Which one a new project should build on depends on how long it must run, whether it can absorb behaviour changes, and whether it needs the newest features. Answer those questions and the table rules lines out:

Interactive · answer and compare Which line fits a new project? Answer the questions about your project and watch the table rule lines out or keep them with a caveat.
How long must this run before you are willing to do a major upgrade?
Can the application absorb behaviour changes between releases?
Do you need features that only the newest releases carry?
Does the project standardise on MySQL or on MariaDB?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Four release lines against the four things a new project weighs when it picks one.
Line Years of support Stable behaviour Newest features Low upgrade effort
MySQL LTS Example version: 8.4 or 9.7 Good five years of Premier plus three of Extended support from the lines first release Good only fixes after the .0 release, so behaviour does not change within the line Poor no new features arrive until the next LTS line Good in-place upgrades within the line and in-place to the next LTS line
MySQL Innovation Example version: 26.7 Poor supported only until the next Innovation release ships Poor behaviour changes are expected between Innovation releases Good the newest features and changes arrive here first Mixed frequent upgrades, but each step stays inside one compatibility lineage
MariaDB LTS Example version: 12.3 Good three years of community binaries plus two more years of source-only security fixes Good a yearly LTS keeps behaviour steady between the major series Mixed features reach the yearly LTS about a year after the rolling releases Good the mariadb-upgrade tool moves a database up from any older release
MariaDB rolling Example version: 13.x Poor each quarterly rolling release is superseded by the next one Mixed minor releases add features but keep backwards compatibility Good new features appear every quarter in the rolling releases Mixed quarterly updates, each tested, but frequent
How the answers decide
  • “How long must this run before you are willing to do a major upgrade?” Several years and “Can the application absorb behaviour changes between releases?” No, keep it stable: rules out MySQL Innovation and MariaDB rolling. A short-lived release forces a major upgrade within a year or less and may change behaviour each time, which a stable multi-year service cannot absorb.
  • “How long must this run before you are willing to do a major upgrade?” A year or less and “Do you need features that only the newest releases carry?” Yes, soon: adds a caveat to MySQL LTS and MariaDB LTS. An LTS line lags the newest features by up to a year, so wanting them soon points to an Innovation or rolling release instead.
  • “Can the application absorb behaviour changes between releases?” Yes, we test and adapt and “Do you need features that only the newest releases carry?” Yes, soon: adds a caveat to MySQL LTS. If the team can absorb change and wants new features now, Innovation or rolling releases deliver them sooner than the next LTS.
  • “Does the project standardise on MySQL or on MariaDB?” MySQL: rules out MariaDB LTS and MariaDB rolling. The project standardises on MySQL, so the MariaDB lines are set aside here.
  • “Does the project standardise on MySQL or on MariaDB?” MariaDB: rules out MySQL LTS and MySQL Innovation. The project standardises on MariaDB, so the MySQL lines are set aside here.

Key idea

Pick a line from its support window and your appetite for change, not from its number: an LTS line trades the newest features for years of stable behaviour, while an Innovation release trades stability for features and a short support window.

Upgrades move one line at a time

Once a line is chosen, the next question is how to leave it later, and MySQL is strict about the steps. The rules on the release-model page say an LTS line upgrades within its own series or to the immediately following LTS series, and may not skip one; an Innovation release upgrades only inside its own compatibility lineage; and only the last sequential LTS, 9.7.x, may upgrade directly into the first calendar-versioned lineage. This program encodes those rules and prints which direct upgrades between the four lines are supported:

Which direct upgrades between release lines are supported Python · release_lineage.py
"""Which direct upgrades between MySQL release lines are supported?

The rules below come from the MySQL 26.7 Reference Manual, section 1.3 "MySQL Releases: Innovation and LTS". The
program (standard library only) encodes them as data and prints an allowed/not-supported grid for direct in-place
upgrades between the lines the downloads page offers: 8.0, 8.4, 9.7 and 26.7. Each decision carries the rule that
produced it, so you can see why 8.0 straight to 9.7, or 8.4 straight to 26.7, is refused and must go one step at a
time.
"""

# Lines in release order. 8.0, 8.4 and 9.7 are LTS lines; 9.7 is the last sequentially numbered line, and 26.7 is an
# Innovation release that opens the first calendar-versioned lineage, which the manual says is anchored by 9.7.0.
LINES = ["8.0", "8.4", "9.7", "26.7"]


def direct_upgrade(src, dst):
    i, j = LINES.index(src), LINES.index(dst)
    if i == j:
        # "in-place upgrades and downgrades are possible within the LTS series" (release-model page, LTS Notes)
        return True, "same line, in place within the series"
    if j < i:
        # Downgrades work in place only inside an LTS series; across lines they need a logical dump and load.
        return False, "downgrade, not in place across lines"
    if src in ("8.0", "8.4") and dst == LINES[i + 1]:
        # "Upgrading to the next LTS series is supported ... while skipping an LTS series is not." (LTS Notes)
        return True, "to the next LTS series, no skip"
    if src == "9.7" and dst == "26.7":
        # "Only ... MySQL 9.7.x LTS can upgrade directly into the first calendar-versioned compatibility lineage."
        return True, "9.7 LTS into the first calendar lineage"
    # Skipping an LTS series, or crossing a compatibility-lineage boundary, is not a supported direct upgrade.
    return False, "skips a line or crosses a lineage"


print(f"{'from':5} {'to':5} {'direct?':8} rule")
for src in LINES:
    for dst in LINES:
        if src == dst:
            continue
        allowed, rule = direct_upgrade(src, dst)
        print(f"{src:5} {dst:5} {('yes' if allowed else 'no'):8} {rule}")

Output

from  to    direct?  rule
8.0   8.4   yes      to the next LTS series, no skip
8.0   9.7   no       skips a line or crosses a lineage
8.0   26.7  no       skips a line or crosses a lineage
8.4   8.0   no       downgrade, not in place across lines
8.4   9.7   yes      to the next LTS series, no skip
8.4   26.7  no       skips a line or crosses a lineage
9.7   8.0   no       downgrade, not in place across lines
9.7   8.4   no       downgrade, not in place across lines
9.7   26.7  yes      9.7 LTS into the first calendar lineage
26.7  8.0   no       downgrade, not in place across lines
26.7  8.4   no       downgrade, not in place across lines
26.7  9.7   no       downgrade, not in place across lines

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 release_lineage.py

The grid shows three yes rows: 8.0 to 8.4, 8.4 to 9.7, and 9.7 to 26.7, each a single step to the next line. Every jump that skips a line is a no: 8.0 straight to 9.7, 8.0 to 26.7, and 8.4 to 26.7 all fail, because the target is more than one line ahead. Downgrades across lines are no too; the manual allows in-place downgrades only within an LTS series, and an Innovation downgrade needs a dump and reload. So an estate on 8.0 reaches 26.7 by hopping line to line, never in one leap:

Interactive · stage by stage One line at a time, from 8.0 to 26.7 Step through the supported hops an estate on 8.0 takes to reach the newest release, one line at a time.
One line at a time, from 8.0 to 26.7, step 4 of 4. Showing MySQL 8.0, MySQL 8.4 LTS, MySQL 9.7 LTS, MySQL 26.7. In focus: MySQL 26.7.MySQL 8.0Sustaining SupportMySQL 8.4 LTSthe next LTS lineMySQL 9.7 LTSlast sequential lineMySQL 26.7Innovation, newest

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. Start on 8.0. The estate runs on MySQL 8.0, which has moved to Oracle Sustaining Support, so no more bug or security fixes are produced for it. It is time to move.
  2. Hop to 8.4 LTS. The only supported next step is the immediately following LTS line, 8.4. A direct jump to 9.7 or 26.7 is refused because it skips a line.
  3. Hop to 9.7 LTS. From 8.4 LTS the next supported hop is 9.7 LTS. A service that needs steady behaviour can stop here and take only its fixes for years.
  4. Hop to 26.7. A team that wants the newest features continues to 26.7, the first calendar version. MySQL 9.7 is the only line that can enter that calendar lineage directly.

An in-place upgrade or downgrade is fine within one LTS series, so 8.4.0 to 8.4.11 and back is routine; it is only the jump between lines that the rules police.

Checkpoint

Check: Support windows and picking a line · One-line-at-a-time upgrades

  1. Question 1 of 2 How long is a MySQL LTS line supported, and where does MySQL 8.0 stand now?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Five years of Premier plus three of Extended support; 8.0 has moved to Sustaining Support with no new fixes

    An LTS line is supported for five years of Premier and three of Extended support from its first release. MySQL 8.0 has passed that and moved to Sustaining Support, which the notice says should prompt a move to 8.4 or 9.7.

  2. Question 2 of 2 An estate on MySQL 8.0 wants to reach 26.7. Which direct upgrade does the recorded grid allow as the first step?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 8.0 to 8.4, because 8.4 is the immediately following LTS line

    Upgrades move one line at a time. From 8.0 the only supported first hop is 8.4, then 9.7, then 26.7, which is why the grid shows three yes rows and marks every skipping jump no.

MariaDB: a yearly LTS and quarterly rolling releases

MariaDB Server is a separate database, not a build of MySQL. It was started by the original developers of MySQL as an enhanced, drop-in replacement, and the MariaDB Foundation says it will stay open source under GPLv2. A decade of its own development means it is no longer a straight drop-in: later lessons in this track show where its SQL and defaults differ.

Its release model echoes MySQL’s two tracks but with different words and timing. A new MariaDB rolling release: A MariaDB release shipped every quarter with the latest fixes and features, superseded by the next quarterly release, as opposed to MariaDB's yearly long-term support release. ships every quarter, carrying the latest fixes and features, and one long-term release is named each year. The Foundation’s maintenance policy sets the community support window: LTS binaries are published for three years from the release date, and critical and security fixes continue in source-only releases for two more years after that.

MariaDB release Cadence Community support
LTS, such as 12.3 One a year Binaries for three years, then two more years of source-only security fixes
Rolling, such as 13.0 and 13.1 One a quarter Each is superseded by the next quarterly release

The release list shows the picture at a glance: 12.3, 11.8, 11.4, 10.11 and 10.6 are long-term support lines, while 13.0 is a stable rolling release and 13.1 a release candidate. So MariaDB gives you a slower, yearly LTS cadence than MySQL’s multi-year LTS lines, and a faster, quarterly feature cadence than MySQL’s Innovation releases.

Distributions, forks and the licences they ship under

The server you install is one of a small family. The build this lesson runs reports its edition as MySQL Community Server - GPL: the edition this track runs, distributed under GPLv2 with the Universal FOSS Exception, which lets it be combined with other open-source licences. Alongside it Oracle sells MySQL Enterprise Edition, whose extra parts are commercial only; this track labels those Enterprise-only features wherever they appear, so that a feature such as JavaScript stored programs, Enterprise Audit, Firewall or Enterprise Backup is never mistaken for something the Community server can do.

Distribution From Licence and note
MySQL Community Server Oracle GPLv2 with the Universal FOSS Exception; the edition this track records on
MySQL Enterprise Edition Oracle Commercial; adds Enterprise-only features such as Enterprise Audit, Firewall and Enterprise Backup
Percona Server for MySQL Percona GPLv2; a drop-in build of MySQL Community with extra diagnostics and features
MariaDB Server MariaDB GPLv2; the separate server described above

Percona Server for MySQL tracks MySQL’s versions closely and stays a drop-in build, so a team can run it where it wants Percona’s additions without leaving the MySQL line it chose. The licence matters as much as the features: all three open-source servers are GPLv2, so the choice between them is about support, tooling and behaviour, not about paying for the database engine itself.

Checkpoint

Check: MariaDB's release model · Distributions and licences

  1. Question 1 of 2 How does MariaDB's release cadence compare with MySQL's tracks?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: A long-term release once a year and a rolling release each quarter

    MariaDB names one LTS a year (12.3 is a current one) and ships a rolling release each quarter (13.0, 13.1), a slower LTS cadence and a faster feature cadence than MySQL's tracks.

  2. Question 2 of 2 Under which licence is MySQL Community Server distributed?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: GPLv2 with the Universal FOSS Exception

    MySQL Community Server is GPLv2 with the Universal FOSS Exception. Percona Server for MySQL and MariaDB Server are GPLv2 too, so the choice between them is about support and features, not the engine's price.

Practise

Run the three ideas that decide a real choice: which track a line belongs to, how long it is supported, and which single-step upgrades are allowed. The self-check starts where your earlier answers were weakest:

Practice that adapts to you

Practise this lesson

12 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 12 A service on an Innovation release finds a query behaves differently after an upgrade. Is that expected?

    Choose one answer.

    Show the answer: Question 1 of 12

    Answer: Yes; Innovation releases may change behaviour, such as removed defaults and new reserved words

    Behaviour changes are a defining feature of the Innovation track, which is why it suits teams with strong tests and fast upgrade cycles.

  2. Question 2 of 12 A payments service must stay stable for years with rare, planned upgrades. Which track fits, and why?

    Choose one answer.

    Show the answer: Question 2 of 12

    Answer: An LTS line, because its behaviour is fixed and it is supported for years

    A service that values stability and plans upgrades rarely belongs on the latest LTS line; Innovation suits teams that adopt features early and test each change.

  3. Question 3 of 12 In MySQL's calendar version 26.7.0, what does each part mean?

    Choose one answer.

    Show the answer: Question 3 of 12

    Answer: Year 2026, month 7 (July), patch 0

    MySQL 9.7 was the last sequential line; from 26.7 the version is YY.M.P, where 26 is the year, 7 the month and 0 the patch.

  4. Question 4 of 12 Which MySQL line was the last to use the old sequential version numbers?

    Choose one answer.

    Show the answer: Question 4 of 12

    Answer: 9.7

    9.7 closed the sequential era. The next release became 26.7, the first calendar version, so the number jumped from 9 to 26 without any missing releases.

  5. Question 5 of 12 How long is a MySQL Innovation release supported?

    Choose one answer.

    Show the answer: Question 5 of 12

    Answer: Only until the next Innovation release ships

    An Innovation release is superseded by the next one, so its support window is short and a team on that track must upgrade often.

  6. Question 6 of 12 A server still runs MySQL 8.0. What does its move to Oracle Sustaining Support mean for new security fixes?

    Choose one answer.

    Show the answer: Question 6 of 12

    Answer: No new bug or security fixes are produced; the notice says move to 8.4 or 9.7

    Sustaining Support means the version still exists but gets no new bug or security fixes, which is the signal to plan an upgrade to a supported LTS line.

  7. Question 7 of 12 Can a MySQL LTS line be upgraded in a single step that skips the next LTS line?

    Choose one answer.

    Show the answer: Question 7 of 12

    Answer: No; it must upgrade to the immediately following LTS line, without skipping one

    Both tracks move one line at a time. An LTS line steps to the next LTS line, which is why 8.0 reaches 26.7 only through 8.4 and 9.7.

  8. Question 8 of 12 Why can MySQL 9.7 upgrade directly to 26.7, while 8.4 cannot?

    Choose one answer.

    Show the answer: Question 8 of 12

    Answer: Only the last sequential LTS, 9.7, may step into the first calendar-versioned lineage

    9.7 is the one sequential line the rules let cross into the first calendar lineage. An estate on 8.4 must go to 9.7 first, then to 26.7.

  9. Question 9 of 12 How often does MariaDB ship a rolling release?

    Choose one answer.

    Show the answer: Question 9 of 12

    Answer: Once a quarter

    MariaDB names one LTS a year and ships a rolling release each quarter, so new features reach rolling users four times a year.

  10. Question 10 of 12 For how long does a MariaDB community LTS get support, and in what form?

    Choose one answer.

    Show the answer: Question 10 of 12

    Answer: Three years of binaries, then two more years of source-only security fixes

    A MariaDB community LTS gets binaries for three years and then source-only security fixes for two more, five years of security coverage in total.

  11. Question 11 of 12 A tutorial shows JavaScript stored programs and Enterprise Audit on MySQL. Can the Community server do these?

    Choose one answer.

    Show the answer: Question 11 of 12

    Answer: No; these are Enterprise-only features, not in MySQL Community Server

    Enterprise Audit, Firewall, Enterprise Backup and JavaScript stored programs are Enterprise-only. The track names such features wherever they appear so they are not mistaken for Community features.

  12. Question 12 of 12 Which statement about the licences of MySQL Community, Percona Server and MariaDB Server is correct?

    Choose one answer.

    Show the answer: Question 12 of 12

    Answer: All three are GPLv2, so the choice between them is about support and features, not engine price

    The three open-source servers share the GPLv2 licence, so they cost nothing for the engine itself; what differs is their features, tooling and support.

Interview questions

Warm-up (fresher): What do MySQL’s LTS and Innovation tracks each promise, and how often must you upgrade on each?

An LTS line, such as 8.4 or 9.7, promises stable behaviour: its feature set is fixed in the .0 release, and later releases in the line add only fixes, with nothing added or removed. It is supported for a long time under Oracle’s Lifetime Support policy, five years of Premier Support and three of Extended Support from the line’s first release, so a service can sit on one LTS line for years and take only patch upgrades within it, such as 8.4.0 up to 8.4.11. An Innovation release, such as 26.7, promises the newest features but expects behaviour to change between releases as code is refactored, defaults change and new reserved words appear; it is supported only until the next Innovation release ships, which can be months, so you must upgrade along the Innovation track much more often and test each step. A new production service that values stability picks the latest LTS line; a team that wants features early and has strong automated tests follows Innovation.

These two cases go further than the warm-up. Work out your own answer first, then open each for a model answer and the follow-ups an interviewer would ask:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A retail company's main database still runs on MySQL 8.0.46 and the team is anxious about touching it. What do you tell them about where 8.0 stands today, the supported path to a current release, and how to carry the move out safely?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A product team expects its service to run for at least five years and asks whether to build on a MySQL LTS line or to follow the Innovation releases. How do you frame the decision for them?

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. How does a MySQL LTS line differ from an Innovation release?

    LTS lines (8.4, 9.7) get fixes but no new features, for a support window measured in years. Innovation releases (26.7) bring features and possible behaviour changes, and each loses support once the next Innovation release is out.

  2. From 26.7 on, can you tell from a MySQL version number whether it is LTS or Innovation?

    No. Versions take the calendar form YY.M.P from 26.7, and the number alone hides the track; Oracle's release metadata and version tables say which kind a release is.

  3. How long is a MySQL LTS line supported, and what is left for MySQL 8.0?

    Eight years in all from the line's first release: Premier Support for five, Extended Support for three more. MySQL 8.0 is now in Sustaining Support, which brings no new fixes.

  4. Which route takes a MySQL 8.0 estate to 26.7?

    8.0 to 8.4, then 9.7, then 26.7. Upgrades move one line at a time, an LTS line goes to the next LTS without skipping one, and only 9.7 may step straight into the first calendar-versioned lineage.

  5. What release cadence does MariaDB Server follow?

    Its own schedule, as a separate server: one LTS a year, with community binaries for three years and two more years of source-only fixes, and a rolling release every quarter that the next quarter's release replaces.

  6. Under which licence do MySQL Community Server, Percona Server for MySQL and MariaDB Server ship?

    All three are GPLv2. Features that only MySQL Enterprise Edition has are named as Enterprise features wherever the track mentions them.

Key takeaways

  • MySQL ships two tracks at once: LTS lines (8.4, 9.7) receive only fixes and are supported for years, while Innovation releases (26.7) add features, may change behaviour, and are supported only until the next Innovation release.
  • From 26.7 MySQL uses calendar versions in the form YY.M.P, and the number alone does not say whether a release is LTS or Innovation; Oracle’s release metadata and version tables do.
  • An LTS line gets five years of Premier plus three of Extended support from its first release; MySQL 8.0 has already moved to Oracle Sustaining Support, which produces no new fixes.
  • Upgrades move one line at a time: an LTS line goes to the next LTS without skipping, and only 9.7 may step directly into the first calendar-versioned lineage, so an estate on 8.0 reaches 26.7 through 8.4 and 9.7.
  • MariaDB is a separate GPLv2 server with its own schedule: a yearly LTS with three years of community binaries plus two more of source-only fixes, and a quarterly rolling release.
  • MySQL Community Server, Percona Server for MySQL and MariaDB Server are all GPLv2; Enterprise-only MySQL features are named as such wherever they appear.

Terms in this lesson

Innovation release
A MySQL release that adds features and may change behaviour between releases, and is supported only until the next Innovation release ships; it suits teams that upgrade often and test heavily.
long-term support (LTS) release
A MySQL release line that receives only fixes after its first (.0) release, adding or removing no features, and is supported for five years of Premier plus three years of Extended support.
rolling release
A MariaDB release shipped every quarter with the latest fixes and features, superseded by the next quarterly release, as opposed to MariaDB's yearly long-term support release.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.