MySQL allows several different ways to authenticate users. These are called "authentication plugins," and each user can specify its own plugin. To see which plugin is assigned to a user, you can use the plugin
value in the mysql.user
table. On Ubuntu systems (and possibly other Linux systems), the default settings for a new MySQL installation give the MySQL root
user a different plugin from standard users, which explains the discrepancy you've noticed.
Specifically, the MySQL root
user is assigned auth_socket
authentication by default, while standard users are assigned either mysql_native_password
(pre-MySQL 8) or caching_sha2_password
(MySQL 8+). That is, you would see something like
# Prior to MySQL 8
mysql> USE mysql;
mysql> SELECT plugin FROM mysql.user WHERE user='root';
+-------------+
| plugin |
+-------------+
| auth_socket |
+-------------+
mysql> SELECT plugin FROM mysql.user WHERE user='basic_db_user';
+-----------------------+
| plugin |
+-----------------------+
| mysql_native_password |
+-----------------------+
or
# MySQL 8 and above
mysql> USE mysql;
mysql> SELECT plugin FROM mysql.user WHERE user='root';
+-------------+
| plugin |
+-------------+
| auth_socket |
+-------------+
mysql> SELECT plugin FROM mysql.user WHERE user='basic_db_user';
+-----------------------+
| plugin |
+-----------------------+
| caching_sha2_password |
+-----------------------+
auth_socket
is socket authentication, which works by matching the connecting Unix user to the MySQL user of the same name, if there is one. This only works when connecting locally, which is one reason it's good for the privileged MySQL root
user; under the auth_socket
plugin, it isn't possible for someone to access your database as its root
user remotely (unless they also have OS-level root
access, in which case they already own your machine anyway).
Both mysql_native_password
and caching_sha2_password
are forms of password authentication. To understand the difference you see between accessing MySQL as its root
user and as a standard user, we'll compare the login process between password authentication and socket authentication.
Password authentication
Let's say you attempt to access a local MySQL server using the command that's typically recommended. Your Unix/Linux OS user is joeuser
, and you want to connect to the MySQL server as the MySQL user joesql
. You type
joeuser@localhost:~$ mysql -u joesql -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
...
The connection is successful. What has happened here is that the mysql
program (use $ which mysql
to find it on your system) is invoked with the command-line arguments -u, joesql, -p
. From these, MySQL understands that you're trying to connect as the MySQL user joesql
. It checks the plugin
value of mysql.user
for joesql
and finds, let's say, caching_sha2_password
. Thus, it knows you need a password to connect. From the -p
command-line argument, it knows that you want it to ask you to type in your password, and it does so. You enter your password, and MySQL hashes it and compares that hash to what it has stored as the password for joesql
. It matches, so you are allowed to connect as MySQL user joesql
.
Socket Authentication
Now, you want to access the local MySQL server as the MySQL root
user. If you naively attempt the same command you used for the standard user joesql
, you run into the problem you've noted:
joeuser@localhost:~$ mysql -u root -p
Enter password:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
This happens even if you have a password defined for the MySQL root
user and enter that when prompted. So what happened? As before, you've invoked the mysql
program with command-line arguments -u, root, -p
, which tells MySQL that you'd like to connect as the MySQL root
user. Now, however, when it checks the plugin
value of mysql.user
for root
, it finds auth_socket
instead of one of the password authentication values.
So, instead of asking for a password, it checks a specified socket file that your Unix/Linux system wrote expressly for this purpose, and which includes the name of the OS user that you're acting as. Instead of matching passwords, auth_socket
matches usernames. Thus, noting that the OS username you're using, joeuser
, is not equal to the MySQL username you're attempting to connect as, root
, it forbids the connection with the seriously useless error message Access denied for user 'root'@'localhost'
.
So, irritated, you do the standard Linux move of adding 'sudo' to make it do what you want:
joeuser@localhost:~$ sudo mysql -u root -p
[sudo] password for joeuser:
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
...
This works. Why did this work? When the shell interprets your command, the first thing it finds is that you're using sudo
to act as the Unix root
user, so it asks you for your sudo
password. Once given, the remainder of the command is executed as that user. Thus, the socket file created when connecting to the MySQL server is identified with the Unix root
, not joeuser
, and MySQL's socket authentication matches this to the MySQL user of the same name. Authentication is passed at this point, but you did specifically request to be asked for your password with the -p
option, so MySQL dutifully obliges by asking for the MySQL root
user password. After entering it, you are successfully connected to the local MySQL server.
An obvious question once you understand this is, if socket authentication is based on matching usernames (Unix username to MySQL username), is the password even necessary? It isn't! You can leave off the -p
and do this:
joeuser@localhost:~$ sudo mysql -u root
[sudo] password for joeuser:
Welcome to the MySQL monitor. Commands end with ; or \g.
...
and skip entering your MySQL root
password. This works only for the MySQL root
user, and only if that user is set to use auth_socket
as their authentication plugin.
Summary
Under the standard defaults for MySQL on recent versions of Ubuntu (at least 16.04-20.04, probably others), these are the commands to most easily connect to the mysql
shell:
MySQL root
user: $ sudo mysql -u root
(enter your sudo password)
Other MySQL user: $ mysql -u <username> -p
(enter the <username> MySQL password)
Of course, you've asked about Debian Linux, not Ubuntu. Ubuntu is derived directly from Debian, and I assume it's similar to what I've said here. Comments about how the above explanation varies among various distributions and versions of Linux are welcome.