文章目录

  • 1.MySQL入门
    • 1.1.源码安装MySQL5.7
    • 1.2.Docker安装MySQL5.7
    • 1.3.忘记MySQL超户密码
    • 1.4.MySQL支持简体中文
  • 2.MySQL数据库操作
    • 2.1.SQL语句概述
    • 2.2.数据库表的管理操作
    • 2.3.用户权限设置
    • 2.4.其他的常用的命令
  • 3.MySQL索引
    • 3.1.数据库索引概述
    • 3.2.常见的检索方案
    • 3.3.B+Tree数据结构解析
    • 3.4.创建、查看、删除索引
  • 4.MySQL数据库管理
    • 4.1.MySQL用户账号管理
    • 4.2.MySQL用户授权操作
    • 4.3.MySQL日志管理
    • 4.4.MySQL数据乱码问题
  • 5.MySQL完全备份与恢复
    • 5.1.数据备份的重要性
    • 5.2.数据备份的分类
    • 5.3.MySQL完全备份操作
    • 5.4.恢复数据库
    • 5.5.Mysql数据库备份脚本
  • 6.MySQL增量备份与恢复
    • 6.1.MySQL增量备份的概念
    • 6.2.MySQL增量恢复
  • 7.MySQL存储引擎
    • 7.1.存储引擎概念介绍
    • 7.2.MyISAM介绍
    • 7.3.InnoDB介绍
    • 7.4.InnoDB索引的结构
    • 7.5.企业选择存储引擎
    • 7.5.配置适合的存储引擎
  • 8.MySQL事务
    • 8.1.事务简介
    • 8.2.事务隔离级别
    • 8.3.脏读幻读不可重复读
  • 9.MySQL主从复制
    • 9.1.单个MySQL问题分析
    • 9.2.主从复制原理
    • 9.3.主从复制的模式
    • 9.4.MySQL主从复制服务搭建
    • 9.5.GTID方式搭建主从复制
  • 10.MySQL读写分离
    • 10.1.读写分离场景背景
    • 10.2.SpringBoot+MySQL实现读写分离
  • 11.MySQL性能优化分析
    • 11.1.基于Durid分析MySQL执行性能
    • 11.2.MySQL性能优化分析
  • 12.MySQL分库分表
    • 12.1.分库分表的原因
    • 12.2.分库分表的方式
    • 12.3.Sharding-JDBC实现分库分表读写分离
  • 13.MMM实现MySQL高可用
    • 13.1.MMM介绍
    • 13.2.MMM实现MySQL高可用

1.MySQL入门

1.1.源码安装MySQL5.7

1、关闭防火墙、selinux

systemctl stop firewalld.service
vi /etc/selinux/config
更改 SELINUX=disabled

2、安装cmake工具

yum -y install cmake

3、解压boost压缩包(mysql5.1.17必须依赖)

tar -xf boost_1_59_0.tar.gz

4、yum安装其他组件

yum -y install gcc gcc-c++ bzip2 bzip2-devel bzip2-libs python-devel ncurses ncurses-devel openssl-devel

5、创建mysql程序用户

useradd -M -s /sbin/nologin mysql

6、创建mysql安装路径,数据文件路径,并授权

安装路径:mkdir -p /usr/local/mysql
数据文件路径:mkdir -p /usr/local/mysql/data
授权:chown -R mysql:mysql /usr/local/mysql /usr/local/mysql/data

7、使用cmake工具对mysql5.7.17进行环境收集检验于配置相关模块

#解压mysql源码包,并进入解压后的路径 (注意:cmake之前一定要解压boost_1_59_0.tar.gz)
tar -xf mysql-5.7.17.tar.gz
cd mysql-5.7.17
cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql/ \
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
-DMYSQL_DATADIR=/usr/local/mysql/data \
-DSYSCONFDIR=/etc \
-DMYSQL_USER=mysql \
-DMYSQL_TCP_PORT=3306 \
-DWITH_XTRADB_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DMITH_READLINE=1 \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_EXTRA_CHARSETS=1 \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DEFAULT_CHARSET=all \
-DWITH_BIG_TABLES=1 \
-DWITH_BOOST=../boost_1_59_0 \
-DWITH_DEBUG=0
参数详细信息解释:
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \  #指定mysql数据库安装目录
-DMYSQL_DATADIR=/data/mydata \             #指定数据库文件路径
-DSYSCONFDIR=/etc \                            #指定配置文件目录
-DWITH_INNOBASE_STORAGE_ENGINE=1 \         #安装INNOBASE存储引擎
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \          #安装ARCHIVE存储引擎
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \        #安装BLACKHOLE存储引擎
-DWITH_READLINE=1 \                        #使用readline功能
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock \        #连接文件位置
-DWITH_SSL=system \                        #表示使用系统上的自带的SSL库
-DWITH_ZLIB=system \                       #表示使用系统上的自带的ZLIB库
-DDEFAULT_CHARSET=utf8 \                   #指定默认使用的字符集编码
-DDEFAULT_COLLATION=utf8_general_ci \      #指定默认使用的字符集校对规则
-DDOWNLOAD_BOOST=1 \
-DWITH_BOOST=../boost_1_59_0 \                 #指定Boost库的位置,mysql5.7必须添加该参数
-DENABLE_DOWNLOADS=1                       #支持下载可选文件

编译完成警告忽略,如果出现报错删除mysql安装包下的CMakeCache.txt文件

8、编译并安装(此步时间过长,可以来把王者压压惊)

make && make install

9、编译并安装

/usr/local/mysql/bin/mysqld \
--initialize \
--user=mysql \
--basedir=/usr/local/mysql \
--datadir=/usr/local/mysql/data \
-socket=/tmp/mysql.sock

10、修改my.cnf配置文件

vi /etc/my.cnf
[mysqld]
datadir=/usr/local/mysql/data
socket=/tmp/mysql.sock
symbolic-links=0[mysqld_safe]
log-error=/usr/local/mysql/log/mysql.errlog
pid-file=/usr/local/mysql/data/$hostname.pid

11、创建错误日志文件,并将文件归属到mysql程序用户下

touch /usr/local/mysql/log/mysql.errlog
chown -R mysql:mysql /usr/local/mysql/log/mysql.errlog

注意:如果配置文件里面配置了,某个文件一定要在启动前提前创建好,否则会报错

Starting MySQL.2019-08-03T14:19:37.028727Z mysqld_safe error: log-error set to '/usr/local/mysql/log/mysql.errlog', however file don't exists. Create writable for user 'mysql'. ERROR! The server quit without updating PID file (/data/mydata/localhost.localdomain.pid).

12、初始化数据库

mysql_install_db --user=mysql --datadir=/usr/local/mysql/data

13、创建软链接

ln -s /usr/local/mysql/bin/* /usr/bin/

14、启动mysql服务

/usr/local/mysql/support-files/mysql.server start

15、停止mysql服务

/usr/local/mysql/support-files/mysql.server stop

16、登入mysql服务

mysql -uroot -p
设置密码:set password=password('123456');

1.2.Docker安装MySQL5.7

1、查看centos内核版本

uname -a

  • 安装条件:docker官方至少要求3.8,建议3.10以上

2、安装Docker的前期准备

(1)关闭防火墙

systemctl stop firewalld

(2)关闭selinux

vi /etc/selinux/config   --->将SELINUX=disabled

(3)安装wget命令

yum -y install wget

(4)下载阿里云docker社区版yum源

cd /etc/yum.repos.d/  进入到yum源的路径wget  http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

(5)下载网络源安装contain-SElinux包

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clean allyum -y update #这一块时间有点长,耐心等待
  • 注意:一定要有这一步,否则会报找不到selinux包

(6)安装docker ce社区版

yum -y install docker-ce.x86_64

(7)设置开机自启

systemctl enable docker

(8)更新xfsprogs

yum -y update xfsprogs

3、启动docker容器

systemctl start docker

4、查看docker版本和详细信息

(1)查看docker版本

docker version

(2)查看docker详细信息

docker info

5、配置阿里云镜像加速

  • 新建文件:vi /etc/docker/daemon.json
  • 编辑文件
{"registry-mirrors": ["https://6j96rs65.mirror.aliyuncs.com"]
}
  • 重启docker
  • systemctl daemon-reload
  • systemctl restart docker
  • 查看容器独有的IP地址
  • docker inspect --format=‘{{.NetworkSettings.IPAddress}}’ 容器名|容器ID

6、拉取mysql:5.7镜像

docker pull mysql:5.7

7、启动MySQL5.7容器

docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
docker run #启动一个容器
--name mysql #设置容器名称为mysql
-p 3306:3306 #宿主机端口:容器内部端口, 将容器的mysql服务端口映射给宿主机的一个端口,对外提供 访问
-v /usr/local/docker/mysql/conf:/etc/mysql #挂载映射,将容器内部的/etc/mysql文件挂载载 宿主机的/usr/local/docker/mysql/conf目录下,其他同理
-e MYSQL_ROOT_PASSWORD=123456 #-e设置额外的参数,设置root用户的密码
-d #后台运行
mysql:5.7 #镜像名称

8、docker ps查看MySQL容器运行状态

docker ps

1.3.忘记MySQL超户密码

1、源码服务mysql忘记超户密码

(1)暂停 mysq服务

systemctl stop mysql  #或者/etc/init.d/mysqld start

(2)跳过 grant表授权,进入安全模式,并在后台运行

mysqld_safe --skip-grant-tables &

(3)进入安全模式修改密码

[root@mysql ~]# mysqlmysql> use mysql;
Database changed
mysql> update user set Password=password('123456') where user='root';
Query OK, 3 rows affected (0.00 sec)
Rows matched: 4    Changed: 3     Warnings: 0
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> exit
Bye

(4)重启mysql服务

systemctl start mysql #或者/etc/init.d/mysqld restart
mysql -uroot -p123456

2、容器服务mysql忘记超户密码

(1)进入容器内部

docker exec -it mysql bash

(2)设置跳过权限表的加载,注意这块表示任何用户都能登进来,非常不安全

echo "skip-grant-tables" >> /etc/mysql/conf.d/docker.cnf

(3)退出容器,重启容器

1.exit2.docker restart mysql

(4)再次进入容器

docker exec -it mysql bash

(5)登录mysql(无需密码)

mysql -uroot

(6)更新权限,修改密码,退出,替换掉刚才加的跳过权限表的加载参数

1.flush privileges;2.alter user 'root'@'localhost' identified by '123456';
(或者:update user set password=password('123456') where user ='root')3.exit4.sed -i "s/skip-grant-tables/ /" /etc/mysql/conf.d/docker.cnf

(7)退出容器,重启容器

1.exit2.docker restart mysql

1.4.MySQL支持简体中文

1、临时开启mysql支持简体中文

2、永久开启mysql支持简体中文

(1)源码mysql服务

  • 修改/etc/my.cnf配置文件
vi /etc/my.cnf加如下内容:
[client]
default-character-set=utf8[mysql]
default-character-set=utf8

(2)容器mysql服务

  • 进入容器内部,修改配置文件
1.docker exec -it mysql(容器名) bash2.vim /etc/mysql/conf.d/docker.cnf加如下内容:
[client]
default-character-set=utf8[mysql]
default-character-set=utf8

2.MySQL数据库操作

2.1.SQL语句概述

  • SQL语句是(Structured Query Language)的缩写,是关系型数据库的标准语言,用于维护管理数据库,如数据查询、数据更新、访问控制、对象管理等功能。
  • SQL分类:
    • DDL:数据定义语言,用于增删改数据库的对象。
    • DML:数据操作语言,用于对表数据的增删改。
    • DQL:数据查询语句,用于数据查询。
    • DCL:数据控制语句。

2.2.数据库表的管理操作

1、查看数据库结构

查看数据库结构:
show databases;

查看数据库中的数据表信息:
use 数据库名;  #使用数据库
show tables;  #查看当前库下的所有表

显示数据表的结构:
describe 数据库.表名
describe mysql.user; (或者desc user)

2、DDL语句操作

数据定义语言,用于增删改数据库对象。

(1)create创建新库、创建新表

创建test库:
create database test;
创建test表:
create table test (id int(10) not null);

(2)drop删除库、删除表

删除test库:
drop database test;
删除test表:
drop table test;

3、DML语句表数据操作

数据操作语言,用于对表中的数据进行增删改。

(1)insert插入新数据

insert into 表名(字段1,字段2,....) values ('字段1的值','字段2的值',.....);
示例:
INSERT INTO sys.sys_config
(variable, value, set_time, set_by)
VALUES('diagnostics.include_raw', 'OFF', '2021-11-01 14:43:22.0', NULL);

(2)update更改原有的数据

update 表名 set 字段1 = 值1,.... where 表达式条件;
示例:
UPDATE sys.sys_config
SET value='OFF', set_time='2021-11-01 14:43:22.0', set_by=NULL
WHERE variable='diagnostics.include_raw';

(3)delete删除不需要的数据

delete from 表名 where 表达式条件;
示例:
DELETE FROM sys.sys_config
WHERE variable='diagnostics.include_raw';

4、DQL语句操作

数据查询语句,用于数据库的查询。

  • select查询语句
select 字段名1,字段名2,.... from 表名;
select 字段名1,字段名2,.... from 表名 where 表达式条件;
示例:
SELECT variable, value, set_time, set_by
FROM sys.sys_config
WHERE variable='diagnostics.include_raw';

2.3.用户权限设置

1、设置用户权限(用户不存在则新建用户)

grant 权限 on 库名.表名 to 'root'@'localhost' IDENTIFIED BY '123456';
grant all on *.* to 'root'@'localhost' IDENTIFIED BY '123456';all #代表所有权限
on  #作用在那个库那些表中
*.* #第一个*代表所有的库第二个*代表所有的表
to 'root'@'localhost' #作用在本地的root用户上
IDENTIFIED BY '123456' #设置密码grant select on mysql.* to 'lixiang'@'localhost' IDENTIFIED BY '123456';#将mysql库的所有表的查询的权限付给本地lixiang这个用户,初始密码为123456

2、查看用户权限

show grants;#查看当前用户的权限
show grants for 'lixiang'@'localhost';#查看本地用户lixiang的权限
show grants for 'lixiang'@'192.168.10.4';#查看远程用户lixiang的权限
#注意:执行完成记得flush privileges;刷新一下

3、撤销用户权限

revoke select on mysql.* from 'lixiang'@'localhost';
#删除lixiang用户对mysql下的所有表的查询权限,注意:撤销完记得flush privileges;刷新一下

2.4.其他的常用的命令

1、用于显示广泛的服务器状态信息

show status;

2、显示服务器错误或警告信息

show errors;
show warnings;

3、显示当前连接用户

select user();

4、显示当前时间

select now();

5、显示当前数据库

select database();

6、显示服务器状态

status;

3.MySQL索引

3.1.数据库索引概述

1、数据库索引

  • 在数据库中,索引使数据库程序无需对整个表进行扫描,就可以在其中找到对应的数据。
  • 数据库的索引是某个表中一列或者若干列值的集合,以及物理标识这些值的数据页的逻辑指针清单。

2、索引的作用

  • 设置了合适的索引之后,数据库利用各种快速的定位技术,能够大大加快查询速率,特别是当表很大时,或者查询涉及到多个表时,使用索引可使查询加快成千倍。
  • 可以降低数据库的 IO成本,并且索引还可以降低数据库的排序成本。
  • 通过创建唯一性索引保证数据表数据的唯一性,可以加快表与表之间的连接。
  • 在使用分组和排序时,可大大减少分组和排序时间。

3、索引的分类

(1)普通索引

  • 这是最基本的索引类型,而且没有唯一性之类的限制。

(2)唯一性索引

  • 与普通索引基本相同,区别在于:索引列的所有值都只能出现一次,即必须唯一,但可为空。

(3)主键

  • 是一种特殊的唯一索引,必须指定为“PRIMARY KEY”,具有唯一性的同时不能为空。

(4)全文索引

  • MySQL从 3.23.23版开始支持全文索引和全文检索。在 MySQL中,全文索引的类型为FULLTEXT,全文索引可以在 VARCHAR或者 TEXT类型的列上创建。贴吧的文本内容,和一些小型的网站网页内容,存放在数据库中即为全文索引模式。

(5)单列索引与多列索引

  • 索引可以是单列上创建的索引,也可以是多列上创建的索引。

4、索引高性能保证

  • 把查询过程中的随机事件变成顺序事件
  • 数据保存在磁盘上,为了提高性能,每次又可以把部分的数据读到内存中来计算,访问磁盘的成本大概是访问内存的十万倍左右
  • 磁盘IO与预读
    • 考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据读到内存缓冲区,而是把相邻的数据也读到内存缓冲区内,因为局部预读性原理,当计算机访问一个地址的数据的时候,与其它相邻的数据也会很快被访问到。每一次IO读取的数据我们称为一页(page)。具体一页有多大数据跟操作系统有关系,一般是4K或者8K。

3.2.常见的检索方案

  • 顺序检索:最基本的查询算法-复杂度O (n),大数据量此算法效率糟糕。
  • 二叉树查找(binary tree search):O(log2n),单层节点所能存储数据量较少,需要遍历多层才能定位到数据。
  • hash索引:无法满足范围查找,优点:等值检索快,hash值==物理地址,范围检索
  • B-TREE:每个节点都是一个二元数组[key,data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。

3.3.B+Tree数据结构解析

  • B-Tree的缺点:插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。造成IO操作频繁。区间查找可能需要返回上层节点重复遍历,IO操作繁琐。
  • B+Tree的改进:非叶子节点不存储data,只存储索引key,只有叶子节点才存储data。

  • 高性能保证:

    • 三层的B+Tree可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共就是需要百万次的IO,显然成本非常高。
    • 在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。
    • B+Tree只在叶子节点存储索引数据,所有叶子节点包含一个链指针,其他内层非叶子节点只存储索引数据。只利用索引快速定位数据索引范围,先定位索引在通过索引高效快速定位数据。

3.4.创建、查看、删除索引

根据企业需求选择了何时的索引之后,可以使用 CREATE INDEX创建索引,CREATE INDEX加上各个索引关键字便可创建各个类型的索引。

1、创建普通索引

格式:create index <索引的名字> on 表名(字段);
示例:create index my_index on student(id);
查看表的索引:show index from student;

2、创建唯一性索引

格式:create unique index <索引名称> on 表名(字段);
示例:create unique index my_index on student(id);
查看表的索引:show keys from student;

3、创建主键索引

格式:alter table 表名 add primary key(字段);
示例:alter table student add primary key(员工ID);

4、删除索引

语法:drop index <索引的名称> on 表名;alter table 表名 drop index <索引的名称>;alter table 表名 drop primary key;
示例:
drop index my_index on student;
alter table student drop primary key;

5、不适合用索引的场景

  • 不适合键值较少的列(重复数据较多的列)
  • 索引无法存储null值

6、索引失效的场景

  • 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少使用or的原因),想要使用or,又想让索引生效,只能将or条件中的每个列都加上索引
  • 对于多列索引,不是使用的第一部分,则不会使索引生效
  • like查询以%开头
  • 类型强转情况下不走索引,如varchar类型用long传参来查
  • hash索引不支持范围检索

4.MySQL数据库管理

4.1.MySQL用户账号管理

1、新建用户

语法:create user '用户名'@'来源地址' identifled by '密码'
create user 'amber'@'192.168.159.%' indentified by '123456'

2、删除用户

语法:drop user '用户名'@'来源地址';
drop user 'amber'@'192.168.159.%';

3、重命名及修改主机

格式:RENAME USER ‘原用户名’@’原主机’ to ‘新用户名’@’新主机’;      #可以只修改用户名或只修改主机
create user 'amber'@'192.168.159%' to 'admin'@'%';

4、给用户设置密码

set password=password('密码');  //为当前登录的用户修改密码
set password for '用户名'@'来源地址'=password('密码'); //为其他用户设置密码

4.2.MySQL用户授权操作

1、授予用户权限

格式:grant 权限列表 on 库名.表名 to '用户名'@'来源地址' [identified by 密码];
//将所有库的所有表的所有权限付给admin用户
grant all on *.* to 'admin'@'192.168.159.%' identified by 123456;

2、查看用户权限

格式:show grants; //查看当前登录的用户的授权信息
show grants for '用户名'@'来源地址';
//查看admin用户的所有权限
show grants for 'admin'@'192.168.159.%';

3、撤销用户权限

格式:revoke 权限列表 on 库名.表名 from '用户名'@'来源地址';
//撤销admin用户的drop和create权限
revoke drop,create on *.* from 'amber'@'%';

4、常见的权限列表

ALL:设置除 GRANT OPTION之外的所有权限,如需设置,授权时加上  WITH GRANT OPTION
ALTER:允许使用 ALTER TABLE,修改表
CREATE:允许使用 CREATE TABLE,创建表
CREATE USER:用户管理权限
DELETE:允许使用 DELETE,删除数据
DROP:允许使用 DROP TABLE,删除表
INSERT:允许使用 INSERT,在表中插入信息
REPICATION SLAVE:从主服务器中读取二进制日志事件
SELECT:允许使用 SELECT,查看表内数据
SHOW DATABASES:允许使用 SHOW DATABASES显示所有数据库
UPDATE:允许使用 UPDATE修改表内的数据

4.3.MySQL日志管理

MySQL日志包括:

  • 错误日志
  • 通用查询日志
  • 二进制日志
  • 慢速查询日志

1、错误日志

包含了当MySQL启动和停止时,以及服务器在运行过程中发生的任何错误时的相关信息,默认在安装目录"/用户自己的安装路径/mysql/data/“下的"主机名.err”。

可以在登录时使用“–log-error=文件路径及文件名”的方式来指定mysql保存错误日志的位置,或者修改著配置文件/etc/my.cnf,在[mysqld]下方添加“log-error=文件路径及文件名”,重启mysqld服务。

vi /etc/my.cnf[mysqld]
log-error=mysql_error.log //默认路径在/用户自己指定的mysql目录

2、通用查询日志

mysql所有连接和语句都将被记录。默认关闭此项日志记录,一般用作调试,平时开启会占用大量的磁盘空间。

可以在登录时使用“–log=文件路径及文件名”或“-l 文件路径及文件名”选项指定,或者修改著配置文件/etc/my.cnf,在[mysqld]下面添加“log = ON”和“log=文件路径及文件名”,重启mysqld服务。

vi /etc/my.cnf[mysqld]
log=ON
log= /usr/local/mysql/data/mysql_general.log     //绝对路径可以省略,保证 mysql对该目录具有写入权限

3、二进制日志

包含了所有更新了的数据或者已经潜在更新了数据的所有语句,记录了数据的更改。主要目的是在恢复时能够最大可能地恢复数据库。默认是开启的,默认路径在/usr/local/mysql/data/下的,以“mysql-bin”开头的二进制日志。

可在登录时使用“ --log-bin=文件路径及文件命前缀”选项指定;或修改主配置文件/etc/my.cnf中[mysqld]下的“log-bin =文件路径及文件名”修改存放路径。

可用 mysqlbinlog命令查看二进制日志文件。

vi /etc/my.cnf[mysqld]
log-bin=mysql-bin  //默认就有,无需修改即可

4、慢查询日志

记录所有执行时间超过long_query_time秒的SQL语句,可用于找到执行时间长的查询,以用于优化。默认未开启。

可在登录时使用“–log-slow-queries[=文件路径及文件名]”选项开启,若为指定文件路径及文件会,会在 /usr/local/mysql/data下生成“主机名 -slow.log”;或修改主配置文件/etc/my.cnf,在[mysqld]下添加“long_query_time”和“log-slow-queries =文件路径及文件名”,重启 mysqld服务。

vi /etc/my.cnf[mysqld]
long_query_time = 5  //单位秒
log-slow-queries = mysql_slow.log

4.4.MySQL数据乱码问题

1、MySQL数据乱码的可能原因

(1)服务器系统字符设置问题

(2)数据表语言设置问题

(3)客户端连接语言系统的问题

2、解决办法

(1)在创建数据库时设定

格式:create database 库名 character set 'utf8' collate 'utf8_general_ci'
案例:create database lixiang character set 'utf8' collate 'utf8_general_ci'

(2)在创建表时设定字符集

格式:CREATE TABLE表名(字段格式) DEFAULT CHARSET=utf8;
案例:create table student(name char(40),age int(20)) default charset=utf8;

(3)使用 SET NAMES设置默认字符集

格式:set names utf8;

(4)永久修改,修改主配置文件在[mysql]字段中加入:default_character_set=utf8;

 vim /etc/my.cnf[mysql]
//注意,不是[mysqld]!!!否则启动时报错!!!(若为 yum安装的不会报错)
default-character-set = utf8

5.MySQL完全备份与恢复

5.1.数据备份的重要性

在生产环境中,数据的安全性是至关重要的,任何数据的丢失都有可能产生严重的后果。

造成数据丢失的原因如下:

  • 程序错误
  • 人为错误
  • 计算机失败
  • 磁盘失败
  • 灾难(火灾、地址)和盗窃

5.2.数据备份的分类

1、从物理与逻辑的角度,备份可以分为物理备份和逻辑备份。

(1)物理备份:对数据库操作系统的物理文件(如数据文件、日志文件等)的备份。物理备份又可以分为脱机备份(冷备份)和联机备份(热备份)。

  • 冷备份:是在关闭数据库的时候进行的。
  • 热备份:数据库处于运行状态,这种备份方法依赖于数据库的日志文件。
  • 温备份:数据库锁定表格(不可写入但可读)的状态下进行的。

(2)逻辑备份:对数据库逻辑组件(如表等数据库对象)的备份

2、从数据库的备份策略角度,备份可分为完全备份、差异备份和增量备份。

(1)**完全备份:**每次对数据进行完整的备份。

  • 对整个数据的备份、数据库结构和文件结构的备份,保存的是备份完成时刻的数据库,是差异备份与增量备份的基础。
  • 优点:备份与恢复操作简单方便
  • 缺点:数据存在大量的重复:占用大量的空间,备份与恢复的时间长

(2)**差异备份:**备份的时间节点从上一次完全备份之后被修改过的所有文件。

  • 备份的时间节点是从上一次完整备份起,备份数据量会越来越大。恢复数据时只恢复上一次的完全备份与最近一次的差异备份。

(3)**增量备份:**只有那些在上一次完全备份或者增量备份后被修改的文件才会被备份

  • 以上一次完全备份或上次的增量备份的时间为时间点,仅备份这之间的数据变化,因而备份的数据量小,占用空间小,备份速度快。但恢复时,需要从上一次的完整备份起到最后一次增量备份依次恢复,如中间某次的备份数据损坏,将导致数据丢失。

5.3.MySQL完全备份操作

1、直接打包数据文件

进入到数据目录文件:
cd /data/mydata/tar Jcf mysql_all-$(date +%F).tar.xz /data/mydata/
模拟删除数据:
删除school库
rm -rf /data/mydata/school
show databases;

恢复数据:
tar -xf all_data-2021-11-10.tar.xz
进入到解压目录中:
cd /root/mysql_data_bak/data/mydata
mv * /data/mydata/
再次登录MySQL查看:
show databases;

2、使用专用的备份工具mysqldump

​ MySQL自带的备份工具,相当方便对MySQL进行备份。通过改命令工具可以将指定的库、表、或者全部的库导出为SQL脚本,在需要时可以进行数据恢复。

使用mysqldump对MySQL完全备份操作

  • 数据备份参数:
--all-databases, -A: 备份所有数据库
--databases, -B: 用于备份多个数据库,如果没有该选项,mysqldump 把第一个名字参数作为数据库名,后面的作为表名。使用该选项,mysqldum把每个名字都当作为数据库名。
--force, -f:即使发现sql错误,仍然继续备份
--host=host_name, -h host_name:备份主机名,默认为localhost
--no-data, -d:只导出表结构
--password[=password], -p[password]:密码
--port=port_num, -P port_num:制定TCP/IP连接时的端口号
--quick, -q:快速导出
--tables:覆盖 --databases or -B选项,后面所跟参数被视作表名
--user=user_name, -u user_name:用户名
--xml, -X:导出为xml文件

(1)对单个库进行完全备份

格式:mysqldump -u用户名 -p密码 [选项] [数据库名] >/备份路径/备份文件名
案例:mysqldump -uroot -p123456 school>/root/mysql_data_bak/all_data-$(date +%Y%m%d).sql

(2)对多个库进行完全备份

格式:mysqldump -u用户名 -p密码 [选项] --databases [数据库名1] [数据库名2] >/备份路径/备份文件名
案例:mysqldump -uroot -p123456 --databases school mysql>/root/mysql_data_bak/school+mysql-$(date +%Y%m%d).sql

(3)对全部数据库进行备份

格式:mysqldump -u用户名 -p密码 [选项] --all-databases >/备份路径/备份文件名
案例:mysqldump -uroot -p123456 --opt --all-databases >/root/mysql_data_bak/all_data-$(date +%Y%m%d).sql

(4)对表进行完全备份

格式:mysqldump -u用户名 -p密码 [选项] 数据库名 表名 >/备份路径/备份文件名
案例:mysqldump -uroot -p123456 school student >/root/mysql_data_bak/school_student-$(date +%Y%m%d).sql

(5)对表结构的备份

格式:mysqldump -u用户名 -p密码 -d 数据库名 表名 >/备份路径/备份文件名
案例:mysqldump -uroot -p123456 -d school student >/root/mysql_data_bak/school_student_desc-$(date +%Y%m%d).sql

mysql容器mysqldump命令备份

(1)对单个库进行完全备份

docker exec 容器名(或容器id)sh -c 'exec mysqldump -u用户名 -p密码 数据库名' >/备份的本地路径地址
备份数据库结构:
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 -t school' >/mysql/backup-data/school-$(date "+%Y-%m-%d_%H:%M:%S").sqldocker exec mysql sh -c 'exec mysqldump -uroot -p123456 school' >/mysql/backup-data/school-$(date "+%Y-%m-%d_%H:%M:%S").sql

(2)对多个数据库进行完全备份

docker exec 容器名(或容器id)sh -c 'exec mysqldump -u用户名 -p密码 --databases 数据库名1 数据库名2' >/备份的本地路径地址
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 --databases school mysql' >/mysql/backup-data/school+mysql-$(date "+%Y-%m-%d_%H:%M:%S").sql

(3)对所有数据库进行完全备份

docker exec 容器名(或容器id)sh -c 'exec mysqldump -u用户名 -p密码 --all-databases' >/备份的本地路径地址
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 --all-databases' >/mysql/backup-data/all-databases-$(date "+%Y-%m-%d_%H:%M:%S").sql

(4)对表进行完全备份

docker exec 容器名(或容器id)sh -c 'exec mysqldump -u用户名 -p密码 数据库名 表名' >/备份的本地路径地址
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 school student' >/mysql/backup-data/school_student-$(date "+%Y-%m-%d_%H:%M:%S").sql

(5)对表结构的备份

docker exec 容器名(或容器id)sh -c 'exec mysqldump -u用户名 -p密码 -d 数据库名 表名' >/备份的本地路径地址
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 -d school student' >/mysql/backup-data/desc_school_student-$(date "+%Y-%m-%d_%H:%M:%S").sql

5.4.恢复数据库

1、使用mysqldump备份后,恢复数据库

(1)source命令

语法:source 备份的sql脚本路径
案例:source /data/mydata/all_data-20211110.sql

(2)mysql命令

格式:
mysql -u用户名 -p密码 < 备份的脚本路径
mysql -u用户名 -p密码 库名 < 表备份的脚本路径
案例:
mysql -uroot -p123456 </data/mydata/all_data-20211110.sql
mysql -uroot -p123456 school </data/mydata/school_student-20211110.sql

2、mysql容器进行数据恢复

(1)恢复所有库

docker exec -i 容器名(或容器id)sh -c 'exec mysql -u用户名 -p密码' </备份的本地路径地址
docker exec -i mysql sh -c 'exec mysql -uroot -p123456' </mysql/backup-data/all-databases-2021-11-04_09\:06\:27.sql

(2)恢复某个表库

先恢复库结构:
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 school' >/mysql/backup-data/desc_school-$(date "+%Y-%m-%d_%H:%M:%S").sql
在恢复库数据:
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 school' >/mysql/backup-data/school-$(date "+%Y-%m-%d_%H:%M:%S").sql

(3)恢复某个表

docker exec -i 容器名(或容器id)sh -c 'exec mysql -u用户名 -p密码 数据库名' </备份的本地路径地址
docker exec mysql sh -c 'exec mysqldump -uroot -p123456 school' >/mysql/backup-data/school_student-2021-11-04_09\:15\:42.sql

5.5.Mysql数据库备份脚本

#!/bin/bash
#MySQL数据库备份脚本#设置登录变量
MY_USER="root"
MY_PASS="123456"
MY_HOST="192.168.159.30"
MY_CONN="-u$MY_USER -p$MY_PASS -h$MY_HOST"#设置备份数据库
MY_DB1="school"#定义备份路径、工具、时间、文件名
BF_DIR="/mysql/backup/"
BF_CMD="/usr/bin/mysqldump"
BF_TIME=$(date +%Y%m%d-%H%M)
SQL_NAME="$MY_DB1-$BF_TIME"#备份为.sql脚本,然后打包压缩(打包后删除原文件)
[ -d $BF_DIR] || mkdir -p $BF_DIR
cd $BF_DIR
$BF_CMD $MY_CONN --databases $MY_DB1 >$SQL_NAME.sql
/bin/tar zcf $SQL_NAME.tar.gz $SQL_NAME.sql --remove &>/dev/null

  • 加入定时:crontab -e

    • 0 2 * * * /root/mysql-backup.sh
  • 查看定时:crontal -l

6.MySQL增量备份与恢复

6.1.MySQL增量备份的概念

使用 mysqldump进行完全备份,备份的数据中有重复数据,备份时间与恢复时间长。而增量备份就是备份自上一次备份之后增加或改变的文件或内容。

1、增量备份的特点

  • 没有重复数据,备份量不大,时间短
  • 恢复麻烦:需要上次完全备份及完全备份之后所有的增量备份才能恢复,而且要对所有增量备份进行逐个反推恢复。
  • MySQL没有提供直接的增量备份办法,可以通过 MySQL提供的二进制日志(binary logs)间接实现增量备份。

2、MySQL二进制日志对备份的含义

二进制日志保存了所有更新或者可能更新的数据库操作。

二进制日志在启动MySQL服务器后开始记录,并在文件达到max_binlog_size所设置的大小或者收到flush logs命令后重新创建新的日志。

6.2.MySQL增量恢复

1、应用场景

(1)人为的SQL语句破坏了数据库

(2)在进行下一次全备之前发生系统故障导致数据库数据丢失

(3)在主从架构中,主库数据发生了故障

2、增量恢复的方法

(1)一般的恢复:备份的二进制日志内容全部恢复

格式:mysqlbinlog [–no-defaults] 增量备份文件 | mysql -u用户名 -p密码

(2)基于时间点恢复:便于跳过某个发生错误的时间点实现数据恢复

  • 从日志开头截止到某个时间点的恢复:
格式:mysqlbinlog [--nodefaults] --stop-datetime='年-月-日 小时:分钟:秒' 二进制日志文件 | mysql -u用户名 -p密码
案例:mysqlbinlog --no-defaults --stop-datetime='2016-05-07 15:21:17' /data/mydata/mysql-bin.000006 | mysql -uroot -p123456
  • 从某个时间点到日志结尾的恢复:
格式:mysqlbinlog [--nodefaults] --start-datetime='年-月-日 小时:分钟:秒' 二进制日志文件 | mysql -u用户名 -p密码
案例:mysqlbinlog --nodefaults --start-datetime='2016-05-07 15:21:17' /data/mydata/mysql-bin.000006 | mysql -uroot -p123456
  • 从某个时间点到某个时间点的恢复:
格式:mysqlbinlog [--nodefaults] --start-datetime='年-月-日 小时:分钟:秒' --stop-datetime='年-月-日 小时:分钟:秒' 二进制日志文件 | mysql -u用户名 -p密码
案例:mysqlbinlog --nodefaults --start-datetime='2016-05-07 15:21:17' --stop-datetime='2017-05-07 15:21:17' /data/mydata/mysql-bin.000006 | mysql -uroot -p123456

(3)基于位置恢复:可能在同一时间点既有错误的操作也有正确的操作,基于位置进行恢复更加精准

格式:

mysqlbinlog --stop-position='操作id' 二进制日志文件 | mysql -u用户名 -p密码
案例:mysqlbinlog --stop-position='246' /data/mydata/mysql-bin.000006 | mysql -uroot -p123456
mysqlbinlog --start-position='操作id' 二进制日志文件 | mysql -u用户名 -p密码
案例:mysqlbinlog --start-position='246' /data/mydata/mysql-bin.000006 | mysql -uroot -p123456

7.MySQL存储引擎

7.1.存储引擎概念介绍

  • MySQL中的数据用各种不同的技术存储在文件系统中,每一种技术都使用不同的存储机制、索引技巧,锁定水平并最终提供不同的功能和能力,这些不同的技术以及配套的功能在MySQL中称为存储引擎。
  • 存储引擎就是 MySQL讲数据存储在文件系统中的存储方式或者存储格式目前 MySQL常用的两种存储引擎:MyISAM、InnoDB。
  • MySQL存储引擎是 MySQL数据库服务器中的组件,负责为数据库执行实际的数据 I/O操作,使用特殊存储引擎的主要优点之一在于,仅需提供特殊应用所需的特性,数据库中的系统开销较小,具有更有效和更高的数据库性能。
  • MySQL系统中,存储引擎处于文件系统之上,在数据保存到数据文件之前会传输到存储引擎,之后按照各个存储引擎的存储格式进行存储。

7.2.MyISAM介绍

1、MyISAM概述

MyISAM存储引擎是MySQL关系数据库系统5.5版本之前默认的存储引擎,前身是ISAM。

ISAM是一个定义明确且历经时间考验的数据表格管理方法,在设计之时就考虑到数据库被查询的次数远大于更新的次数。

ISAM的特点:ISAM执行读取操作的速度很快,而且占用不大量的内存和存储资源,它不支持事物,不能够容错。

MyISAM管理非事务表,是 ISAM的扩展格式,提供 ISAM里所没有的索引和字段管理的大量功能。

MyISAM使用一种表格锁定的机制,以优化多个并发的读写操作,MyISAM提供高速存储和检索,以及全文搜索能力,受到web开发的青睐。

2、MyISAM的特点

(1)不支持事务

(2)表级锁定形式,数据在更新时锁定整个表

(3)数据库在读写过程中相互阻塞,会在数据写入的过程中阻塞用户数据的读取,也会在数据读取的过程中阻塞用户的数据写入

(4)可以通过 key_buffer_size来设置缓存索引,提高访问性能,减少磁盘 IO的压力但缓存只会缓存索引文件,不会缓存数据

(5)采用 MyISAM存储引擎数据单独写入或读取,速度过程较快而且占用资源相对较少

(6)MyISAM存储引擎不支持外键约束,只支持全文索引

(7)每个 MyISAM在磁盘上存储成三个文件,每一个文件的名字以表的名字开始,扩展名指出文件类型

  • .frm文件存储表定义
  • .MYD文件存储数据(MYData)
  • .MYI文件存储索引文件(MYIndex)

3、MyISAM使用的生成场景

(1)公司业务不需要事务支持

(2)一般单方读取数据比较多的业务,或单方面写入数据比较多的业务,如:www.blog,图片信息数据库,用户数据库,商品库等业务,MyISAM存储引擎数据读写都比较频繁的场景不适合。

(3)对数据业务一致性要求不是非常高的业务

(4)使用读写并发访问相对较低的业务

(5)数据修改相对较少的业务

(6)服务器硬件资源相对比较差

7.3.InnoDB介绍

1、InnoDB的特点

(1)支持事务:支持 4个事务隔离级别

(2)行级锁定,但是全表扫描仍然会是表级锁定

(3)读写阻塞与事务隔离级别相关

(4)具有非常高效的缓存特性:能缓存索引,也能缓存数据

(5)表与主键以簇的方式存储

(6)支持分区、表空间,类似 oracle数据库

(7)支持外键约束,5.5以前不支持全文索引,5.5版本以后支持全文索引

(8)对硬件资源要求比较高

2、InnoDB使用的生成场景

(1)业务需要事务的支持

(2)行级锁定对高并发有很好的适应能力,但需要确保查询是通过索引来完成

(3)业务数据更新较为频繁的场景,如:论坛,微博

(4)业务数据一致性要求较高,如:银行业务

(5)硬件设备内存较大,利用 InnoDB较好的缓存能力来提高内存利用率,减少磁盘 IO的压力。

7.4.InnoDB索引的结构

InnoDB以表空间Tablespace(idb文件)结构进行组织,每个Tablespace包含多个Segment段,每个段(分为2种段:叶子节点Segment和非叶子节点Segment),一个Segment段包含多个Extent(区),一个Extent占用1M空间包含64个Page(每个Page16k),InnoDB B+Tree一个逻辑节点就分配一个物理Page,一个节点一次IO操作,一个Page里含有多个有序数据Row行数据,Row行数据中包含多个Filed属性数据等信息。

7.5.企业选择存储引擎

1、需要考虑每个存储引擎提供了哪些不同的核心功能及应用场景

2、支持的字段和数据类型

  • 所有引擎都支持通用的数据类型,但不是所有引擎都支持其他的字段类型,如二进制对象。

3、锁定类型:不同的存储引擎支持不同级别的锁定

  • 表锁定:MyISAM、MEMORY
  • 行锁定:InnoDB
  • 页锁定:BDB

4、索引的支持

  • 建立索引在搜索和恢复数据库中的数据的时候能显著提高性能
  • 不同的存储引擎提供不同的制作索引的技术
  • 有些存储引擎根本不支持索引

5、事务处理的支持

  • 事务处理功能通过提供在想表中更新和插入信息期间的可靠性
  • 可根据企业业务是否要支持事务选择存储引擎

7.5.配置适合的存储引擎

1、查看数据库可配置的存储引擎

show engines \G;

2、查看表正在使用的存储引擎

(1)查看正在MySQL的默认引擎

语法:SHOW TABLE STATUS FROM库名    WHERE name = ‘表名’;
show table status from mysql where name='user' \G;

(2)配置存储引擎为所选择的类型

语法:ALTER TABLE 表名 ENGINE=引擎
alter table mysql.user engine=innodb;

(3)修改my.cnf的default-storage-engine为引擎

[mysqld]
default-storage-engine = InnoDB
重启mysqld:/usr/local/mysql/support-files/mysql.server start

8.MySQL事务

8.1.事务简介

1、事物的概念

  • 事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起想系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么不都执行。
  • 事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是最小的控制单元。
  • 事务适用于多用户同时操作的数据库系统的场景,如银行、保险公司及证券交易系统等等。通过事务的完整性以保证数据的一致性。

2、事务的 ACID特点

  • 原子性

    • 事务是一个完整的操作,事务的各元素是不可分的(原子的),事务的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。
  • 一致性
    • 当事务完成时,数据必须处于一致状态:在事务开始之前,数据库汇总存储的数据处于一致状态;在正在进行的事务中,数据可能处于不一致的状态;当事务完成时,数据必须再次回到已知的一致状态。
  • 隔离性
    • 对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应该以任何方式依赖于或影响其他事务。修改数据的事务可以在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。
  • 持久性
    • 事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的。一旦事务被提交,事务的效果会被永久地保留在数据库中。

3、事务的操作

  • 默认情况下 MySQL的事务是自动提交的,当 sql语句提交时事务便自动提交。

  • 手动对事务进行控制的方法:

    • 事务处理命令控制
    • 使用 set设置事务处理方式
  • begin开始一个事务

    commit提交一个事务

    rollback回滚一个事务(撤销)

  • 使用 set命令进行控制

    set autocommit=0禁止自动提交

    set autocommit=1开启自动提交

8.2.事务隔离级别

  • read uncommitted(读未提交):即便是事务没有commit,但是其他连接仍能读取到未提交的数据,这是所有隔离级别中最低的一种。

  • read committed(读已提交):当前会话只能读到其他事务提交的数据,未提交的数据读不到。

  • repeatable read(可重复读):MySQL默认的隔离级别,当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。

  • serializable(串行化):其他绘画对该表的写操作将被挂起。可以看到,这是隔离界别中最严的,也是性能最差的。

  • 设置事务隔离级别代码

//设置read uncommitted级别:
set session transaction isolation level read uncommitted;//设置read committed级别:
set session transaction isolation level read committed;//设置repeatable read级别:
set session transaction isolation level repeatable read;//设置serializable级别:
set session transaction isolation level serializable;
  • ⼀个数据库事务通常包含了⼀个序列的对数据库的读/写操作。它的存在包含有以下两个⽬的:
1、为数据库操作序列提供了⼀个从失败中恢复到正常状态的⽅法,同时提供了数据库即使在异常状态下仍能保持⼀致性的⽅法。
2、当多个应⽤程序在并发访问数据库时,可以在这些应⽤程序之间提供⼀个隔离⽅法,以防⽌彼此的操作互相⼲扰。
  • 查看当前事务隔离级别
SELECT @@tx_isolation;

8.3.脏读幻读不可重复读

1、脏读:所谓脏读是指一个事务中访问到了另外一个事务未提交的数据。

  • 脏读就是指:当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

2、不可重读读:在同一事务中,两次读取同一数据,得到的内容不同

3、幻读:同一事务中,用相同的操作读取两次,得到的记录数不相同

  • 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

9.MySQL主从复制

9.1.单个MySQL问题分析

在企业网站中,后端MySQL数据库只有一台时,会出现以下问题:单点故障,服务不可用,无法处理大量的并发数据请求,数据丢失将造成大灾难。

改造方法:

  • 增加MySQL数据库服务器,对数据进行备份,形成主备。
  • 确保准备MySQL数据库服务器是一样的。
  • 主服务器宕机了备份服务器继续工作,数据有保障。

9.2.主从复制原理

  • MySQL从服务器开启I/O线程,向主服务器请求数据同步(获取二进制文件)
  • MySQL主服务器开启I/O线程回应从服务器
  • 从服务器获得主的二进制日志写入中继日志
  • 从服务器开启SQL线程将日志内容执行,实现数据同步

9.3.主从复制的模式

1、异步模式

异步模式,主节点不会主动push bin log到从节点,这样有可能导致failover情况下,也许从节点没有及时的将最新的bin log同步到本地。

2、半同步模式

这种模式下主节点只需要接受到其中一台从节点的返回信息,就会commit;否则需要等待知道超过时间然后切换成异步模式在提交;这样做的目的可以使只从数据库的数据延迟缩小,可以提高数据安全性,确保了事务提交以后,binlog至少传输到一个节点上,不能保证从节点将此事务更新到db中。性能上会有一定的降低,响应时间会边长。

3、全同步模式

全同步模式是指主节点和从节点全部执行了commit并确认才会向客户端返回成功。

9.4.MySQL主从复制服务搭建

1、环境准备

  • 192.168.159.100(主节点)
  • 192.168.159.101(从节点)
  • 192.168.159.102(从节点)

关闭所有机器的防火墙以及selinux

每台机器上安装配置NTP时间同步服务器:yum -y install ntp

每台服务器与阿里云服务器时间同步:ntpdate ntp1.aliyun.com

2、主库100节点创建开启bin-log日志,并修改server-id重启数据库

vi /etc/my.cnf[mysqld]
log-bin=mysql-bin
log-slave-updates=true #手动添加,开启从日志
server-id=1
重启mysql服务:
/usr/local/mysql/support-files/mysql.server restart
登入mysql服务器给从服务器授权:
mysql -uroot -p123456grant replication slave on *.* to 'myslave'@'%' identified by '123456';刷新:
flush privileges;

3、配置从服务器

(1)101节点配置

修改配置文件:
vi /etc/my.cnf[mysqld]
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
server-id=2 #注意主从不能一致,每个mysql服务独有一个server-id,不能重复

(2)102节点配置

修改配置文件:
vi /etc/my.cnf[mysqld]
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
server-id=3 #注意主从不能一致,每个mysql服务独有一个server-id,不能重复
重启从节点的mysql服务/usr/local/mysql/support-files/mysql.server restart

查看主节点的bin-log日志位置: mysql -uroot -p123456 -e ‘show master status’(主节点机器执行)

登入从的mysql服务与主节点进行绑定:

change master to master_host='192.168.159.100',master_user='myslave',master_password='123456',master_log_file= 'mysql-bin.000001',master_log_pos=154;

在从库开启主从复制:

start slave;

查看从库的状态:

show slave status\G;

注意:在生产环境钟数据库是有初始数据的,在做主从复制之前,做一次全备

全备之前要锁表:
锁表命令  flush table with read lock;
然后全备主库
mysqldump -uroot -p123456 -A |gzip -9 >all.sql.gz
将初始数据导入从库
解锁
unlock tables;

9.5.GTID方式搭建主从复制

1、GTID简介

(1)GTID作用方式

最开始的时候,MySQL只⽀持⼀种binlog dump⽅式,也就是指定binlog filename + position,向master发送COM_BINLOG_DUMP命令。

可以指定flag为BINLOG_DUMP_NON_BLOCK,这样master在没有可发送的binlog event之后,就会返回⼀个EOFpackage。不过通常对于slave来说,⼀直把连接挂着可能更好,这样能更及时收到新产⽣的binlog event。

在MySQL 5.6之后,⽀持了另⼀种dump⽅式,也就是GTIDdump,通过发送COM_BINLOG_DUMP_GTID命令实现,需要带上的是相应的GTID信息。

(2)GTID工作原理

  • master更新数据时,会在事务前产⽣GTID,⼀同记录到binlog⽇志中

  • slave端的i/o 线程将变更的binlog,写⼊到本地的relay log中sql线程从relay log中获取GTID,然后对⽐slave端的binlog是否有记录 2 3 4 5

  • 如果有记录,说明该GTID的事务已经执行,slave会忽略

  • 如果有记录,slave就会从relay log中执⾏该GTID的事务,并记录到binlog

  • 在解析过程中会判断是否有主键,如果没有就⽤⼆级索引,如果没有就⽤全部扫描

(3)GTID优势

  • ⼀个事务对应⼀个唯⼀ID,⼀个GTID在⼀个服务器上只会执行一次;

  • GTID是⽤来代替传统复制的方法,GTID复制与普通复制模式的最⼤不同就是不需要指定⼆进制⽂件名和位置;

  • 减少手工干预和降低服务故障时间,当主机挂了之后通过软件从众多的备机中提升⼀台备机为主机;

(4)GTID的劣势

  • 不⽀持⾮事务引擎;
  • 不⽀持create table … select 语句复制(主库直接报错);(原理:会⽣成两个sql, ⼀个是DDL创建表SQL, ⼀个是insert into 插⼊数据的 sql; 由于DDL会导致⾃动提交, 所以这个sql⾄少需要两个GTID, 但是GTID模式下, 只能给这个sql⽣成⼀个GTID)
  • 不允许⼀个SQL同时更新⼀个事务引擎表和非事务引擎表
  • 在⼀个复制组中,必须要求统⼀开启GTID或者是关闭GTID

2、基于GTID方式搭建主从模式

(1)首先开启GTID方式,vi /etc/my.cnf

gtid_mode=on #(必选)
enforce-gtid-consistency=1 #(必选)
log_bin=mysql-bin #(可选) #⾼可⽤切换,最好开启该功能
log-slave-updates=1 #(可选) #⾼可⽤切换,最好打开该功能

(2)重启mysql服务

systemctl restart mysql

(3)主节点中授权用户,myslave

grant replication slave on *.* to 'myslave'@'%' identified by '123456';

(4)从库执行

change master to master_host='192.168.159.100',master_user='myslave',master_password='123456',master_auto_position=1;

10.MySQL读写分离

10.1.读写分离场景背景

​ 一个项目中数据库最基础同时也是最主流的是单机数据库,读写都在一个库中。当用户逐渐增多,单机数据库无法满足性能要求时,就会进行读写分离改造(适用于读多写少),写操作一个库,读操作多个库,通常会做一个数据库集群,开启主从备份,一主多从,以提高读取性能。当用户更多读写分离也无法满足时,就需要分布式数据库了

10.2.SpringBoot+MySQL实现读写分离

1、环境准备

  • SpringBoot2.x+MySQL5.7+Druid数据源

2、pom.xml文件导入maven依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--SpringBoot集成Aop起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--SpringBoot集成Jdbc起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--SpringBoot集成WEB起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mybatis集成SpringBoot起步依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!--MySQL驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--alibaba数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><!--lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency></dependencies>

3、配置application.yml配置文件

spring:datasource:master:jdbc-url: jdbc:mysql://192.168.159.100:3306/school?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave1:jdbc-url: jdbc:mysql://192.168.159.101:3306/school?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&useSSL=false&serverTimezone=Asia/Shanghaiusername: root   # 只读账户password: 123456driver-class-name: com.mysql.cj.jdbc.Driver
#    slave2:
#      jdbc-url: jdbc:mysql://192.168.159.102:3306/school?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&useSSL=false&serverTimezone=Asia/Shanghai
#      username: root   # 只读账户
#      password: 123456
#      driver-class-name: com.mysql.cj.jdbc.Driver

4、编写DBTypeEnum枚举类

/*** 数据源枚举类*/
public enum DBTypeEnum {MASTER,SLAVE1;
}

5、编写DBContextHolder配置类

/*** Description 这里做读写模式切换* 原理是用ThreadLocal保存当前线程处于那种模式进行切换* 操作结束后要清除该数据,避免内存泄露*/
@Slf4j
public class DBContextHolder {private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();//private static final AtomicInteger counter = new AtomicInteger(-1);public static void setDBType(DBTypeEnum dbTypeEnum){contextHolder.set(dbTypeEnum);}public static DBTypeEnum getDBType(){return contextHolder.get();}public static void master(){try {setDBType(DBTypeEnum.MASTER);log.info("切换到主数据源-master节点");}finally {//最后一定要释放ThreadLocal,防止内存泄漏clearDBType();}}public static void slave(){try {setDBType(DBTypeEnum.SLAVE1);log.info("切换到从数据源-slave1节点");}finally {//最后一定要释放ThreadLocal,防止内存泄漏clearDBType();}//负载均衡时,放开下面代码/*int index = counter.getAndIncrement()%2;if (counter.get()>9999){counter.set(-1);}if(index == 0){setDBType(DBTypeEnum.SLAVE1);log.info("切换到从数据源-slave1节点");}else{setDBType(DBTypeEnum.SLAVE2);log.info("切换到从数据源-slave2节点");}*/}public static void clearDBType() {contextHolder.remove();}}

6、编写MyRoutingDataSource数据源选择配置类

/*** 获取路由key,确定当前数据源* spring在开始进行数据库操作时会通过这个方法决定使用那个数据库,因此在determineCurrentLookupKey* 方法里调用获取数据源的操作getDBType*/
public class MyRoutingDataSource extends AbstractRoutingDataSource {@Nullable@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.getDBType();}
}

7、编写DataSourceConfig数据源配置类

/*** 数据源配置类*/
@Slf4j
@Configuration
public class DataSourceConfig {/*** 设置主数据源* @return*/@Bean@ConfigurationProperties("spring.datasource.master")public DataSource masterDataSource(){log.info("初始化master主节点,载入配置");return DataSourceBuilder.create().build();}/*** 设置从数据源* @return*/@Bean@ConfigurationProperties("spring.datasource.slave1")public DataSource slave1DataSource(){log.info("初始化slave从节点,载入配置");return DataSourceBuilder.create().build();}/*** 路由数据源,通过MyRoutingDataSource中的determineCurrentLookupKey确定使用那个数据源* @param masterDataSource* @param slave1DataSource* @return*/@Beanpublic DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,@Qualifier("slave1DataSource") DataSource slave1DataSource){Map<Object,Object> targetDataSources = new HashMap<>();targetDataSources.put(DBTypeEnum.MASTER,masterDataSource);targetDataSources.put(DBTypeEnum.SLAVE1,slave1DataSource);MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);myRoutingDataSource.setTargetDataSources(targetDataSources);log.info("初始化路由配置数据源");return myRoutingDataSource;}}

8、编写MyBatisConfig配置类

/*** 多数据源要自己配置sqlSessionFactory* mybatis配置类,现在spring容器内有三个数据源,所以我们需要为事物管理器和MyBatis手动执行一个明确的数据源*/
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {@Resource(name = "myRoutingDataSource")private DataSource myRoutingDataSource;@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();//配置数据源sqlSessionFactoryBean.setDataSource(myRoutingDataSource);//设置mapper位置sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));return sqlSessionFactoryBean.getObject();}/*** 手动设置事务,需要知道当前数据源才可以进行设置事务* @return*/@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(myRoutingDataSource);}
}

9、配置方法切面类DataSourceAop

@Aspect
@Component
public class DataSourceAop {@Pointcut("execution(* com.lixiang.service.impl.*.select*(..))"+"|| execution(* com.lixiang.service.impl.*.find*(..)))")public void readPointcut(){}@Pointcut("execution(* com.lixiang.service.impl.*.save*(..))"+"|| execution(* com.lixiang.service.impl.*.insert*(..))"+"|| execution(* com.lixiang.service.impl.*.add*(..))"+"|| execution(* com.lixiang.service.impl.*.update*(..))"+"|| execution(* com.lixiang.service.impl.*.edit*(..))"+"|| execution(* com.lixiang.service.impl.*.delete*(..))"+"|| execution(* com.lixiang.service.impl.*.remove*(..))")public void writePointcut(){}@Before("readPointcut()")public void read(){DBContextHolder.slave();}@Before("writePointcut()")public void write(){DBContextHolder.master();}
}

10、编写实体,Mapper

//student实体
public class StudentPO{private Integer id;private String name;private Integer age;
}//StudentMapper.java
public interface StudentMapper{List<Map<String,Object>> findAllStudent();void addStudent(StudentPO studentPO);
}//StudentMapper.xml<select id="findAllStudent" resultType="java.util.Map">select * from student;</select><insert id="addStudent" parameterType="com.lixiang.domain.StudentPO">insert into student (`name`,age) values (#{name},#{age})</insert>

11、service业务层

//IStudentService
public interface IStudentService {List<Map<String,Object>> findAllStudent();void addStudent();
}//StudentServiceImpl
@Service
public class StudentServiceImpl implements IStudentService {@Resourceprivate StudentMapper studentMapper;/*** 查询全部学生信息* @return*/@Overridepublic List<Map<String, Object>> findAllStudent() {return studentMapper.findAllStudent();}/*** 插入新的学生信息*/@Overridepublic void addStudent() {StudentPO studentPO = new StudentPO();studentPO.setName("测试");studentPO.setAge(18);studentMapper.addStudent(studentPO);}
}

12、controller层

@Slf4j
@RequestMapping("api/v1/student")
@RestController
public class StudentController {@Resourceprivate IStudentService iStudentService;/*** 查询所有学生信息接口* @return*/@GetMapping("/findAllStudent")public List<Map<String,Object>> findAllStudent(){log.info("调用查询所有学生信息接口--api/v1/student/findAllStudent");return iStudentService.findAllStudent();}/*** 保存学生信息* @return*/@GetMapping("/saveStudent")public Map<String,Object> saveStudent(){log.info("调用保存学生信息接口--api/v1/student/saveStudent");iStudentService.addStudent();Map<String,Object> map = new HashMap<>();map.put("code","0000");map.put("desc","操作成功");return map;}
}

13、调用结果

读写分离只是数据库扩展的一个临时解决办法,并不能一劳永逸,随着负载进一步增大,只有一个库用于写入肯定是不够的,而且单表的数据库是有上限的,mysql 最多千万级别的数据能保持较好的查询性能。最终还是会变成–分库分表架构的。

11.MySQL性能优化分析

11.1.基于Durid分析MySQL执行性能

  • 配置Durid打开SQL执行监控
@Configuration
public class DruidConfig {/*** 注入数据源的username,为登录用户*/@Value("${spring.datasource.druid.username}")private String username;/*** 注入数据源的密码,为登录密码*/@Value("${spring.datasource.druid.password}")private String password;/*** 配置数据源,prefix截止到druid之前,spring会自动获取到druid及它下面的内容* @return*/@ConfigurationProperties(prefix = "spring.datasource")public DataSource druidDataSource(){return new DruidDataSource();}@Beanpublic ServletRegistrationBean druidServlet() {ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();servletRegistrationBean.setServlet(new StatViewServlet());servletRegistrationBean.addUrlMappings("/druid/*");Map<String, String> initParameters = new HashMap<>();initParameters.put("loginUsername", username);// ⽤户名initParameters.put("loginPassword", password);// 密码initParameters.put("resetEnable", "false");// 禁⽤HTML⻚⾯上的“Reset All”功能servletRegistrationBean.setInitParameters(initParameters);return servletRegistrationBean;}@Beanpublic FilterRegistrationBean filterRegistrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new WebStatFilter());filterRegistrationBean.addUrlPatterns("/*");filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return filterRegistrationBean;}
}
  • 在项目运行成功之后访问地址:127.0.0.1:8080/druid/login,输入账号名称和密码(数据库连接的账号名和密码)
  • 数据源:项目中管理的所有数据源配置的详细信息
  • SQL监控:锁执行sql语句数量、时长、执行时间分布
  • SQL防火墙:druid提供了黑白名单的访问,可以清楚的看到sql防护情况
  • Web应用:目前运行的web程序的详细信息
  • URI监控:监控到所有的请求路径的请求次数、请求时间等其他参数
  • Session监控:当前的session状况,创建时间、最后活跃时间、请求次数、请求时间等详细参数

11.2.MySQL性能优化分析

  • SQL优化可以分为两个部分,一个是设计阶段,另一个是查询阶段
  • 设计阶段运用到的优化
    • 使用适当的数据库列类型和大小
    • 尽量从设计上采用单表查询解决业务问题
    • 在适当字段加入索引,能用唯一索引引用唯一索引
  • 查询阶段设计的优化
    • 尽可能不用select * :让优化器无法完成索引覆盖扫描这类的优化,而且还会增加额外的I/O、内存和CPU的消耗
    • 慎用join操作:单张表查询可以减少锁的竞争,更容易应对业务的发展,方便对数据库进行拆分
    • 慎用子查询和临时表:未带索引的字段上的group by 操作,union查询,部分order by操作,例如distinct函数和order by一起使用且distinct和prder用一个字段
    • 尽量不要使用limit,部分场景可以改用between and

1、索引优化查询

(1)无索引的情况下千万级别数据查询时间

(2)name字段加上索引查询

2、千万级别数据量分页查询优化

(1)分页查询耗时一览,发现越到后面的查询的时间越长,真正到达亿级查询耗时可想而知

select * from school.student limit 10,20    //耗时4msselect * from school.student limit 100,20   //耗时4msselect * from school.student limit 1000,20  //耗时4msselect * from school.student limit 10000,20  //耗时9msselect * from school.student limit 100000,20  //耗时73msselect * from school.student limit 1000000,20 //耗时542msselect * from school.student limit 10000000,20 //耗时3.377s

(2)对limit分页问题的性能优化方法

利用表的覆盖索引来加速分页查询

利用了索引查询的语句中如果只包含了那个索引列(覆盖索引),那么这种情况会查询很快。

因为利用索引查找有优化算法,且数据就在查询索引上面,不用再去找相关的数据地址了,这样节省了很多时间。另外Mysql中也有相关的索引缓存,在并发高的时候利用缓存就效果更好了。

在我们的例子中,我们知道id字段是主键,自然就包含了默认的主键索引。现在让我们看看利用覆盖索引的查询效果如何:

select id from school.student limit 10000000,20 //耗时1.857s

相对于查询了所有列的大概一半的查询的速度。

那么如果我们也要查询所有列,有两种方法,一种是id>=的形式,另一种就是利用join,看下实际情况:

select * from school.student s where id >= (select id from school.student limit 10000000,1 ) limit 20 //耗时1.808sselect * from school.student a join (select id from school.student limit 10000000,20 ) b on a.id = b.id //耗时1.818s

12.MySQL分库分表

12.1.分库分表的原因

  • 数据库是最容易产生性能瓶颈的服务组件。数据库因为表多、数据多造成的性能问题。

  • 单一服务中心的数据访问压力也必然会达到单机数据库的承载上限,所以在进行服务化改造的同一时间段内,需要对数据库能力做扩展的工作。

  • 单台数据库mysql,当访问连接数过多时,就会出现“too many connections”的错误,一般来说时访问量太大或者数据库设置的最大连接数太小的原因。

  • MySQL默认的最大连接数为100,这个连接数可以修改,而mysql服务允许的最大连接数为16384。

  • 什么是分区?

    • 分表是将一张表分成N多张小表,分区时把一张表的数据分成N多个区块,这些区块可以在同一磁盘上,也可以在不同的磁盘上。
    • 物理上多表存储,但是逻辑上单表操作。

12.2.分库分表的方式

  • 一般会有两种分库分表方向,分别是垂直方向和水平方向,第一种方案时直接对现有的数据库进行垂直拆分,可以缓解目前写峰值QPS过大、DB主从延迟的问题。第二种方案时对现有数据库大表进行分库分表。

  • 根据不同规模对垂直方向和水平方向的选择

    • 单个库太大,这时我们要看时因为表多而倒是数据多,还是因为单张表里的数据多,还是因为单张表里面的数据多。如果是因为表多而数据多,使用垂直切分,根据业务切分成不同的库。
    • 单张表的数据量太大,甚至多个库上的多张表。分库分表的顺序应该是先垂直分,后水平分。因为垂直分更简单,更符合我们处理现实世界问题的方式。
  • 垂直拆分

    • 垂直分表:也就是“大表拆小表”,基于列字段进行的。一般是表中的字段较多,将不常用,数据较大,长度较长的拆分到扩展表

    • 垂直分库:垂直分库针对的是一个系统中的不同业务进行拆分,数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数以及单机硬件资源的瓶颈。

  • 水平拆分

    • 水平分表:针对数据量巨⼤的单张表(⽐如订单表),按照某种规则(RANGE,HASH取模等),切分到多张表⾥⾯去。 但是这些表还是在同⼀个库中,所以库级别的数据库操作还是有IO瓶颈。
    • 水平分库:将单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。 ⽔平分库分表能够有效的缓解单机和单库的性能瓶颈和压⼒,突破IO、连接数、硬件资源等的瓶颈。


12.3.Sharding-JDBC实现分库分表读写分离

1、Sharding-JDBC简介

Sharding定位为轻量级的java框架,在java的JDBC层提供了额外服务。它使用客户端直接连接数据库,以jar包的形式提供服务,无需额外部署和依赖,可理解为增强版JDBC驱动,完全兼容JDBC和各种ORM框架。

适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。

支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。

支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

2、主从复制、分库分表架构图

3、创建maven项目加入依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--MySQL驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--alibaba数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version></dependency><!--lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!--mybatis集成SpringBoot起步依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!--SpringBoot集成Jdbc起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.0.0-RC1</version></dependency></dependencies>

4、配置yml文件

spring:main:allow-bean-definition-overriding: trueshardingsphere:props:sql:show: truedatasource:names: masterdb0,masterdb1,slave1db0,slave1db1,slave2db0,slave2db1 #配置的数据源masterdb0: #主product0库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3306/product0?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivermasterdb1: #主product1库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3306/product1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave1db0: #从1product0库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3307/product0?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave1db1: #从1product1库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3307/product1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave2db0: #从2product0库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3308/product0?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave2db1: #从2product1库type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.159.91:3308/product1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMTusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driversharding:default-database-strategy:inline:#做分片的字段是store_idsharding-column: store_id#分片的算法是store_id对2取余,product$为对库的选择algorithm-expression: product$->{store_id%2}tables:product:#数据节点,product{0,1}.product{0,1,2,3,4,5,6,7}actual-data-nodes: product$->{0..1}.product$->{0..7}table-strategy:inline:#表的分区策略也是根据store_idsharding-column: store_id#根据store_id对7取余+1来区分存在那个product表中,product_$表示对表的选择algorithm-expression: product_$->{(store_id%7)+1}key-generator:column: store_id #自动生成主键type: SNOWFLAKE #生成主键的策略是雪花算法master-slave-rules:product0: #分区product0master-data-source-name:  masterdb0 #分区product0的主数据源slave-data-source-names: slave1db0,slave2db0 #分区product0的从数据源product1: #分区product1master-data-source-name:  masterdb1 #分区product1的主数据源slave-data-source-names: slave1db1,slave2db1 #分区product1的从数据源
sharding:jdbc:config:masterslave:load-balance-algorithm-type: random #轮询策略
mybatis:configuration:log-impl: org.apache.ibatis.logging.slf4j.Slf4jImplmapper-locations: classpath:/mapper/*.xml

5、测试代码

  • 控制层
    @RequestMapping(value = "/addProduct", method = RequestMethod.GET)public String add() {for (int i=10000;i<11000;i++){Product product = new Product();product.setProductSeq("productSeq" + i);product.setCreateTime(LocalDateTime.now());product.setSkuId("skuId" + i);product.setSpuId("spuId" + i);product.setSaleId("saleId" + i);product.setStoreId((long) i);product.setUpdateTime(LocalDateTime.now());productService.addProduct(product);}return "success";}
  • 业务层
@Service
public class ProductServiceImpl implements ProductService {@Resourceprivate ProductMapper productMapper;@Overridepublic void addProduct(Product product){productMapper.insertSelective(product);}@Overridepublic List<Map<String,Object>> findAllProduct() {return productMapper.findAllProduct();}@Overridepublic Product findProductById(Long id) {return productMapper.selectByPrimaryKey(id);}}
  • 数据库存储



13.MMM实现MySQL高可用

13.1.MMM介绍

MMM(Mater-Master Replication Manager for MySQL)MySQL主主复制管理器。MMM是一套灵活的脚本程序,基于perl实现,用来对mysql replication进行监控和故障转移,并能管理mysql Master-Master复制的配置(同一时间只有一个几点是可写的)

MMM的监管端是会提供多个虚拟IP(VIP),包括一个可写的VIP,多个可读的VIP,通过监管的管理,这些IP会绑定在可用的MySQL之上,当某一台mysql宕机时,会将VIP迁移至其他的MySQL上。

mmm_mond:监控进程,负责所有的监控工作,决定和处理所有节点角色活动。因此脚本需要在监管机上运行。

mmm_agentd:运行在每个mysql服务器上的代理进程,完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上与运行。

mmm_control:一个简单的脚本,提供管理mmm_mond进程的命令。

13.2.MMM实现MySQL高可用

1、环境准备

  • mysql-master1:192.168.159.151
  • mysql-master2:192.168.159.152
  • mysql-slave1:192.168.159.153
  • mysql-slave2:192.168.159.154
  • mysql-monitor:192.168.159.155
  • Writer-VIP:192.168.159.201
  • Reader-VIP:192.168.159.202,192.168.159.203

2、主主复制,master1与master2互为主从

(1)编辑配置文件

[client]
socket = /usr/local/mysql/data/mysql.sock
[mysqld]
lower_case_table_names = 1
default-storage-engine = InnoDB
port = 3306
datadir = /usr/local/mysql/data
character-set-server = utf8
socket = /usr/local/mysql/data/mysql.sock
log_bin = mysql-bin
log-error=/usr/local/mysql/logs
server_id = 1 #注意server_id每个节点都要保证不同
innodb_buffer_pool_size = 200M
slave-parallel-workers = 8
thread_cache_size = 600
back_log = 600
slave_net_timeout = 60
max_binlog_size = 512M
key_buffer_size = 8M
query_cache_size = 64Mjoin_buffer_size = 2M
sort_buffer_size = 2M
query_cache_type = 1
thread_stack = 192Kgtid_mode = ON
log_slave_updates
enforce_gtid_consistency

(2)互相授权,两个主中都执行

grant replication slave on *.* to 'slave'@'192.168.159.%' identified by '123456'

(3)设置互为主从

  • 151节点执行
change master to master_host='192.168.159.152',master_user='slave',master_password='123456',master_auto_position=1;
  • 152节点执行
change master to master_host='192.168.159.151',master_user='slave',master_password='123456',master_auto_position=1;

3、配置mysql-slave1、mysql-slave2为mysql-master1的主节点

change master to master_host='192.168.159.151',master_user='slave',master_password='123456',master_auto_position=1;

目前数据同步为在master1(151)上或者master2(152)上创建数据在四个库中应该都可以看到

4、在mysql-monitor上安装mysql-mmm-monitor服务

(1)5台机器机器安装epel源

yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
wget https://mirrors.aliyun.com/repo/Centos-7.repo

(2)master1、master2、slave1、slave2安装mysql-mmm-agent,monitor安装mysql-mmm-monitor

151,152,153,154节点:
yum -y install mysql-mmm-agent
155节点:
yum -y install mysql-mmm-monitor

(3)四台服务器配置151、152、153、154

cd /etc/mysql-mmm/cp -p mmm_common.conf mmm_common.conf.bak #备份mysql-mmm的配置文件 vi mmm_common.conf
  • 配置如下:
active_master_role      writer<host default>cluster_interface       ens33 #网卡名称pid_path                /run/mysql-mmm-agent.pidbin_path                /usr/libexec/mysql-mmm/replication_user        slave #授权主从复制的用户replication_password    123456   #主从复制用户的密码agent_user              mmm_agent #agent代理的用户名agent_password          123456      #代理的密码
</host><host db1>ip      192.168.159.151 #ipmode    master #角色peer    db2 #peer表示同级,db1与db2互为主从
</host><host db2>ip      192.168.159.152mode    master peer    db1
</host><host db3>ip      192.168.159.153mode    slave
</host><host db4>ip      192.168.159.154mode    slave
</host>
<role writer>hosts   db1, db2 #配置写库,db1、db2    ips     192.168.159.201 #写库的vipmode    exclusive
</role><role reader>hosts   db3, db4 #配置读库,db3、db4   ips     192.168.159.202, 192.168.159.203 #读库的vipmode    balanced
</role>

(3)登入MySQL给mmm_agent授权

#这个用户为上方agent_user的代理用户
grant super,replication client,process on  *.* to 'mmm_agent'@'192.168.159.%'  identified by '123456';
flush privileges;

(4)修改/etc/mysql-mmm/mmm_agent.conf为 db1、db2、db3、db4

(5)在mysql-monitor(155)节点编辑 mmm_mon.conf

vi /etc/mysql-mmm/mmm_mon.confinclude mmm_common.conf<monitor>ip                  127.0.0.1   #不用修改,默认本机pid_path            /run/mysql-mmm-monitor.pidbin_path            /usr/libexec/mysql-mmmstatus_path         /var/lib/mysql-mmm/mmm_mond.statusping_ips            192.168.159.151,192.168.159.152,192.168.159.153,192.168.159.154 #管理的ip节点auto_set_online     10 #间隔10s进行健康检测# The kill_host_bin does not exist by default, though the monitor will# throw a warning about it missing.  See the section 5.10 "Kill Host# Functionality" in the PDF documentation.## kill_host_bin     /usr/libexec/mysql-mmm/monitor/kill_host#
</monitor><host default>monitor_user        mmm_agent #之前授权的代理用户monitor_password    123456 #密码
</host>debug 0

(6)151、152、153、154启动mysql-mmm-agent,155启动mysql-mmm-monitor

#151、152、153、154节点执行
systemctl start mysql-mmm-agent
#155节点执行
systemctl start mysql-mmm-monitor

(7)在mysql-monitor上查看监控状态

mmm_control show   #查看监控状态

  • 检查vip绑定情况

(8)测试高可用

  • 停掉master1测试,查看监管


  • 停掉slave1测试,查看监控

【超详细】MySQL零基础入门实战相关推荐

  1. 视频教程-19全新mysql教程零基础入门实战精讲mysql视频DBA数据库视频教程SQL教程-MySQL

    19全新mysql教程零基础入门实战精讲mysql视频DBA数据库视频教程SQL教程 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/ja ...

  2. 怎么安装Python,超详细,零基础必看

    如何下载并安装Python?(以win7 64位系统为例) 首先,通过这个链接进入python官网, Welcome to Python.org​www.python.org/正在上传-重新上传取消 ...

  3. 【MongoDB 快速上手】别找了,主流开源分布式系统存储系统三剑客:FastDFS、Minio、MongoDB零基础入门实战教程!

    MongoDB用起来 - 快速上手&集群和安全系列 课程目标 1 MongoDB相关概念 1.1 业务应用场景 1.2 MongoDB简介 1.3 体系结构 1.4 数据模型 1.5 Mong ...

  4. 【超详细】零基础自学网络安全,来收藏这一篇就够了(白嫖60G网安教程)

    01 什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 "红队"."渗透测试" 等就是研究攻击技术,而"蓝队".&quo ...

  5. 全网完整版Python学习路线图(超详细适合零基础自学)

    本文包含了千锋教育Python学习路线全阶段视频教程(从入门到精通),涵盖了你所需要掌握的所有前沿技术及知识点! 全网最新,史上最全Python学习路线,从基础到项目实战应有尽有,牛批卡拉斯! Pyt ...

  6. 【Python学习】 - 超详细的零基础Pandas学习(附Python数据分析与应用课本第四章实训答案)

    (博文体系参考:<Python数据分析与应用>课本) 任务数据如下: 读入csv文件时,encoding必须是正确的,常用的编码格式有:UTF-8 , UTF-16 , GBK , GB2 ...

  7. Spark零基础入门实战(一)Scala安装

    Scala是一种将面向对象和函数式编程结合在一起的高级语言,旨在以简洁.优雅和类型安全的方式表达通用编程模式.Scala功能强大,不仅可以编写简单脚本,还可以构建大型系统. Scala运行于Java平 ...

  8. 超详细、零基础安装纯净的原版Windows 10系统

    https://www.toutiao.com/a6740040272035447308/

  9. MySQL零基础入门(二)

    文章目录 修改表 1.添加列 2.修改列 3.删除列 4.代码 5.insert语句 6.说明insert语句的细节 7.update语句 修改表 1.添加列 alter table tablenam ...

最新文章

  1. Windows SharePoint Services 3.0 应用程序模板
  2. Spring MVC+Spring +Hibernate配置事务,但是事务不起作用
  3. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1069:乘方计算
  4. P1020 导弹拦截(最长不上升序列+二分)
  5. [转]cocos2d游戏开发,常用工具集合
  6. 当实在受不了了,就开干吧!
  7. [旧博客]不用编程也能搞定作弊刷票
  8. Python:Numpy库中的invert()函数的用法
  9. C++相对路径下新建文件夹
  10. 强大的Bigdecimal
  11. java字符串不足后面补0,JAVA字符串格式化长度不足补0
  12. 两种方法去除页眉页脚:基于OCR识别后的文本/基于图片切割
  13. 通过多视图信息瓶颈学习鲁棒表征
  14. android 地址选择下载,选择走开下载-选择走开(吃饭位置选择)下载v1.0.4 安卓版-西西软件下载...
  15. 关于PLC控制系统中电动机过载保护方法的探究
  16. 折线迷你图怎么设置_Excel如何制作小微型的迷你图?
  17. 网站分享:7个非常好用的电子书网站
  18. Java初学者作业——编写Java程序, 实现根据用户购买商品总金额, 计算实际支付的金额及所获得的购物券金额。
  19. 标注工具:parselmouth(歌声合成语音合成标注)
  20. hdu 6656 Final Exam

热门文章

  1. 大剑漫画下载[CLAYMORE]
  2. python 爬取视频真实地址_python 爬取视频
  3. 儒家文化圈孕育人工智能新文明
  4. Git Bash/GUI Here “找不到应用程序问题” 的解决方案
  5. 小程序内关注微信公众号的几种方式
  6. oracle高可用培训,企业级Oracle数据库高可用性(OracleDataGuard)DBA培训视频全集
  7. MySQL轻快入门2021.3.19(常用函数)
  8. php画拼图滑动验证,滑动拼图验证
  9. CodeForces 283C Coin Troubles 分析+背包思想
  10. 美团智能配送系统的运筹优化实战-笔记