🛢️ 第2章 初识MySQL
📄 一句话概括

安装向导问的每一个问题,最后都落到一个 my.ini 文件里。 所以装完想改配置,不用重装——改文件、重启服务。 最该动的一个参数是 innodb_buffer_pool_size,默认值对生产机来说太小。 另一个是字符集:utf8 只有三字节,utf8mb4 才是完整的四字节。

📝 本章和前面几章的关系

前面三篇都是「认识」,这一篇是本章唯一要动手的,所以标两星。 沙箱里没有 MySQL 服务端,所以「装」和「启停」只能讲清楚步骤,不能演。 但配置文件可以真写、真读、真算——这是本篇 Demo 的落点。 它会反复出现在后面每一章:字符集坑在第 5 章建表时咬人, 认证插件坑在第 24 章 JDBC 连接时咬人。

一、下载页上那两个选择

下载页最容易让人卡住的是两处:版本类型、安装包形式。

选项 选哪个 为什么
社区版/企业版 社区版 企业版要订阅,功能差异对学习没影响
MSI 安装包/ZIP 压缩包 新手选 MSI MSI 有向导,ZIP 要自己写配置、自己注册服务
完整安装/仅服务端 服务端加客户端 完整安装会搭上图形工具、示例库、文档

教材用的是 MSI 安装包,一路「下一步」,中途有几个页面要停一下。 那几个页面才是安装的重点,其余都是点「同意」和「下一步」。

二、向导那一路,到底在定什么

把向导页和它定的事对上,装完就不慌。

向导那一页 它在定什么 建议值
Choosing a Setup Type 装哪些组件 服务端加客户端
Type and Networking 端口、是否开机自启 3306,开机自启
Authentication Method 用哪种密码插件 默认加强版,老程序选兼容版
Accounts and Roles root 密码 记牢,别用六个 1
Windows Service 服务名、是否随系统启动 MySQL80
Apply Configuration 把上面所有答案写进 my.ini 到这里才真正落盘

Authentication Method 那一页最容易被随手点过去,也最容易在半年后咬人。 选了加强版(caching_sha2_password),5.7 时代的老驱动就连不上。 选兼容版(mysql_native_password)能兼容,但安全性差一截。 正路是选加强版,然后升级驱动。 临时救急的办法是把那个账号改回兼容版,别把它当成长期方案。

三、启动、连接、断开、停止

这四个动作里有两个人人都会混:断开和停止。

动作 Windows 上的命令 说明
启动 net start MySQL80 要管理员权限的命令行
连接 mysql -u root -p 回车后再输密码
断开 exit 或 quit 只退出你这个会话,服务还在跑
停止 net stop MySQL80 停服务,所有连接一起断

断开只关你自己那一个会话,别人照常用;停止是把整个服务停掉,谁都连不上。 「我明明连着,怎么突然断了」——先问是不是有人 net stop 了。 Linux 上对应的是 systemctl start mysqld 和 systemctl stop mysqld。 名字可能是 mysqld 也可能是 mysql,取决于安装包,systemctl list-units 一看就知道。 这一节沙箱里跑不了,没有服务端可启停,只能照着敲。

四、配置文件:一个 ini 管所有

my.ini 是 Windows 上的名字,Linux 上叫 my.cnf,格式一样:分节、键值对。 [mysqld] 段给服务端读,[client] 段给客户端读,两段可以同名不同义。

import configparser
import os

ini = """[mysqld]
port=3306
datadir=C:/mysql/data
character-set-server=utf8mb4
innodb_buffer_pool_size=128M
max_connections=151
"""
cp = configparser.ConfigParser()
cp.read_string(ini)
g = cp["mysqld"]

def mb(s):
    s = s.strip().upper()
    return int(s[:-1]) if s.endswith("M") else int(s[:-1]) * 1024

pool = mb(g["innodb_buffer_pool_size"])
mem = 8192
print("机器内存 %d MB,缓冲池 %d MB,占 %.1f%%" % (mem, pool, pool * 100.0 / mem))
print("经验值 50%%~75%%,建议 %d MB ~ %d MB" % (mem * 0.5, mem * 0.75))
print("连接数 %s,每个连接按 256 KB 算,光栈就要 %.0f MB"
      % (g["max_connections"], int(g["max_connections"]) * 256 / 1024.0))
print("datadir 在配置里是 %s,这台机器上存在吗:%s"
      % (g["datadir"], os.path.isdir(g["datadir"])))

这段代码算出两件事,都值得记住。 第一,128 MB 的缓冲池放在 8 GB 机器上只占 1.6%,远远不够。 第二,datadir 在文件里写着,不代表它真存在——这一行输出是 False, 因为这台机器上根本没装 MySQL。 缓冲池是 InnoDB 最重要的参数,经验值是物理内存的 50% 到 75%。 但它不是越大越好:留给操作系统不够,换页会把性能拖垮。 另一个常被忽略的是连接数:每个连接都要栈和缓冲区,151 个不算多,一千个就要按几百兆算。 连接数用完时的报错是 Too many connections,那时候连登录都困难。

五、命令行客户端怎么进

8.0 装完开始菜单里有个「MySQL 8.0 Command Line Client」,点开就要密码。 它其实就是替你执行了 mysql -u root -p,只是把路径配好了。 进去之后先做三件事,比急着建表有用。

想知道什么 敲什么
当前在哪个库 SELECT DATABASE();
服务端版本 SELECT VERSION();
字符集设成什么了 SHOW VARIABLES LIKE 'character_set%';

没选库的时候 SELECT DATABASE() 返回 NULL,这时候建表会报「没有数据库被选中」。 所以第一件事永远是 USE 库名;。 顺带记住两条退出方式:exit 和 quit 都行,效果一样。

六、动手 Demo:安装时那几个选项,最后都落在哪

这一课把 my.ini 真写出来、再读回来,看每个参数管什么。 顺带把 utf8 和 utf8mb4 的差别逐字符量一遍——这一处坑最多。

import configparser

ini = """[mysqld]
port=3306
basedir=C:/mysql
datadir=C:/mysql/data
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
default-storage-engine=INNODB
max_connections=151
innodb_buffer_pool_size=128M
"""
cp = configparser.ConfigParser()
cp.read_string(ini)
for k in ("port", "datadir", "character-set-server",
          "max_connections", "innodb_buffer_pool_size"):
    print("%-24s %s" % (k, cp["mysqld"][k]))

s = "机械键盘😀"
for ch in s:
    n = len(ch.encode("utf-8"))
    print("%s  U+%04X  %d 字节  utf8 %s"
          % (ch, ord(ch), n, "能存" if n <= 3 else "存不下"))

实跑输出:

--------------------------------------------------------------------
第七课:安装时那几个选项,到底在定什么
--------------------------------------------------------------------

  **装 MySQL 时向导问了一堆问题:端口、数据目录、字符集、服务名、是否加入 PATH。**
  **这些答案最后都落到一个配置文件里。这一课把那个文件真写出来、读回来,看每个参数管什么。**

  一、配置文件长什么样
  --------------------------------------------------------------------
  **一个 ini 两段:`[mysqld]` 是服务端读的,`[client]` 是客户端读的。**
  **同一个参数名在两段里可能意思不同——字符集就是。**

  参数                     填的值              它管什么
  ------------------------------------------------------------------------------------------
  port                     3306                服务端监听的端口,客户端默认连这个
  datadir                  C:/mysql/data       **数据放哪**,备份就是备这个目录
  character-set-server     utf8mb4             **服务端默认字符集**,建库不写就用它
  collation-server         utf8mb4_0900_ai_ci  排序规则,决定 `=` 比不比较大小写、怎么排中文
  default-storage-engine   INNODB              建表不写 `ENGINE` 时用哪个引擎
  max_connections          151                 **最大连接数**,超了报 `Too many connections`
  innodb_buffer_pool_size  128M                **InnoDB 的缓存池**,最重要的一个性能参数
  **第七行那个参数比前六个加起来都重要。**
  **它决定多少数据和索引能待在内存里——设小了,所有查询都在等磁盘。**

  二、utf8 和 utf8mb4,差别真的存在
  --------------------------------------------------------------------
  **教材专门有一节讲设置编码格式,因为这一处坑最多。**
  **MySQL 的 `utf8` 最多 3 字节,`utf8mb4` 才是完整的 4 字节 UTF-8。**

  字符  码点     UTF-8 字节数  utf8(3 字节) 存得下吗
  --------------------------------------------------
  机    U+673A   3             能
  械    U+68B0   3             能
  键    U+952E   3             能
  盘    U+76D8   3             能
  😀    U+1F600  4             不能
  **最后那个 emoji 占 4 字节,在 `utf8` 列里存进去会报 `Incorrect string value`。**
  **或者被截成问号,或者整条 INSERT 直接失败。**
  **8.0 起默认就是 `utf8mb4`,所以新库不会踩这个坑。**
  **但 5.7 建的库升上来还是 `utf8`,得手动改。**

  三、改字符集是有代价的
  --------------------------------------------------------------------
  **`ALTER DATABASE ... CHARACTER SET` 只改「以后新建的表」。**
  **已有的表还是老字符集。**
  **要改已有的表,得一张张 `ALTER TABLE ... CONVERT TO CHARACTER SET`。**
  **大表上这一步会锁表、会重写数据,几百 G 的表能跑一晚上。**

  改动            影响范围        要注意
  ------------------------------------------------------------------------
  改库默认字符集  只管以后建的表  老表不动,等于没改
  改表字符集      这张表全部列    **会重写整张表**,大表挑低峰做
  只改某一列      那一列          列与列之间比较时可能隐式转换,走不了索引
  **第三行是最隐蔽的:两张表字符集不同,join 的时候要做隐式转换。**
  **索引直接失效,这种情况 EXPLAIN 里看不出来,只能靠 SHOW CREATE TABLE 对。**

第一段把七个参数列成一张表,最该盯的是最后两行。 max_connections 和 innodb_buffer_pool_size 是唯二需要按机器配置调的, 其余大多填对一次就不用管。 第二段逐字符量字节数,量出那个 emoji 是 4 字节。 utf8 列只收 3 字节以内的字符,存进去会报 Incorrect string value,或者被截成问号。 注意这是 MySQL 的历史包袱:utf8 这个名字被占了,但它是残缺的。 第三段是升级时最费时间的活。 改库的默认字符集只管以后新建的表,老表一动不动。 要改老表得一张张 CONVERT TO CHARACTER SET,这一步会锁表、会重写数据。 几百 G 的表能跑一整晚,所以这件事得挑低峰做,还得先量清楚有多大。 最后那行「列与列之间字符集不同」最隐蔽。 两张表 join 时要做隐式转换,索引直接失效,而 EXPLAIN 里看不出来。 只能靠 SHOW CREATE TABLE 一张张对。

30 秒实验

  1. 把 CODE241 里的 128M 改成 4096M,再跑一遍。占比从 1.6% 变成 50%,正好落在经验区间的下沿。
  2. 把 max_connections 改成 1000,算一下光连接缓冲区要多少内存。按每连接 256 KB 算是 250 MB。 这就是为什么连接数不能随便调大——它吃的是内存,不是免费的。
  3. 想一想:为什么 datadir 不建议放在系统盘? 重装系统会连数据一起清掉,磁盘写满也会先拖死数据库。 生产上一般单独挂一块盘,datadir 指过去。

常见误区

  1. 「root 密码随便设,反正是本机。」 3306 一旦对外可达就是灾难,本机也可能被恶意程序扫。 更麻烦的是很多人拿它当跳板,从一台失守的机器横向到数据库。
  2. 「改完 my.ini 立刻生效。」 不会,要重启服务。 少数参数支持在线改(SET GLOBAL),但绝大多数要重启才读新值。
  3. 「utf8 就是 UTF-8。」 MySQL 的 utf8 最多三字节,utf8mb4 才是完整的四字节 UTF-8。 8.0 起默认已经是 utf8mb4,但老库升上来还是旧的。
自测三问(点开对答案) 1. 安装向导里选的那些,最后写在哪? —— **写在 `my.ini`(Linux 上是 `my.cnf`)里。** **所以改配置不用重装,改文件加重启服务就行。** 2. 「断开」和「停止」差在哪? —— **断开只退出你那个会话,服务照跑,别人不受影响。** **停止是把服务进程关掉,所有连接一起断,谁都连不上。** 3. 8 GB 内存的机器,缓冲池设多少合适?为什么不能拉满? —— **4 GB 到 6 GB,也就是 50% 到 75%。** **留不够给操作系统,内存会换页,反而更慢。**

下一篇是本章最后一节,也是教材里唯一讲「方法」的一节。 它给的三条太笼统,这一篇把它们落到具体动作上——其中最实用的一条是:先学会读报错。

相关笔记