首页 技术 正文
技术 2022年11月21日
0 收藏 780 点赞 2,575 浏览 7811 个字

1. 读写分离&读写分离 简介

  • 主从同步延迟
  • 分配机制
  • 解决单点故障
  • 总结

2. 主从复制&读写分离 搭建

  • 搭建主从复制(双主)
  • 搭建读写分离

1. 读写分离&读写分离 简介

随着用户和数据的增多,单机的数据库往往支撑不住快速发展的业务,所以数据库集群就产生了!

读写分离顾名思义就是读和写分离,对应到数据库集群一般都是一主一从(一个主库,一个从库)或者一主多从(一个主库,多个从库),业务服务器把需要写的操作都写到主数据库中,读的操作都去从库查询。主库会同步数据到从库保证数据的一致性。

这种集群方式的本质是把访问的压力从主库转移到从库也就是在单机数据库无法支撑并发读写,并且读的请求很多的情况下适合这种读写分离的数据库集群。如果写的操作很多的话不适合这种集群方式,因为你的数据库压力还是在写操作上,即使主从了之后压力还是在主库上,这样和单机的区别就不大了。

在单机的情况下,一般我们做数据库优化都会加索引,但是加了索引对查询有优化,但会影响写入,因为写入数据会更新索引。所以做了主从之后,我们可以单独地针对从库(读库)做索引上的优化,而主库(写库)可以减少索引而提高写的效率。

还有两点要注意:主从同步延迟、分配机制的考虑。

主从同步延迟

主库有数据写入之后,同时也写入在binlog(二进制日志文件)中,从库是通过 binlog 文件来同步数据的,这期间会有一定时间的延迟,可能是 1 秒,如果同时有大量数据写入的话,时间可能更长。

这会导致什么问题呢?比如有一个付款操作,你付款了,主库是已经写入数据,但是查询是到从库查,从库里还没有你的付款记录,所以页面上查询的时候你还没付款。那可不急眼了啊,吞钱了这还了得!打电话给客服投诉!

所以为了解决主从同步延迟的问题有以下几个方法:

1)二次读取

二次读取的意思就是读从库没读到之后再去主库读一下,只要通过对数据库访问的 API 进行封装就能实现这个功能。很简单,并且和业务之间没有耦合。但是有个问题,如果有很多二次读取相当于压力还是回到了主库身上,等于读写分离白分了。而且如有人恶意攻击,就一直访问没有的数据,那主库就可能爆了。

2)写之后的马上的读操作访问主库

也就是写操作之后,立马的读操作指定为访问主库,之后的读操作则访问从库。这就等于写死了,和业务强耦合了。

3)关键业务读写都由主库承担,非关键业务读写分离

类似付钱的这种业务,读写都到主库,避免延迟的问题,但是例如改个头像啊,个人签名这种比较不重要的就读写分离,查询都去从库查,毕竟延迟一下影响也不大,不会立马打客服电话投诉。

分配机制

分配机制的考虑也就是怎么制定写操作是去主库写,读操作是去从库读。

一般有两种方式:代码封装、数据库中间件。

1)代码封装

代码封装的实现很简单,就是抽出一个中间层,让这个中间层来实现读写分离和数据库连接。讲白点就是搞个 provider 封装了 save、select 等通常数据库操作,内部 save 操作的 dataSource 是主库的,select 操作的 dataSource 是从库的。

优点:

  1. 实现简单。
  2. 可以根据业务定制化变化,随心所欲。

缺点:

  1. 如果哪个数据库宕机了,发生主从切换了之后,就得修改配置重启。
  2. 如果系统很大,一个业务可能包含多个子系统,一个子系统是 java 写的,一个子系统用 go 写的,这样的话得分别为不同语言实现一套中间层,重复开发。

2)数据库中间件

就是有一个独立的系统,专门来实现读写分离和数据库连接管理,业务服务器和数据库中间件之间是通过标准的 SQL 协议交流的,所以在业务服务器看来数据库中间件其实就是个数据库。

优点:

  1. 因为是通过 SQL 协议的所以可以兼容不同的语言不需要单独写一套。
  2. 由中间件来实现主从切换,业务服务器不需要关心这点。

缺点:

  1. 多了一个系统其实就等于多了一个关心,比如数据库中间件挂了。
  2. 多了一个系统就等于多了一个瓶颈,所以对中间件的性能要求也高,因为所有的数据库操作都要先经过它。
  3. 中间件实现较为复杂,难度比代码封装高多了。

常用的开源数据库中间件有 Mysql Proxy、Atlas、LVS 等。

为什么使用 MySQL-Proxy 而不是 LVS?

  • LVS:分不清读还是写;不支持事务。
  • MySQL-Proxy:自动区分读操作和写操作;支持事务(注意在 MySQL-Proxy 中不要使用嵌套查询,否则会造成读和写的混乱)。

解决单点故障

解决 Proxy 单点故障问题

MySQL-Proxy 实际上非常不稳定,在高并发或有错误连接的情况下,进程很容易自动关闭,因此打开 –keepalive 参数让进程自动恢复是个比较好的办法,但还是不能从根本上解决问题,通常最稳妥的做法是在每个应用服务器(如 Tomcat)上安装一个 MySQL-Proxy 供自身使用(解决 Proxy 单点故障问题),虽然比较低效但却能保证稳定性。

双(多)主机制

Proxy 之后搭 LVS,LVS 为两台主数据库做负载均衡,从数据库从两台主数据库同步。此方案旨在解决:

  • 主数据库的单点故障问题(服务不可用、备份问题)
  • 分担写操作的访问压力。

不过多主需要考虑自增长 ID 问题,这个需要特别设置配置文件,比如双主可以使用奇偶。总之,主之间设置自增长 ID 相互不冲突就能解决自增长 ID 冲突问题。

总结

读写分离相对而言是比较简单的,比分表分库简单,但是它只能分担访问的压力,分担不了存储的压力,也就是你的数据库表的数据逐渐增多,但是面对一张表海量的数据,查询还是很慢的,所以如果业务发展的快数据暴增,到一定时间还是得分库分表。

正常情况下,只有当单机真的顶不住压力了才会集群,不要一上来就集群,没这个必要。有关于软件的东西都是越简单越好,复杂都是形势所迫。

一般我们是先优化单机,如优化一些慢查询,优化业务逻辑的调用或者加入缓存等。如果真的优化到没东西优化了,然后才上集群。先读写分离,读写分离之后顶不住就再分库分表。

2. 主从复制&读写分离 搭建

主从同步的方式也分很多种,一主多从、链式主从、多主多从,根据你的需要来进行设置。

搭建主从复制(双主)

1)服务器二台:分别安装 MySQL 数据库

  1. 安装命令:yum -y install mysql-server
  2. 配置登录用户的密码:mysqladmin -u root password ‘密码’
  3. 配置允许第三方机器访问本机 MySQL:
delete from user where password = '';
update user set host='%';
flush privileges;

2)分别修改 MySQL 配置

配置 masterA:

[root@adailinux ~]# vim /etc/my.cnf
[mysqld]
datadir = /data/mysql
socket = /tmp/mysql.sock
server_id = 1 # 指定server-id,必须保证主从服务器的server-id不同
auto_increment_increment = 2 # 设置主键单次增量
auto_increment_offset = 1 # 设置单次增量中主键的偏移量:奇数
log_bin = mysql-bin # 创建主从需要开启log-bin日志文件
log-slave-updates # 把更新的日志写到二进制文件(binlog)中,台服务器既做主库又做从库此选项必须要开启

配置 masterB:

[root@adailinux ~]# vim /etc/my.cnf
[mysqld]
datadir = /data/mysql
socket = /tmp/mysql.sock
server_id = 2 # 指定server-id,必须保证主从服务器的server-id不同
auto_increment_increment = 2 # 设置主键单次增量
auto_increment_offset = 2 # 设置单次增量中主键的偏移量:偶数
log_bin = mysql-bin # 创建主从需要开启log-bin日志文件
log-slave-updates = True # 把更新的日志写到二进制文件(binlog)中

以上为同步配置的核心参数。

server_id 有两个用途:

  1. 用来标记 binlog event 的源产地,就是 SQL 语句最开始源自于哪里。
  2. 用于 IO_thread 对主库 binlog 的过滤。如果没有设置 replicate-same-server-id=1 ,那么当从库的 IO_thread 发现 event 的源与自己的 server-id 相同时,就会跳过该 event,不把该 event 写入到 relay log 中。从库的 sql_thread 自然就不会执行该 event。这在链式或双主结构中可以避免 sql 语句的无限循环。

注意:如果设置多个从服务器,每个从服务器必须有一个唯一的 server-id 值,且与主服务器的以及其它从服务器的都不相同。

分别重启 masterA 和 masterB 并查看主库状态:

[root@adailinux ~]# /etc/init.d/mysqld restart
Shutting down MySQL.. SUCCESS!
Starting MySQL. SUCCESS! masterA:
[root@adailinux ~]# mysql -uroot
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 419 | TSC | mysql | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)masterB:
[root@adailinux ~]# mysql -uroot
mysql> show master status
-> ;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 419 | TSC | mysql | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

3)配置同步信息

masterA:

[root@adailinux ~]# mysql -uroot
mysql> change master to master_host='192.168.8.132',master_port=3306,master_user='repl',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=419;
#注:IP为masterB的IP(即,从服务器的IP)
mysql> start slave;
Query OK, 0 rows affected (0.05 sec)mysql> show slave status\G;
在此查看有如下状态说明配置成功:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

masterB:

[root@adailinux ~]# mysql -uroot
mysql>change master to master_host='192.168.8.131',master_port=3306,master_user='repl',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=419;
Query OK, 0 rows affected, 2 warnings (0.06 sec)mysql> start slave;
Query OK, 0 rows affected (0.04 sec)mysql> show slave status\G
在此查看有如下状态说明配置成功:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

4)测试主从同步

在 masterA 上创建一个库,验证 masterB 是否同步创建了该库。

搭建读写分离

场景描述:

  • 数据库 Master 主服务器
  • 数据库 Slave 从服务器
  • MySQL-Proxy 调度服务器

以下操作,均是在 MySQL-Proxy 调度服务器上进行的。

1)MySQL 服务器安装

2)检查/安装系统所需软件包

yum -y install gcc* gcc-c++* autoconf* automake* zlib* libxml* ncurses-devel* libmcrypt* libtool* flex* pkgconfig* libevent* glib* readline*

3)编译安装 lua

MySQL-Proxy 的读写分离主要是通过 rw-splitting.lua 脚本实现的,因此需要安装 lua。

方式 1:一般系统自带。

方式 2:手工安装。

这里我们建议采用源码包进行安装:

  1. cd /opt/install
  2. wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
  3. tar zvfxlua-5.2.3.tar.gz
  4. cd lua-5.1.4
  5. vi src/Makefile
  6. 在CFLAGS= -O2 -Wall $(MYCFLAGS) 这一行记录里加上-fPIC,更改为 CFLAGS= -O2 -Wall -fPIC$(MYCFLAGS) 来避免编译过程中出现错误。
  7. make linux(编译到内存)
  8. make install

4)安装 Mysql-Proxy

MySQL-Proxy 可通过此网址获得:http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/

推荐采用已经编译好的二进制版本,因为采用源码包进行编译时,最新版的 MySQL-Proxy 对 automake、glib 以及 libevent 的版本都有很高的要求,而这些软件包都是系统的基础套件,不建议强行进行更新。

并且这些已经编译好的二进制版本在解压后都在统一的目录内,因此建议选择以下版本:

  • 32 位 RHEL5 平台:http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.4-linux-rhel5-x86-32bit.tar.gz
  • 64 位 RHEL5 平台:http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.4-linux-rhel5-x86-64bit.tar.gz
tar -xzvf mysql-proxy-0.8.3-linux-rhel5-x86-64bit.tar.gz
mv mysql-proxy-0.8.3-linux-rhel5-x86-64bit /opt/mysql-proxy

创建 Mysql-Proxy 服务管理脚本:

cd /opt/mysql-proxy
mkdir scripts
cp share/doc/mysql-proxy/rw-splitting.lua scripts/
chmod +x /opt/mysql-proxy/scripts
mkdir /opt/mysql-proxy/run
mkdir /opt/mysql-proxy/log
mkdir /opt/mysql-proxy/scripts

5)修改读写分离脚本 rw-splitting.lua

修改默认连接,进行快速测试,不修改的话要达到连接数为 4 时才启用读写分离

vi /opt/mysql-proxy/scripts/rw-splitting.lua

-- connection pool
if not proxy.global.config.rwsplitthen
proxy.global.config.rwsplit = {
min_idle_connections = 1, //默认为4
max_idle_connections = 1, //默认为8
is_debug = false
}
end

proxy.conf:

[mysql-proxy]
admin-username=root
admin-password=admin
proxy-read-only-backend-addresses=192.168.188.143,192.168.188.139
proxy-backend-addresses=192.168.188.142
proxy-lua-script=/opt/mysql-proxy/bin/rw-splitting.lua
admin-lua-script=/opt/mysql-proxy/lib/mysql-proxy/lua/admin.lua

bin 目录下执行:

./mysql-proxy --defaults-file=/opt/mysql-proxy/proxy.conf

mysql-proxy 脚本参数详解:

  • –proxy_path=/opt/mysql-proxy/bin:定义 mysql-proxy 服务二进制文件路径。
  • –proxy-backend-addresses=192.168.10.130:3306:定义后端主服务器地址。
  • –proxy-lua-script=/opt/mysql-proxy/scripts/rw-splitting.lua:定义 lua 读写分离脚本路径。
  • –proxy-id=/opt/mysql-proxy/run/mysql-proxy.pid:定义 mysql-proxy PID 文件路径。
  • –daemon:定义以守护进程模式启动。
  • –keepalive:使进程在异常关闭后能够自动恢复。
  • –pid-file=$PROXY_PID:定义 mysql-proxy PID 文件路径。
  • –user=mysql:以 mysql 用户身份启动服务。
  • –log-level=warning:定义 log 日志级别,由高到低分别有(error|warning|info|message|debug)。
  • –log-file=/opt/mysql-proxy/log/mysql-proxy.log:定义 log 日志文件路径。

6)测试读写分离 

  1. 登录 Proxy:mysql -u -p -h<proxy_ip> -P4040
  2. stop slave
  3. 在 Proxy 上插数据后,验证主数据库新增数据,而从没有新增数据。
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,994
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,507
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,350
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,135
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,768
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,845