MySQL 2025-10-25 · 15 min 阅读

MySQL 基础篇


mysql环境准备

启动一个mysql容器,对外端口号是3316,root用户默认的登陆密码是root:

docker run -p 3316:3306 --name mysql5.7 -v /Users/madong/opt/mysql5_7/conf:/etc/mysql/conf.d -v /Users/madong/opt/mysql5_7/logs:/logs -v /Users/madong/opt/mysql5_7/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
# 进入到mysql容器中,查看当前的mysql的连接信息
docker exec -it mysql5.7 /bin/bash
# 查看数据库的连接情况,当前的mysql实例有一个连接对象:
mysql> show processlist;
#+----+------+-----------+------+---------+------+----------+------------------+
#| Id | User | Host      | db   | Command | Time | State    | Info             |
#+----+------+-----------+------+---------+------+----------+------------------+
#|  2 | root | localhost | NULL | Query   |    0 | starting | show processlist |
#+----+------+-----------+------+---------+------+----------+------------------+
#1 row in set (0.00 sec)

mysql组件概览

大体来说,MySQL可以分为Server层和存储引擎两部分。其中,Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能。而存储引擎层负责数据的存储和提取,现常用的存储引擎是InnoDB。

mysql中的日志模块,redo log、binlog以及两阶段提交?拿更新语句分析,在专栏中,作者拿”孔乙己”中记账粉板来类比,MySQL中的WAL也就是先写日志,然后再写磁盘。

mysql> update T set c=c+1 where ID=2;

当有一条记录更新时,InnoDB引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。InnoDB的redo log是固定大小的,可以配置一组4个文件,每个文件的大小是1GB。

在下图中,write pos是当前记录的位置,checkpoint是当前要擦除的位置,当write pos追上checkpoint时,表示”粉板”满了,这时候不能再执行新的更新。

在server层也有自己的日志,称为binlog(归档日志),经常问的redo log和bin log的区别有哪些?

mysql的两阶段提交,是为了让redo log和binlog保持一致。具体步骤为,mysql中有这一行先写入redolog中,处于prepare阶段,然后写binlog,写完后提交事务,处于commit状态。

扩展,binlog和redolog的写入机制:

binlog写入,事务执行过程中,会先把日志写到binlog cache,事务提交的时候,再把binlog cache写入binlog文件中。 每个线程都有自己的binlog cache,但是他们共用一份binlog文件。

redo log也是先写redo log buffer,然后写到page cache里面(上图黄色部分),然后持久化到磁盘上。InnoDB对innodb_flush_log_at_trx_commit提供了3种取值:

mysql日志的组提交机制,LSN是单调递增的,用来对应redo log的一个个写入点。如下图,是三个并发事务(trx1, trx2, trx3)在prepare阶段,都写完redo log buffer,持久化到磁盘的过程,对应的LSN分别是50、120和160。

trx1是第一个到达的,会被选为这组的leader,像写redo log和binlog都会以组为单位,因而,一次组提交里面,组员越多,节约磁盘IOPS的效果越好。

若MySQL遇到了性能瓶颈,并且是在IO上,则可以考虑调参:

  1. 设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数。
  2. 将sync_binlog设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
  3. 将innodb_flush_log_at_trx_commit设置为2(写page cache,避免丢redo log)。这样做的风险是,主机掉电的时候会丢数据。

mysql的事务

提到事务,肯定会想到ACID(Atomitity、Consistency、Isolation、Durability),即原子性、一致性、隔离性、持久性。MySQL事务的隔离级别分为:读未提交、 读已提交、可重复读和串行化。

在”可重复读”级别下,视图是在事务启动时创建,整个事务存在期间都用这个视图。在”读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候创建。”读未提交”隔离 级别直接返回记录的最新值,没有视图的概念;而”串行化”隔离级别下直接用加锁的方式来避免并行访问。

在mysql中,每条记录在更新时都会同时记录一条回滚操作,记录上的最新值,通过回滚操作,都可以得到钱一个状态的值。一个值从1被按顺序改成了2、3、4,在回滚日志里面会有类似下面的记录。

在视图A、B、C里面,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。回滚日志,在不需要的时候才删除,也就是说,当没有事务 再需要用到这些回滚日志时,回滚日志会被删除。

事务到底是隔离的还是不隔离的?

如下启动3终端中执行操作,事务A和事务B查询的值是多少呢?若想要马上启动一个事务,可以使用start transaction with consistent snapshot这个命令。

实际执行下来,事务A看到k的值为1,事务B看到k的值为3。分析原因?

快照在MVCC里面是如何工作的? InnoDB中每个事务都有一个唯一的事务ID,叫做transaction id。它是在事务开始的时候向InnoDB申请的,是按申请顺序严格递增的。

每行数据也都是有多个版本,每次更新事务数据时,都会生成一个新的数据版本,并把transaction_id赋值给这个数据版本的事务ID,记为row trx_id。

如下图,一个数据版本的row trx_id,有以下几种可能:

更新数据规则: 更新数据都是先读后写的,而这个读,只能读当前的值,称为”当前读”(current read),除了update语句外,select语句加锁,也是当前读。

# lock in share mode和for update都是当前读 
select k from t where id = 1 lock in share mode;
select k from t where id = 1 for update;
# MySQL# 大数据