请简述InnoDB如何保证事务的持久化特性

在数据库运行时,为了保障整个数据库系统的正常运行,防止数据意外和不一致,数据库管理系统就要对数据库进行保护和管理。这些管理主要通过数据库的恢复、并发控制、完整性控制和安全性控制,进而引申出事务和事务的特性。

事务是构成单一逻辑工作单元的操作集合。为了保证数据库中的数据总是正确的,我们要求事务具有ACID的性质,也就是原子性、一致性、隔离性和持久性。

其中,事务的持久性就是指一个事务一旦完成全部操作之后,它对数据库的所有更新应永久地反映在数据库中,不会丢失。即使以后系统发生故障,也是如此。MySQL为了实现数据库ACID的要求,引入了支持事务的InnoDB引擎。

InnoDB一方面实现了对事务的实现,另一方通过redo log日志实现了事务的原子性和持久性。 InnoDB是事务的存储引擎,当数据操作提交commit时,必须先将该事务的所有日志写入到redo log文件进行持久化,待事务的commit操作完成才算完成。为了保证每次日志都要写入redo log,InnoDB都要调用fsync操作将日志写入磁盘。

MySQL的主从同步

MySQL从3.23版本开始提供复制的功能。复制是指将主数据库的DDL和DML操作通过二进制日志传到从数据库上,然后在从库上对这些日志redo,从而使得从库和主库的数据保持同步。


概述

  1. 主库在commit时把数据变更作为events记录在binlog中;
  2. 主库推送binlog到从库的relay_log,之后从库从relay_log执行redo,通过逻辑复制以达到主从数据一致。

MySQL通过三个线程来完成主从复制:
1. binlog dump线程在主库上运行,将数据操作推送给从库;
2. I/O线程跑在从库上,将binlog日志中的事件更新到relay_log中;
3. SQL线程跑在从库上,将relay_log中更新的数据库事件应用到从库。

架构

复制的常见架构有:
1. 一主多从复制,适用于读压力大,实现读写分离;
2. 多级复制架构,主要为了解决主库的I/O负载和网络压力,但延迟大;
3. 双主复制架构,适用于主从切换;


搭建

主库

操作系统为ubuntu,域名beijing.sujx.net,关闭ufw,开启vsftpd;

$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf 

server-id = 100
log_bin                 = /var/log/mysql/mysql-bin.log
bind-address            = 192.168.122.106

$ sudo systemctl restart mysql 
$ mysql -u root -p 
mysql> grant replication slave on *.* to replica@'%' identified by 'password'; 
mysql> flush privileges; 
mysql> flush tables with read lock; 

mysql> show master status\G
*************************** 1. row ***************************
File: **mysql-bin.000012**
Position: **1677**
Binlog_Do_DB: 
Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)
mysql> exit
mysqldump -uroot -p --all-databases --lock-all-tables --events > mysql_dump.sql
lftp sujx@shanghai.sujx.net
put mysql_dump.sql
$ mysql -u root -p 
mysql> unlock tables;

从库

操作系统为ubuntu,域名shanghai.sujx.net,关闭ufw,开启vsftpd;

$ mysql -u root -p < /tmp/mysql_dump.sql 
$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

server-id = 101
read_only=1
report-host=beijing.sujx.net

$sudo systemctl restart mysql 
$ mysql -u root -p 
mysql> change master to
-> master_host='beijing.sujx.net',
-> master_user='replica',
-> master_password='password',
-> master_log_file='mysql-bin.000012',
-> master_log_pos=1677;
mysql> SET GLOBAL sql_slave_skip_counter = 1;
mysql> start slave;
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: beijing.sujx.net
                  Master_User: replica
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000012
          Read_Master_Log_Pos: 1677
               Relay_Log_File: shanghai-relay-bin.000020
                Relay_Log_Pos: 1202
        Relay_Master_Log_File: mysql-bin.000012
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 1007
                   Last_Error: Error 'Can't create database 'sujx'; database exists' on query. Default database: 'sujx'. Query: 'create database sujx'
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 989
              Relay_Log_Space: 2147
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 1007
               Last_SQL_Error: Error 'Can't create database 'sujx'; database exists' on query. Default database: 'sujx'. Query: 'create database sujx'
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 100
                  Master_UUID: d84bbcff-a41f-11e7-a7e7-525400537531
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 170929 17:39:55
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

测试

在主库上面进行删库、建库、导入数据即可。

Tips

Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.
这个问题是因为mysql目录中的auto.conf中的UUID值相同,修改之后就可以正常。

Last_IO_Errno: 1045 Last_IO_Error: error connecting to master ‘replication@
这个问题是change master语句错误

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

IO和SQL线程显示正常却无法复制数据时,需要设置SET GLOBAL sql_slave_skip_counter Syntax跳过内部出错的地方。

Ubuntu上的ibdata1文件去哪里了?

在Ubuntu桌面版上使用apt安装MySQL之后,无论使用find还是locate都找不到MySQL的重要文件ibdata1。解决问题的方法很简单,就是切换到root账号之下。

$ locate ibdata1 
$ 
$ su
# locate ibdata1
# /var/lib/mysql/ibdata1

MySQL的数据文件位置

root@TC8304:/var/lib/mysql# ls
auto.cnf         ibdata1      ibtmp1              phpmyadmin  TC8304-slow.log
debian-5.7.flag  ib_logfile0  mysql               sujx
ib_buffer_pool   ib_logfile1  performance_schema  sys

MySQL的性能测试

常用性能测试工具

  • sysbench
  • tpcc-mysql
  • mysqlslap

性能衡量指标

  • 服务吞吐量(TPS\QPS)
    tps是每秒内的事务数,比如执行了dml操作,那么相应的tps会增加;
    qps是指每秒内查询次数,比如执行了select操作,相应的qps会增加。
  • 服务响应时间
  • 服务的并发性

sysbench

  • 业界知名
  • 支持磁盘、cpu和数据库
  • 支持多种数据库

测试准备

$ sysbench --test=oltp --oltp-table-size=1000000 --mysql-host=127.0.0.1 --mysql-user=sujx --mysql-db=sujx --mysql-password=****  prepare

执行测试

$ sysbench --num-threads=16 --max-requests=100000  --test=oltp --oltp-table-size=1000000 --oltp-read-only --mysql-host=127.0.0.1 --mysql-user=sujx --mysql-db=sujx --mysql-password=**** run

测试结果

No DB drivers specified, using mysql
Running the test with following options:
Number of threads: 16

Doing OLTP test.
Running mixed OLTP test
Doing read-only test
Using Special distribution (12 iterations,  1 pct of values are returned in 75 pct cases)
Using "BEGIN" for starting transactions
Using auto_inc on the id column
Maximum number of requests for OLTP test is limited to 100000
Threads started!
Done.

OLTP test statistics:
    queries performed:
        read:                            1400028
        write:                           0
        other:                           200004
        total:                           1600032
    transactions:                        100002 (1936.18 per sec.)
    deadlocks:                           0      (0.00 per sec.)
    read/write requests:                 1400028 (27106.57 per sec.)
    other operations:                    200004 (3872.37 per sec.)

Test execution summary:
    total time:                          51.6490s
    total number of events:              100002
    total time taken by event execution: 825.9599
    per-request statistics:
         min:                                  2.09ms
         avg:                                  8.26ms
         max:                                 20.08ms
         approx.  95 percentile:              11.48ms

Threads fairness:
    events (avg/stddev):           6250.1250/10.94
    execution time (avg/stddev):   51.6225/0.00

Tpcc-mysql

  1. Ubuntu下执行安装
$ sudo apt install -y git mysql-server libmysqlclient-dev  
$ git clone https://github.com/Percona-Lab/tpcc-mysql.git
$ cd tpcc-mysql/src
$ make 
$ export LD_LIBRARY_PATH=$MYSQL_HOME/lib
export C_INCLUDE_PATH=$MYSQL_HOME/include
export PATH=$MYSQL_HOME/bin:$PATH
mysql>create database tpcc;
mysql>source create_table.sql
mysql>source add_fkey_idx.sql
$ ./tpcc_load 127.0.0.1 sujx sujx ***** 5
./tpcc_start -h 127.0.0.1 -d sujx -u sujx -p ***** -w 5 -c 16 -r 1 -l 10 -i 1 -f report-mysql-tpcc.txt

输出结果

STOPPING THREADS................

<Raw Results>
  [0] sc:3243  lt:0  rt:0  fl:0 
  [1] sc:3235  lt:0  rt:0  fl:0 
  [2] sc:325  lt:0  rt:0  fl:0 
  [3] sc:323  lt:0  rt:0  fl:0 
  [4] sc:325  lt:0  rt:0  fl:0 
 in 10 sec.

<Raw Results2(sum ver.)>
  [0] sc:3243  lt:0  rt:0  fl:0 
  [1] sc:3242  lt:0  rt:0  fl:0 
  [2] sc:325  lt:0  rt:0  fl:0 
  [3] sc:323  lt:0  rt:0  fl:0 
  [4] sc:325  lt:0  rt:0  fl:0 

<Constraint Check> (all must be [OK])
 [transaction percentage]
        Payment: 43.42% (>=43.0%) [OK]
   Order-Status: 4.36% (>= 4.0%) [OK]
       Delivery: 4.33% (>= 4.0%) [OK]
    Stock-Level: 4.36% (>= 4.0%) [OK]
 [response time (at least 90% passed)]
      New-Order: 100.00%  [OK]
        Payment: 100.00%  [OK]
   Order-Status: 100.00%  [OK]
       Delivery: 100.00%  [OK]
    Stock-Level: 100.00%  [OK]

<TpmC>                 19458.000 TpmC

总结

  • IO测试要远大于内存、CPU测试要小于内存;
  • 测试时间应当大于60分钟;
  • sysbench倾向于测试MySQL性能,TPCC更接近业务;
  • 运行测试程序需要同时监控机器负载、MySQL各项监控指标

tips:
- tpcc的详细介绍和使用tpcc
- Sysbench的介绍和具体使用sysbench