原文网址:http://www.bzfshop.net/article/180.html

对一个电子商务网站而言,最宝贵的资源就是数据。服务器是很廉价的东西,即使烧了好几个也问题不大,但是用户数据如果丢失了,那整个业务就会陷入停顿,一天由于业务停顿而带来的损失可能是好几个服务器几年的成本。随着棒主妇商城(http://www.bangzhufu.com)业务的增长,我们开始考虑异地容灾的问题,假如某天服务器突然报废了,或者机房整个挂掉了,或者机房所在城市发生地震了,我们需要保证公司业务的正常运行,尽量做到业务不中断,或者中断时间非常短(10分钟内切换到另外一个城市的备份上,重新启动业务)。
这篇文章记录了我们搭建“垮IDC异地备份”的实现,对于一个中小型电商而言,它足够可靠(数据延迟在1秒以内,最差情况丢失灾难发生前1秒的数据),它足够廉价,它足够安全(数据不会有任何入侵的可能性),它实现足够简单(普通的运维人员就可以实现)。

1. 选择备份服务器

备份服务器,只是用于存储数据,无所谓性能的要求,选择一个配置最低 + 磁盘容量够大的VPS就足够了。

考虑到网络备份的实时性要求,备份VPS服务器到你的主站服务器之间网络性能要好,目前我们选择的备份服务器到主站服务器之间的 ping 延迟在 20ms 以内。

VPS 选择尽量不要和主服务器在同一个城市或者同一个地区。比如四川发生地震,那边的机房都受到影响,如果你的主服务器和备份服务器都在四川,那备份等于没用了。

目前我们的主服务器和备份服务器分别在中国的东部和中西部不同城市,应该不至于发生从东部一直地震到中西部的情况。只要有一个城市服务器没有受到影响,我们就可以在 10 分钟内恢复整个网站的业务,并且网站交易数据几乎没有损失(备份最多损失灾难前 1秒下的订单)

2. 安全性配置

备份数据都是网站最机密的数据,安全就显得非常重要。目前我们采用 iptables 做 IP 认证,确保只有我们自己的服务器才能相互访问,同时数据传输采用 SSL 加密,这样就算有人做网络监听也没法得到我们的数据。

3. 数据库的实时同步备份

数据库备份采用 Master —> Slave 的方式, Slave 纯粹就是一个实时同步的备份,本身没有任何业务访问它。

注意:要做数据库的 Master —> Slave 配置,最好两个数据库是完全一样的版本,否则你可能会遇到很多莫名的错误。

主服务器配置

修改 mysql 的配置文件 my.cnf 在 [mysqld] 下面加入

## 在 [mysqld] 下面加入

server-id		= 1
log_bin = /var/log/mysql/mysql-bin.log ## 注意,格式使用 MIXED,千万不要使用缺省的 STATEMENT 方式,数据不安全
binlog_format = MIXED
expire_logs_days = 10
max_binlog_size = 100M ## 我们同步除了 cacti 之外其它所有数据库,包括 mysql 系统自身
binlog_ignore_db = cacti

配置的一个重点说明:

binlog_format 千万不要使用 STATEMENT (如果你不写这个配置的话,缺省用的就是 STATEMENT),STATEMENT 好处是同步快,缺点是数据安全没有保障。简单的说,STATEMENT 可以在某些时候会同步失败,并且不能保证数据同步过去一定是正确的。你说你飞快的同步数据库过去,然后发现同步过去的数据有错误,那你丫还同步有啥意义?

重启主服务器  /etc/init.d/mysql stop ;  /etc/init.d/mysql start;

查看主服务器的工作状态

## 登陆 mysql

mysql -h localhost -u root -p ## 输入密码登陆

## 执行查询 SQL 语句
mysql> show master status \G ## 显示如下结果,说明你的主服务器成功启动了,并且已经开始输出 binlog 了 *************************** 1. row ***************************
File: mysql-bin.000004
Position: 8681158
Binlog_Do_DB:
Binlog_Ignore_DB: cacti
1 row in set (0.00 sec)

增加同步账号

Slave 要从主服务复制数据,就需要使用一个账号登陆主服务器,然后从主服务器读取 bin log 文件内容。

自己创建一个 slave 账号,并且赋予 replication slave 权限(只需要这一个权限就足够了,其它的没必要)

mysql> GRANT REPLICATION SLAVE ON *.* TO 'slave'@'Slave服务器IP' IDENTIFIED BY 'password';

配置 Slave 服务器

Slave 服务器处于 另外某个城市的机房。平时 Slave  只是作为一个备份,并没有业务的访问,如果主服务器崩溃(比如机房地震了),我们需要手动切换  Slave 为新的主服务器并且提供服务。

由于平时没有任何业务访问,为了保证安全,我配置 Slave 为 ReadOnly 的,防止数据发生任何形式的破坏

在 Slave 服务器的 [mysqld] 里面加入

server-id		= 2
log_bin = /var/log/mysql/mysql-bin.log ## 我们同时开启 Slave 的 binlog ,这样如果有别的 Slave,他们可以把这个 Slave 当做主库,从这里做同步
## 就不用每个 Slave 都从主库同步,这样可以减轻主库的压力
binlog_format = MIXED
expire_logs_days = 10
max_binlog_size = 100M log-slave-updates ## 设置数据库为 只读,确保数据不会某种原因而被破坏
read-only=1 replicate-ignore-db=cacti

 注意:有些文章会让你把 master 的信息(比如 master-host) 也写到配置文件里面去,我们不建议这么做,你应该在 Slave 启动之后手动设置 master 信息

主库 Dump 数据给 Slave 数据库

网上很多文章都让你用 mysqldump 导出数据,然后在 Slave 再 mysqlimport 导入。这实在是一个傻办法。最简单的办法就是把整个数据库  tar 一下复制过去。

锁住主数据库,确保你 dump 出来的数据是一致的(现在是 read only 了,不能写入库)

## 锁住 Master 数据库
mysql> FLUSH TABLES WITH READ LOCK;

把数据库数据 复制出来(本地复制)

## mysql 的数据文件一般都在 /var/lib/mysql 目录下,我们直接把整个目录复制出来
cp -ar /var/lib/mysql /home/DATA/tmp ## 注意,cp 用了 -a 复制,用于保持文件权限

查看并记录 Master 复制时候的状态

## 执行查询 SQL 语句
mysql> show master status \G ## 显示如下结果,说明你的主服务器成功启动了,并且已经开始输出 binlog 了 *************************** 1. row ***************************
File: mysql-bin.000004
Position: 8681158
Binlog_Do_DB:
Binlog_Ignore_DB: cacti
1 row in set (0.00 sec)

注意:请把这里的  File 和 Position 记录一下,后面配置 Slave 需要使用

提醒: 查看 Master 的状态可以在上面 cp 数据的时候同时进行,反正 cp 也要花一点时间

解锁 Master 恢复数据库的正常访问

我们前面之所以使用本地 cp 来复制数据,目的就是希望这个过程竟可能的快,这样一复制完成就可以把数据库解锁,恢复正常访问了。

## 解锁数据库,数据库可以正常写入了

mysql> UNLOCK TABLES;

到现在为止你的 Master 数据库就释放了,后面不用管它了。

把数据库数据打包发送到 Slave 服务器上

刚才你把数据库  cp 到了哪个目录下? 现在去打包,然后发送到 Slave 服务器上就行了

## 数据库文件打包 ,根据你的数据量大小压缩可能会很慢,耐心等待吧
cd /home/Tmp
tar -cjf mysql.tar.bz2 mysql/ ## 把打包好的文件传送到 Slave 数据库上
## 我们这里采用 rsync,如果你不了解 rsync 的话,你也可以用你熟悉的任何方法
## 包括 scp, ftp, wget .... rsync -v mysql.tar.bz2 slaveIP地址::/SyncData

Slave 数据库导入数据

怎么导入数据? 最简单的方式就是整个替换 mysql 的数据目录

## 停止 mysql 服务器
/etc/init.d/mysql stop ## 删除旧的数据文件
rm -rf /var/lib/mysql ## 把新的数据文件解压,放到数据目录位置
tar -jxf mysql.tar.bz2
mv mysql /var/lib ## 启动 Slave Mysql
/etc/init.d/mysql start ## 设置 Slave 的同步开始状态
mysql -h localhost -u root -p mysql> CHANGE MASTER TO -> MASTER_HOST='master_host_name', -> MASTER_USER='replication_user_name', -> MASTER_PASSWORD='replication_password', -> MASTER_LOG_FILE='前面让你记录下的 master 状态显示的 logfile 名字', -> MASTER_LOG_POS=前面记录下的 postion; ## 启动 Slave 开始复制 mysql> START SLAVE; ## 查看 Slave 的状态 mysql > show slave status \G *************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: IP地址xxxxx
Master_User: slave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 17767065
Relay_Log_File: mysqld-relay-bin.000018
Relay_Log_Pos: 17766187
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB: cacti ## 当你看到 Slave_IO_Running: Yes Slave_SQL_Running: Yes 说明 Slave 已经成功启动了,之后它就会自动同步不用管了 ## 如果没有成功,这里应该会报错,你就根据错误自己查错吧

问题: Master —> Slave 运行起来了,假如发生了 网络断网、数据库服务器重启、 … 那数据库同步怎么办?

回答: 放心,Mysql 已经做得足够完善了,你网络断了,重启服务器了,等你的机器恢复之后 myql 会自动从上次断网的地方继续同步,完全不需要人工干预。

进一步的安全考虑

为了让数据库更加安全,我们在 Slave 上对数据库每隔一个小时就做一次全量 dump (使用 mysqldump),然后 bzip2 压缩保存。

一天 24 个小时,保留 30 天的数据量,也就是  30 * 24 * 数据库dump 压缩文件的大小, 需要不小的磁盘空间,不过现在磁盘空间很便宜,这不是什么问题。

4. 网站文件的同步

一个网站,除了有数据库,还有很多别的文件,比如用户上传的图片,你的网站代码之类,光有数据库而没有这些文件你的网站也没法跑起来。

和数据库比起来,网站文件要大得多,而且文件数目也多得多,要做到实时同步必须考虑“增量文件同步”,一般传统的方法是使用  inotify + rsync 写脚本来操作。

我们这里采用  Sersync (https://code.google.com/p/sersync/) 来做网站数据同步  。

配置 Slave 上的 WWW 同步

采用 Rsync 做数据同步

uid=root
gid=root
use chroot=no
max connections=10
pid file=/var/run/rsyncd.pid
lock file=/var/run/rsync.lock
log file=/var/log/rsyncd.log
munge symlinks = no
hosts allow=这里填写 Master 的 IP 地址
hosts deny=*
list=false
read only=no
ignore errors [WWW]
path=/var/WWW ## 网站数据所在的位置

启动 rsync 服务:  rsync –daemon

启动 Master 上的 Sersync2 做数据同步

<?xml version="1.0" encoding="ISO-8859-1"?>
<head version="2.5">
<host hostip="localhost" port="8008"></host>
<debug start="false"/>
<fileSystem xfs="false"/>
<filter start="true">
<exclude expression="^BZFSHOP/Asset/*"></exclude>
<exclude expression="^BZFSHOP/Runtime/*"></exclude>
<exclude expression="^BZFSHOP/Trash/*"></exclude>
<exclude expression="^BZFSHOP/Data/cache/*"></exclude>
<exclude expression="^BZFSHOP/Data/temp/*"></exclude>
</filter>
<inotify>
<delete start="true"/>
<createFolder start="true"/>
<createFile start="true"/>
<closeWrite start="true"/>
<moveFrom start="true"/>
<moveTo start="true"/>
<attrib start="false"/>
<modify start="false"/>
</inotify> <sersync>
<localpath watch="/var/WWW">
<remote ip="Slave的IP" name="WWW"/>
</localpath>
<rsync>
<commonParams params="-artu"/>
<auth start="false" users="root" passwordfile="/etc/rsync.pas"/>
<userDefinedPort start="false" port="874"/><!-- port=874 -->
<timeout start="false" time="100"/><!-- timeout=100 -->
<ssh start="false"/>
</rsync>
<failLog path="/tmp/www_rsync_fail_log.sh" timeToExecute="3"/><!--default every 3mins execute once-->
<crontab start="false" schedule="600"><!--600mins-->
<crontabfilter start="false">
<exclude expression="*.php"></exclude>
<exclude expression="info/*"></exclude>
</crontabfilter>
</crontab>
<plugin start="false" name="command"/>
</sersync> </head>

启动文件监控同步

## 第一次启动加上 -r 参数做首次同步,之后就不要再加  -r 参数了
./sersync2 -n 2 -d -r -o www.xml

后记

完整的对 数据库 和 所有文件 做了实时同步之后,我们的业务就有了保障。一旦主机房出现严重问题,我们可以使用备份数据立即继续中断的业务,最重要的是,业务数据几乎没有任何的丢失。

不像之前,每天一个备份,如果发生严重故障,只能从“昨天”开始了,中间数据丢失会带来巨大的麻烦。

如果需要更加安全,可以考虑 3 备份或者 N 备份,在 N 个城市同时做实时备份数据,一旦发生多点灾难一样可以迅速恢复业务,当然需要的成本也更高。

电商网站垮IDC数据备份,MySql主从同步,图片及其它数据文件的同步的更多相关文章

  1. Ubuntu实现电商网站+Mysql主从复制+NFS

    Ubuntu实现电商网站+Mysql主从复制+NFS 1.环境准备 提前准备:Mysql8.0.30安装包.Mysql安装脚本.shopxo2.3.0安装包.DNS脚本 服务器 IP地址 作用 系统版 ...

  2. 现有某电商网站用户对商品的收藏数据,记录了用户收藏的商品id以及收藏日期,名为buyer_favorite1。 buyer_favorite1包含:买家id,商品id,收藏日期这三个字段,数据以“\t”分割

    实验内容(mapReduce安装请按照林子雨教程http://dblab.xmu.edu.cn/blog/631-2/) 现有某电商网站用户对商品的收藏数据,记录了用户收藏的商品id以及收藏日期,名为 ...

  3. Django项目之Web端电商网站的实战开发(一)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 目录 一丶项目介绍 二丶电商项目开发流程 三丶项目需求 四丶项目架构概览 五丶项目数据库设计 六丶项目框架搭建 一丶项目介绍 产品 ...

  4. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  5. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

  6. 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

     阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...

  7. 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

    阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...

  8. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

  9. 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域

    一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...

随机推荐

  1. Oracle获取系统时间及格式化

    Oracle 获取当前日期及日期格式 获取系统日期:  SYSDATE()   格式化日期:     TO_CHAR(SYSDATE(),'YY/MM/DD HH24:MI:SS)           ...

  2. Android开发之InstanceState详解(转)---利用其保存Activity状态

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  3. 安装tomcat过程中出现问题小结

    报错信息如下:Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these ...

  4. unity3d 多人寻路堵塞堆叠问题

    使用unity提供的NavMeshAgent寻路,当有多个agent一起寻路时总会出现堵塞堆叠的问题. 本人使用了一个非常粗劣简单的方案解决此问题,当然跟魔兽的寻路完全没得比,但保证有比较好的性能,且 ...

  5. 关于Bean\Entity\Model\POJO的一些个人理解

    本文没有长篇累牍的,严格的,标准的表述,只是我在开发过程中,读书过程中的一些个人理解,可能不太准备,但是我觉得应该是最方便初学者理解的吧? 一.Bean 对于Bean而言,我的理解是只要是Java的类 ...

  6. linux自动启动程序

    下面用自启动apache为例: 有两种方法可以让Apache在系统启动时自动启动   1. 在/etc/rc.d/rc.local中增加启动apache的命令,例如:/usr/local/httpd/ ...

  7. /bin/sh 与 /bin/bash 的区别

    /bin/sh 与 /bin/bash 的区别,用 : 截取字符串不是POSIX 标准的. 区别 sh 一般设成 bash 的软链 (symlink) ls -l /bin/sh lrwxrwxrwx ...

  8. AVR单片机的BOOT区

    BOOT区的由来基于一个简单的道理,即单片机的程序是保存在FLASH中的,要运行程序就必须不停的访问FLASH存储器.对于一般的FLASH存储器,数据的写入需要一定的时间来完成,在数据写入完成之前,存 ...

  9. Android获取手机屏幕宽高

    //如果是获取单位是像素,可以如下: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point( ...

  10. DateTime.CompareTo方法

    DateTime.CompareTo(value)方法,与一个时间比较,返回整数,含义如下: 值 说明 小于零 此实例早于 value. 零 此实例与 value 相同. 大于零 此实例晚于 valu ...