zoukankan      html  css  js  c++  java
  • 12 数据备份

    备份介绍

    为何要备份数据

    在生产环境中,我们数据库可能会遭遇各种各样的不测,从而导致数据丢失,大致可以分为以下几种

    硬件故障
    软件故障
    自然灾害
    黑客攻击
    误操作(占比最大)
    

    我们要备份什么

    数据
    二进制日志,innodb事务日志
    代码(存储过程、存储函数、触发器、事件调度器)
    服务器配置文件
    

    备份的类型

    • 冷备

      当数据库进行备份时,数据库不能进行读写操作,即数据库要下线

    • 温备

      不停库、不停服来进行备份,会锁表,阻止用户的写入,即当数据库进行备份时,数据库的读操作可以正常执行,但是不可以执行写操作

    • 热备(建议):不停库、不停服来进行备份,也不会锁表阻止用户的写入,即当数据库进行备份的时候,数据库的读写操作均不是受影响

    备份的工具

    在这里插入图片描述

    设计备份策略

    备份数据的策略要根据不同的应用场景进行定制, 大致有几个参考数值, 我们可以根据这些数值从而定制符合特定环境中的数据备份策略。

    能够容忍丢失多少数据
    恢复数据需要多长时间
    需要恢复哪一些数据

    三种备份策略及应用场景

    针对不同的场景下, 我们应该制定不同的备份策略对数据库进行备份, 一般情况下, 备份策略一般为以下三种:

    直接cp,tar复制数据库文件(基本上可以忽略这一种了,“手动狗头”)
    mysqldump+复制bin logs
    lvm2快照+复制bin logs
    xtrabackup
    以上的几种解决方案分别针对于不同的场景

    如果数据量较小, 可以使用第一种方式, 直接复制数据库文件
    如果数据量还行, 可以使用第二种方式, 先使用 mysqldump对数据库进行完全备份, 然后定期备份binary log达到增量备份的效果
    如果数据量一般, 而又不过分影响业务运行, 可以使用第三种方式, 使用lvm2的快照对数据文件进行备份, 而后定期备份binary log达到增量备份的效果
    如果数据量很大, 而又不过分影响业务运行, 可以使用第 四种方式, 使用xtrabackup进行完全备份后, 定期使用xtrabackup进行增量备份或差异备份

    备份实战

    使用mysqldump + 复制binary logs备份

    mysqldump命令使用

    语法:
    mysqldump -h 服务器 -u 用户名 -p密码 选项与参数 > 备份文件.sql
    
    选项与参数:
    -A/--all-databases 					所有库
    -B/--databases bbs db1 db2 			多个数据库
    db1 								数据库名
    db1 t1 t2 							db1数据库的表t1、t2
    -F 									备份的同时刷新bin log
    -R 									备份存储过程和函数数据(如果开发写了函数和存储过程,就备,没写就不备)
    --triggers 							备份触发器数据(现在都是开发写触发器)
    -E/--events 						备份事件调度器
    -d 									仅表结构
    -t 									仅数据
    
    --master-data=1 				
    备份文件中 change master语句是没有注释的,默认为1
    用于已经制作好了主从,现在想扩展一个从库的时候使用。
    如此备份,扩展添加从库时导入备份文件后便不需要再加mater_pos了。
    change matser to
    master_host='10.0.0.111'
    master_user='rep'
    master_password=123
    master_log_pos=120
    master_log_file='master-bin.000001'
    
    --master-data=2 
    备份文件中change master语句是被注释的
    
    --lock-all-tables, 提交请求锁定所有数据库中的所有表,以保证数据的一致性。
    这是一个全局读锁,并且自动关闭--single-transaction和--lock-tables选项,他们是互斥的。
    对于支持事务的表,例如InnoDB和BDB,推荐使用--single-transaction选项,因为它根本不需要锁定表。
    
    --single-transaction: 快照备份 (搭配--master-data可以做到热备)
    该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎,仅InnoDB。
    本选项和--lock-tables选项是互斥的,不能同时存在,因为LOCK TABLES会使任何挂起的事务隐式提交。
    
    # 完整语句
    mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction > /tmp/full.sql
    
    # 文件太大时可以压缩 gzip ,但是gzip不属于mysql独有的命令,可以利用管道
    # --master-data=2 --single-transaction 这两个选项一起使用等于热备
    mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction | gzip >/tmp/full$(date +%F).sql.gz
    
    # 导出时压缩了,导入时需要解压,可以使用zcat命令,很方便
    zcat /tmp/full$(date +%F).sql.gz | mysql -uroot -p123
    

    案例

    1. 先打开binlog日志
    [root@db01 ~]# vim /etc/my.cnf
    [mysqld]
    server_id=1
    log-bin=/service/mysql/mybinlog
    binlog_format='row'		 	# (row,statement,mixed)
    binlog_rows_query_log_events=on
    max_binlog_size=100M
    
    # 重启数据库
    [root@db01 ~]# systemctl restart mysql
    
    2. 登录数据库,插入测试数据
    mysql> create database db01;
    Query OK, 1 row affected (0.00 sec)
    
    mysql> use db01;
    Database changed
    
    mysql> create table t1(id int);
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert t1 values(1),(2),(3);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    3. 在命令行执行命令,进行全量备份
    [root@db01 ~]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction | gzip > /tmp/full.sql.gz
    Warning: Using a password on the command line interface can be insecure.
    
    4. 在命令行执行命令,刷新bin log,便于日后查找(或者直接在上一条sql语句中加上-F参数也可以)
    [root@db01 ~]# mysql -uroot -p123 -e "flush logs"
    
    5. 登录数据库,再插入一些数据,模拟增量,这些数据写入了新的binlog
    mysql> use db01;
    Database changed
    
    mysql> insert t1 values(4),(5),(6);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    模拟数据丢失
    mysql> drop database db01;
    Query OK, 1 row affected (0.01 sec)
    
    # 查看事件详细信息,我们在做全量备份的时候刷新了bin log日志,所以我们查看的日志文件为mybinlog.000002
    mysql> show binlog events in 'mybinlog.000002';
    [root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002
    
    导出增量备份日志的数据
    # 注意:导出bin log时不要加选项--base64-output
    [root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002 --start-position=192 --stop-position=338 > /tmp/1.sql
    
    
    恢复数据
    1. mysql数据导入时,临时关闭bin log,不要将恢复数据的写操作也记入
    mysql> set sql_log_bin=0;
    Query OK, 0 rows affected (0.00 sec)
    
    2. 先恢复全量
    # 如果不是压缩包直接使用:source /tmp/full.sql
    mysql> system zcat /tmp/full.sql.gz | mysql -uroot -p123
    Warning: Using a password on the command line interface can be insecure.
    
    3. 再恢复增量
    mysql> source /tmp/1.sql;
    
    4. 开启二进制日志,恢复数据的写操作也记入bin log文件
    mysql> set sql_log_bin=1;
    Query OK, 0 rows affected (0.00 sec)
    
    

    LVM备份

    1. 添加硬盘; 这里我们直接实现SCSI硬盘的热插拔, 首先在虚拟机中添加一块硬盘, 无需重启
    echo '- - -' > /sys/class/scsi_host/host0/scan
    echo '- - -' > /sys/class/scsi_host/host1/scan
    echo '- - -' > /sys/class/scsi_host/host2/scan
    
    2. 创建逻辑卷
    pvcreate /dev/sdb
    vgcreate vg1 /dev/sdb
    lvcreate -n lv1 -L 5G vg1
    
    3. 格式化制作文件系统并挂载到指定目录下
    mkfs.xfs /dev/mapper/vg1-lv1
    
    # 将/var/lib/mysql的文件都暂时移动到/opt目录下
    mv /var/lib/mysql/* /opt/
    
    # 挂载
    [root@db01 ~]# mount /dev/mapper/vg1-lv1 /var/lib/mysql
    # 移动回去
    
    4. 先打开binlog日志
    [root@db01 ~]# vim /etc/my.cnf
    [mysqld]
    server_id=1
    log-bin=/service/mysql/mybinlog
    binlog_format='row'		 	# (row,statement,mixed)
    binlog_rows_query_log_events=on
    max_binlog_size=100M
    
    # 重启数据库
    [root@db01 ~]# systemctl restart mysql
    
    5. 往数据库内插入测试数据
    create database db01;
    use db01;
    create table t1(id int);
    insert t1 values(1),(2),(3);
    

    全备

    mysql> FLUSH TABLES WITH READ LOCK; 		 # 锁定所有表
    Query OK, 0 rows affected (0.00 sec)
    
    [root@node1 lvm_data]# lvcreate -L 1G -s -n lv1_from_vg1_snap /dev/vg1/lv1  # 创建快照卷
    
    # 刷新bin log日志,便于日后查找
    [root@db01 ~]# mysql -uroot -p123 -e "flush logs"
    
    mysql> UNLOCK TABLES; 			# 解锁所有表
    Query OK, 0 rows affected (0.00 sec)
    
    [root@node1 lvm_data]# mkdir /snap1 		# 创建文件夹
    [root@node1 lvm_data]# mount -o nouuid /dev/vg1/lv1_from_vg1_snap /snap1
    
    [root@localhost snap1]# cd /snap1/
    [root@localhost snap1]# tar cf /tmp/mysqlback.tar *
    
    [root@localhost snap1]# umount -l /snap1/ 
    [root@localhost snap1]# lvremove vg1/lv1_from_vg1_snap
    
    

    增备

    模拟数据丢失
    use db01;
    create table t2(id int);
    insert t2 values(1),(2),(3);
    drop database db01;
    
    # 查看事件详细信息,我们在做全量备份的时候刷新了bin log日志,所以我们查看的日志文件为mybinlog.000002
    mysql> show binlog events in 'mybinlog.000002';
    [root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002
    
    导出增量备份日志的数据
    # 注意:导出bin log时不要加选项--base64-output
    [root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002 --start-position=120 --stop-position=468 > /tmp/1.sql
    
    1. 先恢复全量
    [root@db01 ~]# tar -xf /tmp/mysqlback.tar -C /service/
    
    2. 再恢复增量
    #  mysql数据导入时,临时关闭bin log,不要将恢复数据的写操作也记入
    mysql> set sql_log_bin=0;
    
    mysql> source /tmp/1.sql;
    
    #  开启二进制日志,恢复数据的写操作也记入bin log文件
    mysql> set sql_log_bin=1;
    

    Xtrabackup备份

    Xtrabackup是由percona提供的mysql数据库备份工具,据官方介绍,这也是世界上惟一一款开源的能够对innodb和xtradb数据库进行热备的工具。特点:

    1. 备份过程快速、可靠;

    2. 备份过程不会打断正在执行的事务;

    3. 能够基于压缩等功能节约磁盘空间和流量;

    4. 自动实现备份检验;

    5. 还原速度快;

    使用xtrabackup使用InnoDB能够发挥其最大功效, 并且InnoDB的每一张表必须使用单独的表空间, 我们需要在配置文件中添加 innodb_file_per_table = ON 来开启。

    安装脚本,当然你也可以一个个执行

    #!/usr/bin/env bash
    cd /opt
    wget https://repo.percona.com/yum/percona-release-latest.noarch.rpm
    yum install percona-release-latest.noarch.rpm -y
    yum install percona-xtrabackup-24 -y
    

    全备

    # 创建备份目录
    [root@db01 ~]# mkdir /backup
    
    # 全备
    [root@db01 ~]# innobackupex --user=root --password=xxx! /backup/full
    
    # 查看
    [root@db01 ~]# cd /backup/
    [root@db01 backup]# ls
    full
    [root@db01 backup]# cd full/
    [root@db01 full]# ls
    2021-07-21_20-31-12
    
    # 去掉时间戳
    [root@db01 full]# innobackupex --user=root --password=xxx! --no-timestamp /backup/full
    [root@db01 full]# ls
    backup-my.cnf   mysql                   xtrabackup_checkpoints
    egon_test       performance_schema      xtrabackup_info
    ib_buffer_pool  sys                     xtrabackup_logfile
    ibdata1         xtrabackup_binlog_info
    
    # 补充:关于备份目录下新增的文件说明,可用cat命令查看 xtrabackup_checkpoints 存储系统版本号,增备的时候会 用到xtrabackup_info 存储UUID,数据库是由自己的UUID的, 如果相同,做主从会有问题 xtrabackup_logfile 就是redo
    

    增备

    --incremental:开启增备功能
    --incremental-basedir:上一次备份的路径
    
    # 增备
    innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/full /backup/xtra
    
    # 查看
    [root@db01 ~]# cd /backup/
    [root@db01 backup]# ls
    full  xtra
    
    # 查看是否衔接
    [root@db01 backup]# cat full/xtrabackup_checkpoints 
    backup_type = full-backuped
    from_lsn = 0
    to_lsn = 5908495
    last_lsn = 5908504
    compact = 0
    recover_binlog_info = 0
    flushed_lsn = 5908504
    [root@db01 backup]# cat xtra/xtrabackup_checkpoints 
    backup_type = incremental
    from_lsn = 5908495
    to_lsn = 5908495
    last_lsn = 5908504
    compact = 0
    recover_binlog_info = 0
    flushed_lsn = 5908504
    

    案例--备份

    # 开启binlog
    vim /etc/my.cnf 
    [mysqld] 
    server_id=1 				 -- 服务ID,主从库必须不一样,必须指定
    log-bin=/service/mysql/mybinlog 		-- 此变量用于控制是否开启二进制日志,而且这是一个只读变量,默认值为OFF
    binlog_format='row'			-- (row,statement,mixed),不建议随意去修改binlog工作模式 
    binlog_rows_query_log_events=on 		 -- 打开才能查看详细记录,默认为off
    max_binlog_size=100M			-- 设置单个二进制日志文件的最大大小,超出100M。MySQL默认会新建一个文件记录日志
    
    # 你权限有问题自己解决
    
    # 重启mysql
    systemctl restart mysqld
    
    # 插数据
    create database egon_test;
    use egon_test;
    create table employee(id int not null unique auto_increment,name varchar(20) not null,sex enum('male','female') not null default 'male', age int(3) unsigned not null default 28,hire_date date not null,post varchar(50),post_comment varchar(100),salary double(15,2),office int, depart_id int);#插入记录#三个部门:教学,销售,运营
    
    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);
    
    # 第一次全备
    innobackupex --user=root --password=xxx! --no-timestamp /backup/full
    
    # 周一继续插数据
    use egon_test;
    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);
    
    # 周一增备
    innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/full /backup/xtra
    
    # 周二继续插数据
    use egon_test;
    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);
    
    # 周二增备
    innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/xtra /backup/xtra1
    
    # 周三插入数据
    use egon_test;
    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);
    
    # 然后数据库没了
    # bomb 纱卡拉卡
    [root@db01 mysql]# rm -rf /var/lib/mysql
    
    # 恢复思路
    1. 停业务,挂维护页 2. 查找可用备份并处理备份:full+inc1+inc2 3. 找到binlog中: inc2 到 故障时间点的binlog 4. 恢复全备+增量+binlog 5. 验证数据 6. 起业务,撤维护页
    

    案例--恢复

    # 整理full
    [root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only /backup/full
    
    # 合并周一的增量到full并整理
    [root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only --incremental-dir=/backup/xtra /backup/full
    
    # 合并周二的增量到full并整理
    [root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only --incremental-dir=/backup/xtra1 /backup/full
    
    # 整理full
    [root@db01 backup]# innobackupex --apply-log --use-memory=3G /backup/full
    
    # 查看周三的binlog_info
    [root@db01 backup]# cat xtra1/xtrabackup_binlog_info 
    mybinlog.000001	4350
    
    [root@db01 backup]# mysqlbinlog /data/binlog/mybinlog.000001 -- start-position=4350 > /backup/binlog.sql # 起始点选择上面的
    
    # 授权
    chown -R mysql.mysql /var/lib/mysql
    
    systemctl start mysqld
    source /backup/binlog.sql
    

    数据库的快速导入和导出

    在公司中,如果运营或者产品手里有几千万甚至几亿条数据,要求你将其导入数据中,请问如何做?
    如果你依据运营或产品交给你的数据文件直接使用insert语句,一行一行地批量插入,那至少需要1-2天时间才能插入完毕,此时我们可以用load data infile语句。 load data infile语句可以从一个文本文件中,将数据以很高的速度读入一个表中。MySQL官方文档也说明了,该方法比一次性插入一条数据性能快20倍。此外,mysql也支持快速导出语句select into outfile 。
    使用MySQL的select into outfile、load data infile快速导出导入数据,12G的数据导出用时3分钟左右,导入用时4分钟左右(执行时间根据机器的配置会有所不同,不具有参考价值)。
    

    快速导出

    语法:
    select ... into outfile 导出文本文件
    
    要想导出成功,需要设置安全目录才行
    vim /etc/my.cnf
    [mysqld]
    secure-file-priv=/tmp			# 指定导出的目录
    
    SELECT * FROM db1.t1
    INTO OUTFILE '/tmp/db1_t1.txt'
    FIELDS TERMINATED BY ',' 			-- 定义字段分隔符
    OPTIONALLY ENCLOSED BY '"' 			-- 定义字符串使用什么符号括起来
    LINES TERMINATED BY '
    '; 			-- 定义换行符
    

    快速导入

    mysql> delete from t1; 
    mysql> create table new_t1(表结构与文件中数据保持一致); 
    mysql> LOAD DATA INFILE '/tmp/db1_t1.txt'
      	   INTO TABLE new_db.new_t1
      	   FIELDS TERMINATED BY ','
      	   OPTIONALLY ENCLOSED BY '"'
      	   LINES TERMINATED BY '
    ';
    

    花里胡哨导出

    mysql -u root -p123 -e 'select * from db1.t1' > /tmp/db1_t1.txt 
    mysql -u root -p123 --xml -e 'select * from db1.t1' > /tmp/db1_t1.xml 		-- xml文件跨平台性
    mysql -u root -p123 --html -e 'select * from db1.t1' > /tmp/db1_t1.html
    

    案例

    # 建表
    create database db01;
    use db01;
    
    create table employee(
    id int not null unique auto_increment,
    name varchar(20) not null,
    sex enum("male","female") not null default "male",  
    age int(3) unsigned not null default 28,
    hire_date date not null,
    post varchar(50),
    post_comment varchar(100),
    salary double(15,2),    
    office int, 
    depart_id int
    );
    
    # 插入数据
    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
    ("sun","male",18,"20170301","猥琐欲为",1000.22,401,1),   
    ("haha","male",78,"20150302","teacher",10000.31,401,1),
    ("xixi","male",81,"20130305","teacher",8300.31,401,1),
    ("dudu","male",72,"20150312","teacher",3100,401,1);
    
    # 快速导出数据
    # 修改配置文件,指定导出的目录
    vim /etc/my.cnf
    [mysqld]
    secure-file-priv=/tmp
    
    # 重启数据库
    systemctl restart mysql
    
    # 导出数据
    mysql> SELECT * FROM db01.employee
        -> INTO OUTFILE '/tmp/employee.txt'
        -> FIELDS TERMINATED BY ','
        -> OPTIONALLY ENCLOSED BY '"'
        -> LINES TERMINATED BY '
    ';
        -> LINES TERMINATED BY '
    ';
    Query OK, 4 rows affected (0.00 sec)
    
    # 查看导出的数据
    cat /tmp/employee.txt 
    # 1,"sun","male",18,"2017-03-01","猥琐欲为",N,1000.22,401,1
    # 2,"haha","male",78,"2015-03-02","teacher",N,10000.31,401,1
    # 3,"xixi","male",81,"2013-03-05","teacher",N,8300.31,401,1
    # 4,"dudu","male",72,"2015-03-12","teacher",N,3100.00,401,1
    
    # 快速导入数据
    #  创建表,表结构与数据保持一致
    create table test(
    id int not null unique auto_increment,
    name varchar(20) not null,
    sex enum("male","female") not null default "male",  
    age int(3) unsigned not null default 28,
    hire_date date not null,
    post varchar(50),
    post_comment varchar(100),
    salary double(15,2),    
    office int, 
    depart_id int
    );
    
    # 导人数据
    mysql> LOAD DATA INFILE '/tmp/employee.txt'
        -> INTO TABLE db01.test
        -> FIELDS TERMINATED BY ','
        -> OPTIONALLY ENCLOSED BY '"'
        -> LINES TERMINATED BY '
    ';
    Query OK, 4 rows affected (0.00 sec)
    Records: 4  Deleted: 0  Skipped: 0  Warnings: 0
    
    # 查看导入的新表数据
    mysql> select * from db01.test;
    +----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
    | id | name | sex  | age | hire_date  | post         | post_comment | salary   | office | depart_id |
    +----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
    |  1 | sun  | male |  18 | 2017-03-01 | 猥琐欲为     | NULL         |  1000.22 |    401 |         1 |
    |  2 | haha | male |  78 | 2015-03-02 | teacher      | NULL         | 10000.31 |    401 |         1 |
    |  3 | xixi | male |  81 | 2013-03-05 | teacher      | NULL         |  8300.31 |    401 |         1 |
    |  4 | dudu | male |  72 | 2015-03-12 | teacher      | NULL         |  3100.00 |    401 |         1 |
    +----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
    4 rows in set (0.00 sec)
    
  • 相关阅读:
    观察者模式股票提醒
    中介者模式虚拟聊天室
    模板方法模式数据库的连接
    职责链模式财务审批
    期末总结
    软件需求分析考试
    tomcat启动极其慢的解决方法困扰我一年多的问题终于解决
    状态模式银行账户
    解释器模式
    动态加载JS文件提升访问网站速度
  • 原文地址:https://www.cnblogs.com/zhaokunhao/p/15041946.html
Copyright © 2011-2022 走看看