MySQL高阶

相关书籍

  1. 《MySQL是怎样运行的 从根儿上理解MySQL》 小孩子4919著
  2. 《高性能MySQL》 第4版

下面内容为《MySQL是怎样运行的 从根儿上理解MySQL》学习笔记。

装作自己是个小白初始MySQL

MYSQL的客户端/服务器架构

  1. 启动MySQL服务器程序
  2. 启动MySQL客户端程序,并连接到服务器程序。
  3. 在客户端程序中输入命令语句,并将其作为请求发送给服务器程序.服务器程序在收到这些请求后,根据请求的内容来操作具体的数据,并将结果返回给客户端.

MySQL服务器程序的进程称为MySQL数据库实例(instance)

MYSQL的安装

在安装MySQL时,无论用源码编译安装还是官方提供的安装包,无论采用哪种安装方式,一定要记住MySQL安装在哪里,一定要记住MySQL的安装目录。

[gaowanlu@vhost02 a5game_data]$ which mysql
/usr/bin/mysql
[gaowanlu@vhost02 a5game_data]$ which mysqld
/usr/sbin/mysqld
[gaowanlu@vhost02 a5game_data]$ 

启动MYSQL服务器程序

./bin/mysqld

或者绝对路径

/usr/sbin/mysqld

比如,环境变量PATH的值为 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin。 这个值表明,在我输入某个命令时,系统会在 /usr/local/bin、/usr/bin、/bin、/usr/sbin和/sbin目录下按照顺序依次寻找输入的这个命令.如果寻找成功,则执行该命令.

也可以修改这个环境变量PATH.把MySQL安装目录下的bin目录的绝对路径添加到PATH中.修改后的环境变量PATH的值为 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/sbin/mysqld• 这样一来,无论命令行解释器的当前工作目录是啥,都可以直接输入可执行文件的名字来启动,比如下面这样.

mysqld

在类UNIX系统中启动服务器程序

在类UNIX系统,用来启动MYSQL服务器程序的可执行文件有很多,大部分都位于MYSQL安装目录的bin目录下。

  1. mysqld

mysqld可执行文件就表示MySQL服务器程序,运行这个可执行文件就可以直接启动一个MySQL服务器进程.但这个可执行文件并不常用。

  1. mysql_safe

mysqld_safe是一个启动脚本,它会间接调用my叫ld并持续监控服务器的运行状态.当服务器进程出现错误时,它还可以帮助重启服务器程序.另外,使用mysqld_safe启动MySQL服务器程序时,它会将服务器程序的出错信息和其他诊断信息输出到错误日志,以方便后期查找发生错误的原因.

[gaowanlu@vhost02 bin]$ ps -ef | grep mysqld
mysql      1595      1  0 Feb27 ?        00:00:00 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
mysql      1760   1595  0 Feb27 ?        00:03:11 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock
gaowanlu  37846  17788  0 15:12 pts/5    00:00:00 grep --color=auto mysqld

出错日志默认写到一个以.err为扩展名的文件中,该文件位于MySQL的数据目录中。

  1. mysql.server

mysql.server也是一个启动脚本,它会间接调用mysqld_safe。有些安装了MySQL可能没有。

mysql.server start
mysql.server stop
  1. mysqld_multi

其实我们在一台计算机上也可以运行多个服务器实例,也就是运行多个MySQL服务器进程。mysqld_multi 可执行文件可以启动或停止多个服务器进程,也能报告它们的运行状态.

在Windows系统中启动服务器程序

它提供了两种启动方法,手动启动和以服务的形式启动。

  1. 手动启动

在Windows系统中安装完MySQL之后,MySQL安装目录的bin目录下也会存在mysqld可执行文件·在命令行解释器中输入mysqld,或者直接在bin目录下双击该文件,就可以启叨MySQL服务器程序了。

  1. 以服务的方式启动

如果我们需要在计算机上长时间运行某个程序,并且无论是谁在使用这台计算机,程序的运行都不受影响,就可以把它注册为一个Windows服务,由操作系统帮我们管理。

"完整的可执行文件路径" --install [-manual] [服务名]

如果我们添加了-manual边项,就表示在Windows系统启动的时候不自动启动该服务,否则会自动启动·

"C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqld" --install
net start MySQL
net stop MySQL

如果你喜欢图形界面,可以通过Windows的服务管理器并用鼠标点击的方式来启动和停止服务.

启动MySQL客户端程序

bin目录下有许多客户端程序,如mysqladmin、mysqldump、mysqlcheck等。主要关注mysql。

mysql -h主机名 -u用户 -p密码 -P端口

连接成功后将会进入一个mysql终端输入以下任意一个命令可以退出连接

mysql> quit
mysql> exit
mysql> \q

连接注意事项

客户端与服务器连接的过程

本质上是进程间的通信。

TCP/IP

端口号是一个整数值,取值范围是0~65535,MySQL服务器在启动时会默认申请3306端口号,之后就在这个端口号上等待客户端进程进行连接。

指定监听其他端口

mysqld -P3307

命名管道和共享内存

如果是Windows用户,本地客户端连接本地服务器,可以使用命名管道和共享内存。

UNIX域套接字

如下面例子

#服务器
mysqld --socket=/tmp/a.txt
#客户端
mysqld -hlocalhost -u root --socket=/tmp/a.txt -p

服务器处理客户揣请求

下面是客户端向服务器发送命令过程

查询请求执行过程

连接管理

每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程专门处理与这个客户端的交互;当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端.这样就不用频繁地创建和销毁线程,从而节省了开销.

在客户端程序发起连续时,需要携带主机信息、用户名、密码等信息,服务器程序会对客户端程序提供的这些信息进行认证.如果认证失败,服务辑程序会拒绝连接.另外,如果客户端程序和服务器程序不运行在一台计算机上,我们还可以通过采用传输层安全性(TransportLayer Security, TLS)协议对连接进行加密,从而保证数据传输的安全性。

解析与优化

  1. 查询缓存

MySQL服务器程序处理查询请求的过程也是这样,会把刚刚处理过的查询请求和结果缓存起来.如果下一次有同样的请求过来,直接从缓存中查找结果就好了,就不用再去底层的表中查找了.这个查询缓存可以在不同的客户端之间共享,也就是说,如果客户端A刚刚发送了一个查询请求,而客户端B之后发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据了.

MySQL服务器并没有人那么聪明,如果两个查询请求有任何字符上的不同,例如空格、注释、大小写,都会导致缓存不会命中,如果查询请求中包含某些系统函数、用户自定义变量和函数、系统表,如mysql、information_schema、performance_schema数据库中的表,则这个请求不会被缓存。

不过既然是缓存,那就有缓存失效的时候.MySQL的缓存系统会监测涉及的每张衰,只要该袤的结构或者数据被修改,比如对该表使用了的INSERT、UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或DROP DATABASE语句,则与该表有关的所有查询缓存都将变为无效并从查询缓存中删除!

从MySQL5.7.20开始,不才在荐使用查询缓存,在小贴士MySQL8.0中直接将其删除.

  1. 语法解析

如果查询缓存没有命中,接下来就需要进入正式的查询阶段了.因为客户端程序发送过来的请求只是一段文本,所以MySQL服务器程序首先妥对这段文本进行分析,判断请求的语法是否正确,然后从文本中将要查询的表、各种查询条件都提取出来放到MySQL服务器内部使用的一些数据结构上.

从本质上来说,这个从指定的文本中提取出需妥的信息算是一个编译过程,涉及词曾习、法解析、语法分析、语义分析等阶段

  1. 查询优化

在语法解析后,服务器程序获得到了需要的信息,比如要查询的表和列是哪些、搜索条件是什么等。这些并不够,因为我们写的MySQL语句执行起来效率可能并不是很高,MySQL的优化程序会对语句进行一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等一堆东西。

存储引擎

MySQL服务器把数据的存储和提取操作都封装到了一个名为存储引擎的模块中.

表是由一行一行的记录组成的,但这只是一个逻辑上的概念.在物理上如何表示记录,怎么从表中读取数据,以及怎么把数据写入具体的物理存储器上,都是存储引擎负责的事情.为了实现不同的功能,MySQL提供了各式各样的存储引擎,不同存储引擎管理的表可能有不同的存储结构,采用的存取算法也可能不同.

MySQL服务器处理请求的过程简单地划分为server层和存储引擎层.连接管理、查询缓存、语法解析、查询优化这些并不涉及真实数据存取的功能划分为server层的功能,存取真实数据的功能划分为存储引擎层的功能.各种不同的存储引擎为server层提供统一的调用接口,其中包含了几十个不同用途的底层函数,比如”读取索引第一条记录1111读取索引下一条记录”“插入记录”等.

常用存储引擎

MySQL支持的存储引擎

不同存储引擎对于某些功能支持情况也不相同。

InnoDB从MySQL5.5.5版本开始作为MySQL的默认存储引擎,之前版本的默认存储引擎为MyISAM.

关于存储引擎的一些操作

查看当前服务器程序支持的存储引擎

mysql> SHOW ENGINES;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.01 sec)

Transactions 是否支持事务、XA 是否支持分布式事务、Savepoints 是否支持事务的部分回滚。

设置表的存储引擎

  1. 创建表时指定存储引擎
CREATE TABLE 表名(
    建表语句;
) ENGINE = 存储引擎名称;
  1. 修改表的存储引擎
ALTER TABLE 表名 ENGINE = 存储引擎名称;

MYSQL的调控按钮–启动选项和系统变量

启动选项和配置文件

mysql设置选项一般都有各自的默认值,比如服务器允许同时连入的客户端的默认数量是151,表的默认存储引擎是InnoDB。 可以在程序启动的时候修改这些默认值,对于这种在程序启动时指定的设置项也称之为启动选项 startup option,启动选项一般也可以在配置文件中指定。

在命令行上使用选项

如在启动时就禁止各客户端使用TCP/IP网络进行通信,

mysqld --skip-networking

指定存储引擎

mysqld --default-storage-engine-MyISAM

不需要记忆,使用 –help查看即可

mysqld --help
mysqld_safe --help

常用的长形式和短形式含义

--host -h  主机名
--user -u 用户名
--password -p 密码
--port -P 端口
--version -V 版本信息

配置文件中使用选项

配置文件的路径

MySQL程序在启动时会在多个路径下寻找配置文件,这些路径有的是固定的,有的可以在命令行中指定。

类UNIX操作系统中的配置文件

/etc/my.cnf
/etc/mysql/my.cnf
SYSCONFDIR/my.cnf
$MYSQL_HOME/my.cnf 特定于服务器选项
default-extra-file 命令行指定的额外配置文件路径
~/.my.cnf 特定于用户的选项
~/.mylogin.cnf 特定于用户的登录路径选项

配置文件内容,如下面这样

[server]
[具体的启动选项...]

[mysqld]
[具体的启动选项...]

[mysqld_safe]
[具体的启动选项...]

[client]
[具体的启动选项...]

[mysql]
[具体的启动选项...]

[mysqladmin]
[具体的启动选项...]

程序的对应类别和能读取的组

mysqld [mysqld] [server]
mysqld_safe [mysqld] [server] [mysqld_safe]
mysql_server [mysqld] [server] [mysql.server]
mysql [mysql] [client]
mysqladmin [mysqladmin] [client]
mysqldump [mysqldump] [client]

内容不是很重要

系统变量

MySQL服务器程序在运行过程中会用到许多影响程序行为的变量,它们被称为系统变量。比如,允许同时连入的客户端数量用系统变盘max_connections表示;表的默认存储引擎用系统变量default_storage_engine表示,查询缓存的大小用系统变量query_ _cache_size表示­MySQL服务器程序的系统变量有好几百个,这里不再一一列举.每个系统变量都看一个默认值,我们可以使用命令行或者配置文件中的选项在启动服务器时改变一些系统变量的值·大多数系统变量的值也可以在程序运行过程中修改,而无须停止并重新启动服务器.

查看系统变量

SHOW VARIABLES; 
mysql> SHOW VARIABLES LIKE 'default_storage_engine';
+------------------------+--------+
| Variable_name          | Value  |
+------------------------+--------+
| default_storage_engine | InnoDB |
+------------------------+--------+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW VARIABLES LIKE 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set, 1 warning (0.00 sec)

模糊查询

mysql> SHOW VARIABLES LIKE 'default%';
+-------------------------------+-----------------------+
| Variable_name                 | Value                 |
+-------------------------------+-----------------------+
| default_authentication_plugin | caching_sha2_password |
| default_collation_for_utf8mb4 | utf8mb4_0900_ai_ci    |
| default_password_lifetime     | 0                     |
| default_storage_engine        | InnoDB                |
| default_table_encryption      | OFF                   |
| default_tmp_storage_engine    | InnoDB                |
| default_week_format           | 0                     |
+-------------------------------+-----------------------+
7 rows in set, 1 warning (0.00 sec)

设置系统变量

  1. 启动时通过命令行添加启动选项
  2. 通过配置文件添加启动选项
  3. 服务器程序运行过程中设置

设计MySQL的大叔提出了系统变量的作用范围的概念,具体来说,作用范围分为以下两种。

SET [GLOBAL|SESSION] 系统变量名 = 值
# 默认为SESSION范围的

查看不同作用范围的系统变量

SHOW [GLOBAL|SESSION] VARIABLES [LIKE 匹配的模式]

状态变量

MySQL服务器程序中维护了许多关于程序运行状态的变量,它们被称为状态变量。

SHOW [GLOBAL|SESSION] STATUS [LIKE 匹配的模式]
mysql> SHOW STATUS LIKE 'thread%' ;
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 0     |
| Threads_connected | 4     |
| Threads_created   | 4     |
| Threads_running   | 2     |
+-------------------+-------+
4 rows in set (0.00 sec)

字符集和比较规则

字符集和比较规则简介

字符集简介

比较规则简介

一般是以下情况直接比较二进制数据,如果不区分大小写的话,则

一些重要的字符集

  1. ASCII字符集

共收录128个字符,包括空格、标点符号、数字、大小写字母和-些不可见字符。由于ASCJJ字符集总共才128个字符,所以可以使用一个字节来进行编码.我们来看几个字符的编码方式:

'L' -> 01001100 (十六进制 0x4c 十进制76)
'M' -> 01001101 (十六进制 0x4D 十进制77)
  1. ISO 8859-1字符集

共收录256个字符,它在ASCIl字符集的基础上又扩充了128个西欧常用字符(包括德法两国的字母).ISO 8859-1字符集也可以使用一个字节来进行编码 这个字符集也有一个别名Latin1.

  1. GB2312字符集

收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄i吾西里尔字母,收录汉字6763个,收录其他文字符号682个.这种字符集同时又兼容ASCJJ字符集,所以在编码方式上显得有些奇怪:如果该字符在ASCIl字符集中,则采用一字节编码:否则采用两字节编码. 这种使用不同字节数来表示一个字符的编码方式称为变长编码方式.

  1. GBK字符集

GBK字符集只是在收录的字符范围上对GB2312字符集进行了扩充,编码方式兼容GB2312字符集.

  1. UTF-8字符集

几乎收录了当今世界各个国家/地区使用的字符,而且还在不断扩充.这种字符集兼容ASCII字符集,采用变长编码方式,编码一个字符时需要使用1-4字节,比如下面这样:

'L' -> 01001100 (1宇节,十六进制0x4C)
'啊'-> 111001011001010110001010 (3宇节,十六进制0xE5958A)

UTF-8只是Unicode字符集的一种编码方案,Unicode字符集可以采用UTF-8、UTF-16、UTF-32这几种编码方案。UTF-8使用1~4字节编码一个字符,UTF-16使用2 或4字节编码一个字符,UTF-32使用4字节编码一个字符。

对同一个字符,不同字符集可能采用不同的编码方式,对于汉字 ‘我’,ASCII字符集中没有这个字符,UTF-8和GB2312字符集对汉字 ’我’的编码方式不同

UTF-8 3字节,十六进制形式为 0xE68891
GB2312 2字节,十六进制形式为 0xCED2

MySQL中支持的字符集和比较规则

MySQL中的utf8和utf8mb4

MySQL定义了下面两个概念。

在MySQL中,utf8是utf8mb3的别名,所以后文在MySQL中提到utf8时,就意味着使用1~3字节来表示一个字符。如果有使用4字节编码一个字符的情况,比如存储一些emoji表情,请使用uf8mb4。

在MySQL8.0中,设计MySQL的大叔已经很大程度地优化了utf8mb4字符集的性能,而且已经将其设置为默认的字符集。

字符集的查看

查看当前MySQL中支持的字符集

SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式]
mysql> SHOW CHARSET;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| binary   | Binary pseudo charset           | binary              |      1 |
| cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
| cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
| cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
| cp850    | DOS West European               | cp850_general_ci    |      1 |
| cp852    | DOS Central European            | cp852_general_ci    |      1 |
| cp866    | DOS Russian                     | cp866_general_ci    |      1 |
| cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
| dec8     | DEC West European               | dec8_swedish_ci     |      1 |
| eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
| euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
| geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 |
| greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 |
| hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 |
| hp8      | HP West European                | hp8_english_ci      |      1 |
| keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 |
| koi8r    | KOI8-R Relcom Russian           | koi8r_general_ci    |      1 |
| koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 |
| latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
| latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 |
| latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 |
| macce    | Mac Central European            | macce_general_ci    |      1 |
| macroman | Mac West European               | macroman_general_ci |      1 |
| sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 |
| swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 |
| tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 |
| ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 |
| ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 |
| utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| utf8mb3  | UTF-8 Unicode                   | utf8mb3_general_ci  |      3 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_0900_ai_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.08 sec)

其中Default collation列表示这种字符集中一种默认的比较规则,Maxlen列,它代表这种字符集最多需要几个字节来表示一个字符。

比较规则的查看

查看MySQL中支持的比较规则

SHOW COLLATION [LIKE 匹配的模式];
mysql> SHOW COLLATION LIKE 'utf8%';
+-----------------------------+---------+-----+---------+----------+---------+---------------+
| Collation                   | Charset | Id  | Default | Compiled | Sortlen | Pad_attribute |
+-----------------------------+---------+-----+---------+----------+---------+---------------+
| utf8mb3_bin                 | utf8mb3 |  83 |         | Yes      |       1 | PAD SPACE     |
| utf8mb3_croatian_ci         | utf8mb3 | 213 |         | Yes      |       8 | PAD SPACE     |
| utf8mb3_czech_ci            | utf8mb3 | 202 |         | Yes      |       8 | PAD SPACE     |
| utf8mb3_danish_ci           | utf8mb3 | 203 |         | Yes      |       8 | PAD SPACE     |
| utf8mb3_esperanto_ci        | utf8mb3 | 209 |         | Yes      |       8 | PAD SPACE     |
...
...
117 rows in set (0.00 sec)
后缀    英文意义    描述
_ai     accent insensitive 不区分重音
_as     accent sensitive    区分重音
_ci     case insensitive    不区分大小写
_cs     case    sensitive   区分大小写
_bin    binary      以二进制方式比较

在执行 SHOW COLLATION语句后返回的结果中,Default列的值为YES的比较规则,就是该字符集的默认比较规则,比如utf8字符集默认的比较规则就是utf8_general_ci。

字符集和比较规则的应用

各级别的字符集和比较规则

MySQL有4个级别的字符集和比较规则,分别是服务器级别、数据库级别、表级别、列级别。

  1. 服务器级别

MySQL提供了两个系统变量表示服务器级别的字符集和比较规则

系统变量    描述
character_set_server    服务器级别的字符集
collation_server    服务器级别的比较规则
mysql> SHOW VARIABLES LIKE 'character_set_server';
+----------------------+---------+
| Variable_name        | Value   |
+----------------------+---------+
| character_set_server | utf8mb4 |
+----------------------+---------+
1 row in set (0.15 sec)

mysql> SHOW VARIABLES LIKE 'collation_server';
+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_0900_ai_ci |
+------------------+--------------------+
1 row in set (0.01 sec)

在启动服务器程序时,可以通过启动选项或者在服务器程序运行过程中使用SET语句来修改这两个变量的值。

  1. 数据库级别

在创建和修改数据库时可以指定该数据库的字符集和比较规则

CREATE DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称];
ALTER DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称];

其中DEFAULT可以省略。

如果想查看当前数据库使用的字符集和比较规则,可以查看两个系统变量的值,前提是使用USE语句选择当前的默认数据库。如果没有默认数据库,则变量与服务器级别下相应的系统变量具有相同的值。

系统变量    描述
character_set_database  当前数据库的字符集
collation_database  当前数据库的比较规则
mysql> use db_log;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW VARIABLES LIKE 'character_set_database';
+------------------------+---------+
| Variable_name          | Value   |
+------------------------+---------+
| character_set_database | utf8mb4 |
+------------------------+---------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'collation_database';
+--------------------+--------------------+
| Variable_name      | Value              |
+--------------------+--------------------+
| collation_database | utf8mb4_0900_ai_ci |
+--------------------+--------------------+
1 row in set (0.00 sec)

不能通过修改这两个变量的值来改变当前数据库的字符集和比较规则.

在数据库的创建语句中不指定字符集和比较规则,将使用服务器级别的字符集和比较规则作为数据库的字符集和比较规则。

  1. 表级别

可以在创建和修改表的时候指定表的字符集和比较规则

CREATE TABLE 表名(列的信息)
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称];
ALTER TABLE 表名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称];

如果创建表的语句中没有指明字符集和比较规则,贝u使用该表所在数据库的字符集和比较规则作为该袤的字符集和比较规则.

  1. 列级别
CREATE TABLE 表名(
    列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
    其他列...
);
ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];

在修改列的字符集时需要注意,如果列中存储的数据不能用修改后的字符集进行表示,则会发生错误.比如,最初使用的字符集是utf8,列中存储了一些汉字,现在把列的字符集转换为回CII的话就会出错,因为脑Cll字符集并不能表示汉字字符.

  1. 仅修改字符集或仅修改比较规则

由于字符集和比较规则之间相互关联,因此如果只修改字符集,比较规则也会跟着变化:如果只修改比较规则,字符集也会跟着变化.具体规则如下

  1. 各级别字符集和比较规则小结

对于给定的表,应该知道它的各个列的字符集和比较规则是什么,从而根据这个列的类型来确定每个列存储的实际数据所占用的存储空间大小。

mysql> INSERT INTO t(col) VALUES('我我');

如果列col使用的字符集是gbk,一个字符’我’在gbk中的编码为0xCED2,占用2字节,则两个字符就占用了4字节,如果把该列字符集修改为utf8,这两个字符实际占用的存储空间就是6字节了。

客户端和服务器通信过程中使用的字符集

  1. 编码和解码使用地字符集不一致

如果使用不同地字符集去解码这个字节序列,最后得到地结果可能让你挠头。

如UTF-8字符集下编码地字节序 0xE68891,程序A把字节序发送到程序B,程序B使用不同的字符集解码这个字节序列使用GBK字符集。

看第一个字节0xE6,它的值大于0x7F(127),说明待取字符是两字节编码,GBK编码表查找0xE688对应字符。
继续读0x91,值也大于0x7F试图读下一个字节,发现后面没有了,所以这是个半字符。

根本解不出来字符’我’。

  1. 字符集转换的概念

如果接收 0xE68891 这个字节序列的程序按照UTF-8字符集进行解码,然后又把它按照GBK字符集进行编码,则编码后的字节序列就是 0xCED2.我们把这个过程称为字符集的转换,也就是字符串’我’从UTF-8字符集转换为GBK字符集.

  1. MySQL中的字符集转换过程

用户角度看,客户端发送的请求以及服务器返回的响应都是一个字符串。机器角度看,客户端发送的请求和服务器返回的响应本质就是一个字节序列。

一般情况下,客户端编码请求字符串时使用的字符集与操作系统当前使用的字符集一致。

当使用类UNIX操作系统时,环境变量 LC_ALL LC_CTYPE LANG 的值决定了操作系统当前使用的是那种字符集。

优先级 LC_ALL > LC_CTYPE > LANG

root@kTY-HK3-QL-86139:/home/root/note# echo $LC_ALL
root@kTY-HK3-QL-86139:/home/root/note# echo $LC_CTYPE
root@kTY-HK3-QL-86139:/home/root/note# echo $LANG
C.UTF-8

获取类UNIX操作系统当前使用的字符集时,调用的是系统函数 nl_langinfo(CODESET), man 3 nl_langinfo 可以查看详情

服务器接收到的请求就是一个字节序列,服务器将这个字节序列看作是使用系统变量 character_set_client代表的字符集进行编码的字节序列,每个客户端与服务器建立连接后,服务器都会为该客户端维护一个单独的 character_set_client变量,这个变量是SESSION级别的。

加入客户端实际使用UTF-8字符集来编码请求的字符串,可以通过命令将character_set_client设置为latin1字符集

SET character_set_client=latin1;

服务器解析失败是会发出警告的。

mysql> SET character_set_client=ascii;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT '我';
+-----+
| ??? |
+-----+
| ??? |
+-----+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1300
Message: Cannot convert string '\xE6\x88\x91' from ascii to utf8mb4
1 row in set (0.00 sec)

服务器会将请求的字节序当作采用 character_set_client 对应的字符集进行编码的字节序,在真正处理请求时又会将其转换为使用SESSION级别的系统变量 charater_set_connection 对应的字符集进行编码的字节序列。

假如有一个表

CREATE TABLE tt(
    c VARCHAR(100)
) ENGINE=INNODB CHARSET=utf8;

列c采用字符集和表级别字符集utf8一致,这里采用默认的比较规则utf8_general_ci。

mysql> SELECT * FROM tt;
+--+
|c |
+--+
||
+--+

如果现在将 character_set_connection 和 collation_connection 的值分别设置为 gbk 和 gbk_chinese_ci

SELECT * FROM tt WHERE c = '我';

上面语句中’我’时gbk编码的,列c是utf8编码的,MySQL规定,在这种情况下,列的字符集和排序规则的优先级更高,这里需要将请求中的字符串 从gbk转换为utf8,然后再使用列c的比较规则utf8_genneral_ci进行比较。

服务器为客户端响应发送的结果编码,这取决于SESSION级别的系统变量 character_set_results的值。

系统变量    描述
character_set_client 服务器认为请求是按照该系统变量指定的字符集进行编码的
character_set_connection 服务器在处理请求时,会把请求字节序列从character_set_client转换为character_set_connection
character_set_results 服务器采用该系统变量指定的字符集对返回给客户端的字符串进行编码

每个MySQL客户端都维护着一个客户端默认字符集,客户端在启动时会自动检测所在操作系统当前使用的字符集,并按照一定的规则映射成MySQL支持的字符集,然后将该字符集作为客户端默认的字符集.

通常的情况是,操作系统当前使用什么字符集,就映射为什么字符集.但是总存在一些特殊情况.假如操作系统当前使用的是asCll字符集,则会被映射为MySQL支持的latinl字符集.如果MySQL不支持操作系统当前使用的字符集,则会将客户端默认的字符集设置为MySQL的默认字符集.

在MySQL5.7以及之前的版本中,MySQL的默认字符集为latin1,自MySQL8.0开始默认字符集为utf8mb4。

另外,如果在启动MySQL客户端时设置了default-cbaracter-set启动选项,那么服务器会忽视操作系统当前使用的字符集,直接将default-cbaracter-set启动选项中指定的值作为客户端的默认字符集。

在连接服务器时,客户端将默认的字符集信息与用户名、密码等信息一起发送给服务器,服务器接收到后将三个系统变量的值初始化为客户端的默认字符集。

SET NAMES charset_name; 
上面这条语句与下面这3条语句的效果一样:
set character_set_client = charset_name;
set character_set_connection = charset_name;
set character_set_results = charset_name;

SET NAMES 不会改变客户端实际使用的字符集。客户端需要自行确保发送的字符串与声明的字符集一致。
如果客户端的默认字符集与 SET NAMES 声明的字符集不一致,可能会导致字符编码问题。

为了避免字符集不一致的问题,建议在客户端连接时明确指定字符集。例如,在 MySQL 客户端连接时可以使用:

mysql --default-character-set=utf8mb4 -u username -p

或者在代码中设置字符集

<?php
$mysqli = new mysqli("localhost", "username", "password", "database");
$mysqli->set_charset("utf8mb4");

如果操作系统当前使用的字符集为UTF-8,在启动MySQL客户端时使用了--default-character-set=gbk,客户端默认字符集会被设置为gbk,服务器的character_set_results也会被设置为gbk,假设服务器发送 ‘我’,发送的为 gbk编码 0xCED2

对于类UNIX操作系统来说,会把接收到的字节序列(也就是0xCED2)直接写到黑框框中,并默认使用操作系统当前使用的字符集(UTF-8)来解释这个字符·很显然无法解释,所以我们在屏幕上看到的就是乱码。

比较规则的应用

大多数用于字符串字符排序

SELECT * FROM t ORDER BY col;

从一条记录说起InnoDB记录存储结构

准备工作

MySQL 服务器中负责对表中的数据进行读取和写入工作的部分是存储引擎,而服务器又支持不同类型的存储引擎,比如 InnoDB、MyISAM、MEMORY啥的。用的最多的就是InnoDB我们下面就学这个。

InnoDB页简介

InnoDB是一个将表中的数据存储在磁盘上的存储引擎,从磁盘中读到内存也是它做的事情。当我们想从表中获取某些记录时,InnoDB不可能一条条把记录从磁盘上读出来,InnoDB采取的方式是, 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位。InnoDB中页的大小一般为16KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

服务器运行过程中不可以更改页面大小。系统变量 innodb_page_size表明了InnoDB存储引擎中的页大小,默认值为16384,该变量只能在第一次初始化MySQL数据目录时指定。

InnoDB行格式

目前为止,设计了4中不同类型的行格式,分别为 COMPACT、REDUNDANT、DYNAMIC、COMPRESSED。

📌 行格式常见选项说明:

格式名 说明
COMPACT 默认格式,InnoDB 默认使用的行格式(5.0 以后)
REDUNDANT 老旧格式,仅用于兼容 MySQL 4.1 之前版本
DYNAMIC 支持更长的行,TEXT/BLOB 存储在页外,推荐使用
COMPRESSED 数据压缩存储,节省磁盘空间

指定行格式的语法

可以在创建或修改表的语句中指定记录所使用的行格式。

CREATE TABLE 表名(列的信息) ROW_FORMAT=行格式名称;
ALTER TABLE 表名 ROW_FORMAT=行格式名称。

例如

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    email VARCHAR(100)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
ALTER TABLE users ROW_FORMAT=COMPRESSED;

COMPACT行格式

一条完整的记录分为记录的额外信息和记录的真实数据两大部分。

COMPACT行格式示意图
  1. 记录的额外信息

分别是变长字段长度列表、NULL值列表和记录头信息。

变长字段长度列表

MySQL支持一些变长的数据类型,比如 VARCHAR(M)、VARBINARY(M)、各种TEXT类型、各种BLOB类型。这些变长字段占用的存储空间分为两部分。

在COMPACT行格式中,所有变长字段的真实数据占用的字节数都存放在记录的开头位置,从而形成一个变长字段长度列表,各变长字段的真实数据占用的字节数按照列的顺序逆序存放.再次强调一遍,是逆序存放!

例如有个表
c1 VARCHAR(10) | c2 VARCHAR(10) | c3 VARCHAR(10)
'aaaa'|'bbb'|'d'

实际占用字节数
0x04 0x03 0x01

在COMPACT行,头部变长信息部分就会存储

01 03 04

这三个字段的长度较短,其实际长度用一个字节就可以存储,但是长度大的话一个字就存不下了,InnoDB有一套字节的规则。

引入W、M和L这几个符号,先分别看看这些符号的意思.

InnoDB在读取记录的变长字段长度列表时会先看表结构,先查看表结构,如果变长字段允许存储的最大字节数不大于255,则直接认为使用1个字节表示真实数据占用的字节数。

NULL值列表

COMPACT行格式把一条记录中值为NULL的列统一管理起来,存储到NULL值列表中。

一个位就能表示一个字段是否为NULL,标记NOT NULL的列不用存标记位。在顺序按照列的顺序的逆序。高位补零。

NULL值列表

记录头信息

TODO

## 溢出列

## DYNAMIC行格式和COMPRESSED行格式

# 盛放记录的大盒子InnoDB数据页结构

# 快速查询的秘籍B+树索引

# B+树索引的使用

# MySQL的数据目录

# InnoDB的表空间

# 单表访问方法

# 连接原理

# 基于成本的优化

# InnoDB统计数据是如何收集的

# 基于规则的优化内含子查询优化二三事

# 查询优化的百科全书EXPLAIN详解

# optimizer trace的神奇功效

# InnoDB的Buffer Pool

# 事务简介

# redo日志

# undo日志

# 事务隔离级别和MVCC

# 

# MySQL相关工具使用

如mysqldump等