1. 获取镜像

  1. # 查看可用的稳定版本
  2. sudo docker search mysql
  3. # 镜像容量:448MB
  4. sudo docker pull mysql:5.7
  5. # 镜像容量:287MB
  6. sudo docker pull percona/percona-xtradb-cluster:5.7
  7. sudo docker image ls |grep mysql
  8. sudo docker image ls |grep percona

2. MySQL单机版

2.1. 创建数据卷

  1. sudo mkdir -p /home/mysql/{conf,data,logs}
  2. # 赋予data目录读写权限
  3. sudo chmod 755 /home/mysql/data
  4. # MySQL配置文件(字符编码设置)
  5. vi /home/mysql/conf/my.cnf

内容如下:

  1. [client]
  2. default_character_set=utf8
  3. [mysqld]
  4. collation_server=utf8_general_ci
  5. character_set_server=utf8
  6. lower_case_table_names=1

2.2. 启动容器

  1. # 简单安装
  2. sudo docker run -d -p 3306:3306 --name mysql \
  3. -e MYSQL_ROOT_PASSWORD=123456 \
  4. mysql:5.7
  5. # 指定数据卷安装
  6. sudo docker run -d -p 3306:3306 --name mysql \
  7. --privileged=true \
  8. -v /home/mysql/conf:/etc/mysql/conf.d \
  9. -v /home/mysql/logs:/var/log/mysql \
  10. -v /home/mysql/data:/var/lib/mysql \
  11. -e MYSQL_ROOT_PASSWORD=123456 \
  12. mysql:5.7
  13. sudo docker start mysql
  14. sudo docker restart mysql
  15. sudo docker stop mysql
  16. sudo docker rm mysql
  17. sudo docker ps -a

2.3. 验证

  1. sudo docker exec -it mysql /bin/bash
  2. mysql -uroot -p123456
  3. mysql -h localhost -uroot -p123456
  4. mysql> SHOW VARIABLES LIKE 'character%'; -- 查看字符集
  5. mysql> create database testdb;
  6. mysql> use testdb;
  7. mysql> create table t1(id int,name varchar(20));
  8. mysql> insert into testdb.t1(id,name) values(1,'zhangsan');
  9. mysql> insert into testdb.t1(id,name) values(2,'李四'); -- 测试中文是否乱码
  10. mysql> select * from testdb.t1;

2.4. 远程登录(非root)

  1. CREATE USER bdapuser IDENTIFIED BY 'bdapuser';
  2. GRANT ALL ON *.* TO 'bdapuser'@'%';
  3. flush privileges;
  4. -- 查询用户是否创建成功
  5. select user,host from mysql.user;
  6. -- host里有“%”说明是远程用户,然后退出mysqldocker容器,并用客户端工具远程登录测试
  7. sudo docker exec -it mysql /bin/bash
  8. mysql -h localhost -ubdapuser -pbdapuser

3. MySQL集群(主从)

3.1. 创建数据卷

  1. sudo mkdir -p /datas/mysql-master/{conf,data,logs}
  2. sudo mkdir -p /datas/mysql-slave/{conf,data,logs}
  3. # 赋予data目录读写权限
  4. sudo chmod 755 /datas/mysql-master/data
  5. sudo chmod 755 /datas/mysql-slave/data
  6. # MySQL配置文件(主机配置)
  7. vi /datas/mysql-master/conf/my.cnf
  8. # MySQL配置文件(从机配置)
  9. vi /datas/mysql-slave/conf/my.cnf

内容如下(主机:mysql-master):

  1. [mysqld]
  2. ## 设置server_id,同一局域网中需要唯一
  3. server_id=101
  4. ## 指定不需要同步的数据库名称
  5. binlog-ignore-db=mysql
  6. ## 开启二进制日志功能
  7. log-bin=mall-mysql-bin
  8. ## 设置二进制日志使用内存大小(事务)
  9. binlog_cache_size=1M
  10. ## 设置使用的二进制日志格式(mixed,statement,row)
  11. binlog_format=mixed
  12. ## 二进制日志过期清理时间,默认值:0,表示不自动清理
  13. expire_logs_days=7
  14. ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断
  15. ## 如:1062-主键重复;1032-主从数据库数据不一致;
  16. slave_skip_errors=1062

内容如下(从机:mysql-slave):

  1. [mysqld]
  2. ## 设置server_id,同一局域网中需要唯一
  3. server_id=102
  4. ## 指定不需要同步的数据库名称
  5. binlog-ignore-db=mysql
  6. ## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
  7. log-bin=mall-mysql-slave1-bin
  8. ## 设置二进制日志使用内存大小(事务)
  9. binlog_cache_size=1M
  10. ## 设置使用的二进制日志格式(mixed,statement,row)
  11. binlog_format=mixed
  12. ## 二进制日志过期清理时间,默认值:0,表示不自动清理
  13. expire_logs_days=7
  14. ################# 以下为从节点特殊配置项 #################
  15. ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断
  16. ## 如:1062-主键重复;1032-主从数据库数据不一致;
  17. slave_skip_errors=1062
  18. ## 从节点配置中继日志
  19. relay_log=mall-mysql-relay-bin
  20. ## 1:表示从节点将复制事件写进自己的二进制日志
  21. log_slave_updates=1
  22. ## 从节点设置为只读(具有super权限的用户除外)
  23. read_only=1

3.2. 启动容器

  • MySQL主节点(master) ```bash sudo docker run -d -p 3307:3306 —name mysql-master \ —privileged=true \ -v /datas/mysql-master/conf:/etc/mysql/conf.d \ -v /datas/mysql-master/logs:/var/log/mysql \ -v /datas/mysql-master/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:5.7

sudo docker start mysql-master sudo docker restart mysql-master sudo docker stop mysql-master sudo docker rm mysql-master


- **MySQL从节点(slave)**
```bash
sudo docker run -d -p 3308:3306 --name mysql-slave \
  --privileged=true \
  -v /datas/mysql-slave/conf:/etc/mysql/conf.d \
  -v /datas/mysql-slave/logs:/var/log/mysql \
  -v /datas/mysql-slave/data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:5.7

sudo docker start mysql-slave
sudo docker restart mysql-slave
sudo docker stop mysql-slave
sudo docker rm mysql-slave

3.3. 主从配置

  1. 主节点创建数据同步用户。 ```bash sudo docker exec -it mysql-master /bin/bash mysql -uroot -p123456

mysql> CREATE USER ‘slave’@’%’ IDENTIFIED BY ‘123456’; mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO ‘slave’@’%’; mysql> flush privileges;

mysql> show master status; — 在主数据库中查看主从同步状态,获取FilePosition +———————————-+—————+———————+—————————+—————————-+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +———————————-+—————+———————+—————————+—————————-+ | mall-mysql-bin.000003 | 769 | | mysql | | +———————————-+—————+———————+—————————+—————————-+


2. 从节点配置主从复制。
```bash
sudo docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456

命令如下:

change master to master_host='192.168.56.101', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000003', master_log_pos=769, master_connect_retry=30;

参数说明:

master_host:主数据库的IP地址。
master_port:主数据库的运行端口。
master_user:在主数据库创建的用于同步数据的用户账号。
master_password:在主数据库创建的用于同步数据的用户密码。
master_log_file:指定从数据库要复制数据的日志文件,注意:通过查看主数据的状态,获取File参数。
master_log_pos:指定从数据库从哪个位置开始复制数据,注意:通过查看主数据的状态,获取Position参数。
master_connect_retry:连接失败重试的时间间隔,单位为秒。
  1. 从节点开启主从同步。 ```bash sudo docker exec -it mysql-slave /bin/bash mysql -uroot -p123456

mysql> start slave; — 从节点开启主从同步 mysql> show slave status; — 查看从数据库状态(表方式展现) mysql> show slave status \G; — 查看从数据库状态(纵向展示)

Slave_IO_RunningSlave_SQL_Running均为“Yes”时表示主从同步已开始工作

* . row ** *: Slave_IO_Running: Yes Slave_SQL_Running: Yes *: *: *

<a name="bU5Ez"></a>
## 3.4. 验证
```bash
sudo docker ps -a

# 主库
sudo docker exec -it mysql-master /bin/bash
mysql -uroot -p123456

mysql> create database testdb; -- 1. 主库创建数据库
mysql> use testdb;
mysql> create table t1(id int,name varchar(20)); -- 2. 主库创建表
mysql> insert into testdb.t1(id,name) values(1,'zhangsan'); -- 3. 主库添加记录

# 从库
sudo docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456

mysql> show databases; -- 4. 从库验证数据库是否同步成功
mysql> use testdb;
mysql> show tables; -- 5. 从库验证表是否同步成功(创建表)
mysql> select * from testdb.t1; -- 6. 从库验证表是否同步成功(表添加记录)

4. MySQL集群(双主)

4.1. 创建数据卷

sudo mkdir -p /datas/mysql-master-A/{conf,data,logs}
sudo mkdir -p /datas/mysql-master-B/{conf,data,logs}

# 赋予data目录读写权限
sudo chmod 755 /datas/mysql-master-A/data
sudo chmod 755 /datas/mysql-master-B/data

# MySQL配置文件(主机配置)
vi /datas/mysql-master-A/conf/my.cnf
# MySQL配置文件(从机配置)
vi /datas/mysql-master-B/conf/my.cnf

内容如下(主机:mysql-master-A):

[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id = 1
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin= mysql-bin
## 指定不需要同步的数据库名称
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
## 0代表读写均可
read-only=0
## 配置中继日志
relay_log=mysql-relay-bin
log-slave-updates=on
auto-increment-offset=1
auto-increment-increment=2

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

内容如下(主机:mysql-master-B):

[mysqld]
server_id = 2
log-bin= mysql-bin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema

read-only=0
relay_log=mysql-relay-bin
log-slave-updates=on
auto-increment-offset=2
auto-increment-increment=2

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

4.2. 启动容器

  • MySQL主节点(master-A) ```bash sudo docker run -d -p 3317:3306 —name mysql-master-A \ —privileged=true \ -v /datas/mysql-master-A/conf/my.cnf:/etc/mysql/my.cnf \ -v /datas/mysql-master-A/logs:/var/log/mysql \ -v /datas/mysql-master-A/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:5.7

sudo docker start mysql-master-A sudo docker restart mysql-master-A sudo docker stop mysql-master-A sudo docker rm mysql-master-A


- **MySQL主节点(master-B)**
```bash
sudo docker run -d -p 3318:3306 --name mysql-master-B \
  --privileged=true \
  -v /datas/mysql-master-B/conf/my.cnf:/etc/mysql/my.cnf \
  -v /datas/mysql-master-B/logs:/var/log/mysql \
  -v /datas/mysql-master-B/data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:5.7

sudo docker start mysql-master-B
sudo docker restart mysql-master-B
sudo docker stop mysql-master-B
sudo docker rm mysql-master-B

4.3. 多主配置

  1. 主节点(master-A)创建数据同步用户。 ```bash sudo docker exec -it mysql-master-A /bin/bash mysql -uroot -p123456

mysql> CREATE USER ‘backup’@’%’ IDENTIFIED BY ‘123456’; mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO ‘backup’@’%’; mysql> flush privileges;

mysql> show master status; — 查看同步状态,获取FilePosition +—————————+—————+———————+—————————+—————————-+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +—————————+—————+———————+—————————+—————————-+ | mysql-bin.000003 | 786 | | | | +—————————+—————+———————+—————————+—————————-+


2. 主节点(master-B)配置主从复制。
```bash
sudo docker exec -it mysql-master-B /bin/bash
mysql -uroot -p123456

命令如下:

change master to master_host='192.168.56.101', master_user='backup', master_password='123456', master_port=3317, master_log_file='mysql-bin.000003', master_log_pos=786, master_connect_retry=30;

参数说明:

master_host:主数据库的IP地址。
master_port:主数据库的运行端口。
master_user:在主数据库创建的用于同步数据的用户账号。
master_password:在主数据库创建的用于同步数据的用户密码。
master_log_file:指定从数据库要复制数据的日志文件,注意:通过查看主数据的状态,获取File参数。
master_log_pos:指定从数据库从哪个位置开始复制数据,注意:通过查看主数据的状态,获取Position参数。
master_connect_retry:连接失败重试的时间间隔,单位为秒。
  1. 主节点(master-B)创建数据同步用户。 ```bash sudo docker exec -it mysql-master-B /bin/bash mysql -uroot -p123456

mysql> CREATE USER ‘backup’@’%’ IDENTIFIED BY ‘123456’; mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO ‘backup’@’%’; mysql> flush privileges;

mysql> show master status; — 查看同步状态,获取FilePosition +—————————+—————+———————+—————————+—————————-+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +—————————+—————+———————+—————————+—————————-+ | mysql-bin.000003 | 786 | | | | +—————————+—————+———————+—————————+—————————-+


4. 主节点(master-B)开启主从同步。
```bash
sudo docker exec -it mysql-master-B /bin/bash
mysql -uroot -p123456

mysql> start slave; -- 从节点开启主从同步
mysql> show slave status; -- 查看从数据库状态(表方式展现)
mysql> show slave status \G; -- 查看从数据库状态(纵向展示)
## `Slave_IO_Running`和`Slave_SQL_Running`均为“Yes”时表示主从同步已开始工作
*************************** *. row ***************************
            *****************: ****
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
            *****************: ****
*****************************: *******************************
  1. 主节点(master-A)配置主从复制。

    sudo docker exec -it mysql-master-A /bin/bash
    mysql -uroot -p123456
    

    命令如下:

    change master to master_host='192.168.56.101', master_user='backup', master_password='123456', master_port=3318, master_log_file='mysql-bin.000003', master_log_pos=786, master_connect_retry=30;
    
  2. 主节点(master-A)开启主从同步。 ```bash sudo docker exec -it mysql-master-A /bin/bash mysql -uroot -p123456

mysql> start slave; — 从节点开启主从同步 mysql> show slave status; — 查看从数据库状态(表方式展现) mysql> show slave status \G; — 查看从数据库状态(纵向展示)

Slave_IO_RunningSlave_SQL_Running均为“Yes”时表示主从同步已开始工作

* . row ** *: Slave_IO_Running: Yes Slave_SQL_Running: Yes *: *: *

<a name="AHfLE"></a>
## 4.4. 验证
```bash
sudo docker ps -a

############### 测试1:master-A中创建库表,master-B中验证 ###############

# 主库(master-A)
sudo docker exec -it mysql-master-A /bin/bash
mysql -uroot -p123456

mysql> create database testdbA; -- 1. 主库(master-A)创建数据库
mysql> use testdbA;
mysql> create table tA(id int,name varchar(20)); -- 2. 主库(master-A)创建表
mysql> insert into testdbA.tA(id,name) values(1,'zhangA'); -- 3. 主库(master-A)添加记录

# 主库(master-B)
sudo docker exec -it mysql-master-B /bin/bash
mysql -uroot -p123456

mysql> show databases; -- 4. 主库(master-B)验证数据库是否同步成功
mysql> use testdbA;
mysql> show tables; -- 5. 主库(master-B)验证表是否同步成功(创建表)
mysql> select * from testdbA.tA; -- 6. 主库(master-B)验证表是否同步成功(表添加记录)

############### 测试2:master-B中创建库表,master-A中验证 ###############

# 主库(master-B)
sudo docker exec -it mysql-master-B /bin/bash
mysql -uroot -p123456

mysql> create database testdbB; -- 1. 主库(master-B)创建数据库
mysql> use testdbB;
mysql> create table tB(id int,name varchar(20)); -- 2. 主库(master-B)创建表
mysql> insert into testdbB.tB(id,name) values(1,'zhangB'); -- 3. 主库(master-B)添加记录

# 主库(master-A)
sudo docker exec -it mysql-master-A /bin/bash
mysql -uroot -p123456

mysql> show databases; -- 4. 主库(master-A)验证数据库是否同步成功
mysql> use testdbB;
mysql> show tables; -- 5. 主库(master-A)验证表是否同步成功(创建表)
mysql> select * from testdbB.tB; -- 6. 主库(master-A)验证表是否同步成功(表添加记录)

4.5. VIP配置(选)

此处使用Ngnix实现双主MySQL的高可用和负载均衡。Nginx安装参考:《Docker-Standalone_Nginx-1.16.1》。
创建Nginx容器:

sudo docker run -itd --name nginx-mysql -p 80:80 -p 3306:3306 nginx-stream:1.16.1

修改Ngnix配置:

# 进入终端
sudo docker exec -it nginx-mysql /bin/bash
vi /apps/nginx/conf/nginx.conf

内容如下(新增):

# TCP/UPD协议配置,与HTTP同级,需安装nginx-stream
stream {
    server {
       listen 3306;
       proxy_pass mysql;    
    }
    upstream mysql {
       server 192.168.56.101:3317;
       server 192.168.56.101:3318;
    }
}

重启容器:

sudo docker restart nginx-mysql

之后可以使用MySQL工具进行连接测试。

5. MySQL集群(MMM、MHA、MGR)

略。

6. MySQL集群(PXC)

6.1. 配置网络信息

sudo docker network create --subnet=172.30.0.0/24 mysql-pxc
sudo docker network list

6.2. 创建存储卷

sudo docker volume create v1
sudo docker volume create v2
sudo docker volume create v3

6.3. 拉取pxc集群镜像

sudo docker pull percona/percona-xtradb-cluster:5.7

# 标记本地镜像,将其归入某一仓库(可选)
sudo docker tag percona/percona-xtradb-cluster:5.7 mysql-pxc:5.7

6.4. 创建容器

  • 第1个节点(初始化1个MySQL节点)

    sudo docker run -d -p 13306:3306 -v v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc --name=pxc_node1 --net=mysql-pxc --ip 172.30.0.2 mysql-pxc:5.7
    
  • 第2个节点(CLUSTER_JOIN:加入之前初始化的MySQL节点)

    sudo docker run -d -p 13307:3306 -v v2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc --name=pxc_node2 -e CLUSTER_JOIN=pxc_node1 --net=mysql-pxc --ip 172.30.0.3 mysql-pxc:5.7
    
  • 第3个节点(CLUSTER_JOIN:加入之前初始化的MySQL节点)

    sudo docker run -d -p 13308:3306 -v v3:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc --name=pxc_node3 -e CLUSTER_JOIN=pxc_node1 --net=mysql-pxc --ip 172.30.0.4 mysql-pxc:5.7
    

    注意:容器的启动应该顺次进行,当启动第1个结点时,需要初始化元数据库mysql信息,这个过程需要等待一段时间,才能进行后续操作。可以通过MySQL客户端工具的连接来判断是否初始完成。

    6.5. 验证

    ```bash sudo docker ps -a

######### 测试1:pxc_node1上创建库表,pxc_node2、pxc_node3上验证

节点(pxc_node1)

sudo docker exec -it pxc_node1 /bin/bash mysql -uroot -p123456

mysql> create database testdb1; — 1. 节点(pxc_node1)创建数据库 mysql> use testdb1; mysql> create table t1(id int,name varchar(20),primary key(id)); — 2. 节点(pxc_node1)创建表(pxc需要每个库表都有主键) mysql> insert into testdb1.t1(id,name) values(1,’pxc_node1’); — 3. 节点(pxc_node1)添加记录

节点(pxc_node2)

sudo docker exec -it pxc_node2 /bin/bash mysql -uroot -p123456

mysql> show databases; — 4. 节点(pxc_node2)验证数据库是否同步成功 mysql> use testdb1; mysql> show tables; — 5. 节点(pxc_node2)验证表是否同步成功(创建表) mysql> select * from testdb1.t1; — 6. 节点(pxc_node2)验证表是否同步成功(表添加记录)

节点(pxc_node3)

sudo docker exec -it pxc_node3 /bin/bash mysql -uroot -p123456

mysql> show databases; — 7. 节点(pxc_node3)验证数据库是否同步成功 mysql> use testdb1; mysql> show tables; — 8. 节点(pxc_node3)验证表是否同步成功(创建表) mysql> select * from testdb1.t1; — 9. 节点(pxc_node3)验证表是否同步成功(表添加记录)

######### 测试2:pxc_node2上创建库表,pxc_node1、pxc_node3上验证

节点(pxc_node2)

sudo docker exec -it pxc_node2 /bin/bash mysql -uroot -p123456

mysql> create database testdb2; — 1. 节点(pxc_node2)创建数据库 mysql> use testdb2; mysql> create table t2(id int,name varchar(20),primary key(id)); — 2. 节点(pxc_node2)创建表(pxc需要每个库表都有主键) mysql> insert into testdb2.t2(id,name) values(1,’pxc_node2’); — 3. 节点(pxc_node2)添加记录

节点(pxc_node1)

sudo docker exec -it pxc_node1 /bin/bash mysql -uroot -p123456

mysql> show databases; — 4. 节点(pxc_node1)验证数据库是否同步成功 mysql> use testdb2; mysql> show tables; — 5. 节点(pxc_node1)验证表是否同步成功(创建表) mysql> select * from testdb2.t2; — 6. 节点(pxc_node1)验证表是否同步成功(表添加记录)

节点(pxc_node3)

sudo docker exec -it pxc_node3 /bin/bash mysql -uroot -p123456

mysql> show databases; — 7. 节点(pxc_node3)验证数据库是否同步成功 mysql> use testdb2; mysql> show tables; — 8. 节点(pxc_node3)验证表是否同步成功(创建表) mysql> select * from testdb2.t2; — 9. 节点(pxc_node3)验证表是否同步成功(表添加记录)

<a name="Y16dJ"></a>
## 6.6. 启停容器
```bash
# 启动pxc集群
sudo docker start pxc_node1
sudo docker start pxc_node2
sudo docker start pxc_node3

# 重启pxc集群
sudo docker restart pxc_node1
sudo docker restart pxc_node2
sudo docker restart pxc_node3

# 停止pxc集群
sudo docker stop pxc_node1
sudo docker stop pxc_node2
sudo docker stop pxc_node3

# 删除pxc集群
sudo docker rm pxc_node1
sudo docker rm pxc_node2
sudo docker rm pxc_node3

参考

博客园:Docker Mysql数据库主从同步配置方法
https://www.cnblogs.com/jinjiangongzuoshi/p/9299275.html
博客园:Docker Mysql数据库双主同步配置方法
https://www.cnblogs.com/jinjiangongzuoshi/p/9299567.html
CSDN:Docker学习二-MySQL双主互备
https://blog.csdn.net/rajayu/article/details/88840234
简书:MySQL双主双从配置-Docker
https://www.jianshu.com/p/19fde522df97
B站(专栏):docker环境下构建mysql数据库PXC集
https://www.bilibili.com/read/cv11651516