目前開發(fā)的平臺(tái)使用單體MySQL數(shù)據(jù)庫岩灭,數(shù)據(jù)量激增后陸陸續(xù)續(xù)遇到了一些問題笆搓,此處做下總結(jié)膏蚓。
單體數(shù)據(jù)庫會(huì)遇到以下幾個(gè)問題:
1实愚、需要定時(shí)備份數(shù)據(jù)并上傳到云存儲(chǔ)兼呵,防黑客攻擊和刪庫;
2腊敲、需要有個(gè)備份數(shù)據(jù)庫击喂,數(shù)據(jù)庫掛了或者不見了需要馬上頂上;
3碰辅、讀的次數(shù)遠(yuǎn)大于寫的時(shí)候懂昂,寫鎖和讀鎖的爭用導(dǎo)致性能下降,單個(gè)物理機(jī)的并發(fā)壓力大没宾;
4凌彬、單體數(shù)據(jù)表太大索引性能下降沸柔,就需要把表拆分;
5铲敛、單庫中表數(shù)量太多整體降低性能褐澎,就需要按業(yè)務(wù)分成多個(gè)庫。
上面特意說復(fù)雜了伐蒋,解決辦法歸納一下就是幾個(gè)點(diǎn):
1工三、冷備份(定時(shí)全量/增量備份)
2、熱備份(在主從架構(gòu)上實(shí)現(xiàn))
3先鱼、主從架構(gòu)(N主M從)
4俭正、讀寫分離(在主從架構(gòu)上實(shí)現(xiàn))
5、數(shù)據(jù)分片(分庫分表)
冷備份
參考文章:https://blog.csdn.net/zone_/article/details/81293131
分三個(gè)步驟:
1焙畔、每周全量備份輸出sql文件掸读,并壓縮;
2宏多、每天增量備份輸出bin-log文件(需要更改數(shù)據(jù)庫配置并重啟生效)寺枉;
3、備份完就調(diào)用腳本上傳到七牛云绷落。
全量備份:
#!/bin/bash
#在使用之前姥闪,請?zhí)崆皠?chuàng)建以下各個(gè)目錄
#獲取當(dāng)前時(shí)間
date_now=$(date "+%Y%m%d-%H%M%S")
backUpFolder=/home/db/backup/mysql
username="root"
password="xtionai"
db_name="zone"
#定義備份文件名
fileName="${db_name}_${date_now}.sql"
echo "${fileName}"
#定義備份文件目錄
backUpFileName="${backUpFolder}/${fileName}"
echo "starting backup mysql ${db_name} at ${date_now}."
#進(jìn)入到備份文件目錄
cd ${backUpFolder}
/usr/bin/mysqldump -u${username} -p${password} --lock-all-tables --flush-logs ${db_name} > ${backUpFileName}
#壓縮備份文件
tar zcvf ${fileName}.tar.gz ${fileName} --remove-files
# use nodejs to upload backup file other place
#NODE_ENV=$backUpFolder@$backUpFileName node /home/tasks/upload.js
date_end=$(date "+%Y%m%d-%H%M%S")
echo "finish backup mysql database ${db_name} at ${date_end}."
# 使用 python 上傳備份文件到 私有云
python /home/ubuntu/aiplat/upload.py $backUpFolder/ ${fileName}.tar.gz
增量備份,這里還是需要開啟mysql的bin-log功能的砌烁,看參考文獻(xiàn):
backupDir=/home/db/backup/mysql-daily
#增量備份時(shí)復(fù)制mysql-bin.00000*的目標(biāo)目錄筐喳,提前手動(dòng)創(chuàng)建這個(gè)目錄
mysqlDir=/var/lib/mysql
#mysql的數(shù)據(jù)目錄
logFile=/home/ubuntu/aiplat/increment.log
BinFile=/var/lib/mysql/mysql-bin.index
#mysql的index文件路徑,放在數(shù)據(jù)目錄下的
mysqladmin -uroot -pxtionai flush-logs
#這個(gè)是用于產(chǎn)生新的mysql-bin.00000*文件
# wc -l 統(tǒng)計(jì)行數(shù)
# awk 簡單來說awk就是把文件逐行的讀入函喉,以空格為默認(rèn)分隔符將每行切片避归,切開的部分再進(jìn)行各種分析處理。
Counter=`wc -l $BinFile |awk '{print $1}'`
NextNum=0
#這個(gè)for循環(huán)用于比對$Counter,$NextNum這兩個(gè)值來確定文件是不是存在或最新的
for file in `cat $BinFile`
do
base=`basename $file`
echo $base
#basename用于截取mysql-bin.00000*文件名管呵,去掉./mysql-bin.000005前面的./
NextNum=`expr $NextNum + 1`
if [ $NextNum -eq $Counter ]
then
echo $base skip! >> $logFile
else
dest=$backupDir/$base
if(test -e $dest)
#test -e用于檢測目標(biāo)文件是否存在梳毙,存在就寫exist!到$logFile去
then
echo $base exist! >> $logFile
else
cp $mysqlDir/$base $backupDir
echo $base copying >> $logFile
# 使用 python 上傳備份文件到 私有云
echo $backupDir/$base >> $logFile
python /home/ubuntu/aiplat/upload.py $backupDir/ $base
fi
fi
done
echo `date +"%Y年%m月%d日 %H:%M:%S"` $Next Bakup succ! >> $logFile
#執(zhí)行上傳備份文件到七牛云
#NODE_ENV=$backUpFolder@$backUpFileName /root/node/v8.11.3/bin/node /usr/local/upload.js
# 使用 python 上傳備份文件到 私有云
#python /home/ubuntu/aiplat/upload.py $backupDir $backUpFileName
上傳腳本,python寫的捐下,版本隨意:
from qiniu import Auth, put_file, etag
import sys
print('參數(shù)個(gè)數(shù)為:', len(sys.argv), '個(gè)參數(shù)账锹。')
print('參數(shù)列表:', str(sys.argv))
# backUpFolder
backUpFolder = sys.argv[1]
# backUpFileName
backUpFileName = sys.argv[2]
import qiniu.config
# 需要填寫你的 Access Key 和 Secret Key
access_key = 'xxx'
secret_key = 'xxx'
# 構(gòu)建鑒權(quán)對象
q = Auth(access_key, secret_key)
# 要上傳的空間
bucket_name = 'aiimage'
# 上傳到七牛后保存的文件名
key = backUpFileName
# 生成上傳 Token,可以指定過期時(shí)間等
token = q.upload_token(bucket_name, key, 3600)
# 要上傳文件的本地路徑
localfile = backUpFolder + backUpFileName
ret, info = put_file(token, key, localfile)
print(info)
assert ret['key'] == key
assert ret['hash'] == etag(localfile)
最后設(shè)置crontab 定時(shí)任務(wù)坷襟,輸入命令crontab -e
設(shè)置下面的就好了:
0 0 * * 0 sudo sh /home/dbbackup/all.sh >/home/dbbackup/cron.log
0 0 * * * sudo sh /home/dbbackup/increment.sh >/home/dbbackup/cron.log
主從架構(gòu)
參考文章:https://blog.csdn.net/qq_22152261/article/details/80374990
分三個(gè)步驟:
1奸柬、主從都開啟bin-log
2、主設(shè)置帳號(hào)權(quán)限組給從用(記得指定IP)
3婴程、從設(shè)置主的信息并啟動(dòng)slave模式
主設(shè)置的命令:
GRANT REPLICATION SLAVE ON *.* TO 'user'@'X.X.X.X' IDENTIFIED BY 'password';
從設(shè)置的命令:
change master to master_host='x.x.x.x', master_user='root', master_password='123456', master_log_file='mysql-bin.000003',master_log_pos=669835806,master_port=3307; start slave;
命令中的master_log
取決于主執(zhí)行命令show master status;
出來的數(shù)據(jù)廓奕。
自己去主庫測試下,新建個(gè)數(shù)據(jù)庫就可以了,從庫也會(huì)跟著建桌粉。
熱備份
在一主多從的架構(gòu)之下蒸绩,理論上主宕機(jī)之后,必須馬上選舉一個(gè)從來充當(dāng)主的角色铃肯,而這種行為叫做熱備
患亿。
而這個(gè)切換的過程分為手動(dòng)和自動(dòng),但是都沒辦法避免一個(gè)問題:第三方工具可以推舉出新的主缘薛,但是沒辦法通知應(yīng)用層
窍育。
目前是一般手動(dòng)更新主庫卡睦,也手動(dòng)更新應(yīng)用層的數(shù)據(jù)庫配置宴胧。
讀寫分離
在一主多從的架構(gòu)之下,讀寫分離成為可能表锻,主為寫庫恕齐,從為讀庫。
使用SpringBoot + Sharding-jdbc實(shí)現(xiàn)讀寫分離瞬逊,分為三步:
1显歧、設(shè)置好主從庫;
2确镊、引入maven包士骤;
3、寫好yaml配置文件
一般推薦用docker+mysql做主從庫蕾域,方便拷肌!參考文章:https://www.cnblogs.com/sweetchildomine/p/7814692.html
我的docker啟動(dòng)命令:
#slave0 是從庫
docker run -d -e MYSQL_ROOT_PASSWORD=123456 -v /home/mysql/cnf/s0.cnf:/etc/mysql/my.cnf -v /home/mysql/slave0_data:/var/lib/mysql -p 3308:3306 mysql:5.7
#master是主庫
docker run -d -e MYSQL_ROOT_PASSWORD=123456 -v /home/mysql/cnf/m.cnf:/etc/mysql/my.cnf -v /home/mysql/master_data:/var/lib/mysql -p 3307:3306 mysql:5.7
我的my.cnf文件內(nèi)容,主從庫一模一樣旨巷,除了serverid不能一樣巨缘!
# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# The MySQL Community Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
[mysqld_safe]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp
log-bin = mysql-bin
server-id = 10001
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
#log-error = /var/log/mysql/error.log
# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# * IMPORTANT: Additional settings that can override those from this file!
# The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/
引入maven包:
HikariCP連接池是必須的,所以我們需要剔除掉默認(rèn)的tomcat-jdbc連接池采呐。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
后面就是寫yml配置了:
具體配置的解釋看官網(wǎng):http://shardingsphere.io/document/current/cn/overview/
sharding:
jdbc:
datasource:
names: master,slave0
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3307/aiplattest?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
slave0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3308/aiplattest?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
config:
masterslave:
load-balance-algorithm-type: round_robin
name: ds_ms
master-data-source-name: master
slave-data-source-names:
- slave0
props:
sql:
show: true
數(shù)據(jù)分片
數(shù)據(jù)分片:指的是 垂直分庫 和 水平分表若锁。
目前主流分庫分表框架是sharding-jdbc和mycat,核心就是開發(fā)者不需要管分了多少個(gè)庫斧吐,多少個(gè)表又固,對于開發(fā)只有多個(gè)庫單個(gè)表,就是物理上的庫和邏輯上的表煤率。
比如:訂單庫的order表分成order_01,order_02,order_03,我們用的時(shí)候直接就是用order表就好了口予,剩下的由框架去組合,當(dāng)然這個(gè)也是要配置的涕侈,參考官網(wǎng)配置http://shardingsphere.io/document/current/cn/overview/
沪停。
dataSources: #數(shù)據(jù)源配置,可配置多個(gè)data_source_name
<data_source_name>: #<!!數(shù)據(jù)庫連接池實(shí)現(xiàn)類> `!!`表示實(shí)例化該類
driverClassName: #數(shù)據(jù)庫驅(qū)動(dòng)類名
url: #數(shù)據(jù)庫url連接
username: #數(shù)據(jù)庫用戶名
password: #數(shù)據(jù)庫密碼
# ... 數(shù)據(jù)庫連接池的其它屬性
shardingRule:
tables: #數(shù)據(jù)分片規(guī)則配置,可配置多個(gè)logic_table_name
<logic_table_name>: #邏輯表名稱
actualDataNodes: #由數(shù)據(jù)源名 + 表名組成木张,以小數(shù)點(diǎn)分隔众辨。多個(gè)表以逗號(hào)分隔,支持inline表達(dá)式舷礼。缺省表示使用已知數(shù)據(jù)源與邏輯表名稱生成數(shù)據(jù)節(jié)點(diǎn)鹃彻。用于廣播表(即每個(gè)庫中都需要一個(gè)同樣的表用于關(guān)聯(lián)查詢,多為字典表)或只分庫不分表且所有庫的表結(jié)構(gòu)完全一致的情況
databaseStrategy: #分庫策略妻献,缺省表示使用默認(rèn)分庫策略蛛株,以下的分片策略只能選其一
standard: #用于單分片鍵的標(biāo)準(zhǔn)分片場景
shardingColumn: #分片列名稱
preciseAlgorithmClassName: #精確分片算法類名稱,用于=和IN育拨。谨履。該類需實(shí)現(xiàn)PreciseShardingAlgorithm接口并提供無參數(shù)的構(gòu)造器
rangeAlgorithmClassName: #范圍分片算法類名稱,用于BETWEEN熬丧,可選笋粟。。該類需實(shí)現(xiàn)RangeShardingAlgorithm接口并提供無參數(shù)的構(gòu)造器
complex: #用于多分片鍵的復(fù)合分片場景
shardingColumns: #分片列名稱析蝴,多個(gè)列以逗號(hào)分隔
algorithmClassName: #復(fù)合分片算法類名稱害捕。該類需實(shí)現(xiàn)ComplexKeysShardingAlgorithm接口并提供無參數(shù)的構(gòu)造器
inline: #行表達(dá)式分片策略
shardingColumn: #分片列名稱
algorithmInlineExpression: #分片算法行表達(dá)式,需符合groovy語法
hint: #Hint分片策略
algorithmClassName: #Hint分片算法類名稱闷畸。該類需實(shí)現(xiàn)HintShardingAlgorithm接口并提供無參數(shù)的構(gòu)造器
none: #不分片
tableStrategy: #分表策略尝盼,同分庫策略
keyGeneratorColumnName: #自增列名稱,缺省表示不使用自增主鍵生成器
keyGeneratorClassName: #自增列值生成器類名稱佑菩。該類需實(shí)現(xiàn)KeyGenerator接口并提供無參數(shù)的構(gòu)造器
logicIndex: #邏輯索引名稱盾沫,對于分表的Oracle/PostgreSQL數(shù)據(jù)庫中DROP INDEX XXX語句,需要通過配置邏輯索引名稱定位所執(zhí)行SQL的真實(shí)分表
bindingTables: #綁定表規(guī)則列表
- <logic_table_name1, logic_table_name2, ...>
- <logic_table_name3, logic_table_name4, ...>
- <logic_table_name_x, logic_table_name_y, ...>
defaultDataSourceName: #未配置分片規(guī)則的表將通過默認(rèn)數(shù)據(jù)源定位
defaultDatabaseStrategy: #默認(rèn)數(shù)據(jù)庫分片策略倘待,同分庫策略
defaultTableStrategy: #默認(rèn)表分片策略疮跑,同分庫策略
defaultKeyGeneratorClassName: #默認(rèn)自增列值生成器類名稱,缺省使用io.shardingsphere.core.keygen.DefaultKeyGenerator凸舵。該類需實(shí)現(xiàn)KeyGenerator接口并提供無參數(shù)的構(gòu)造器
masterSlaveRules: #讀寫分離規(guī)則祖娘,詳見讀寫分離部分
<data_source_name>: #數(shù)據(jù)源名稱,需要與真實(shí)數(shù)據(jù)源匹配啊奄,可配置多個(gè)data_source_name
masterDataSourceName: #詳見讀寫分離部分
slaveDataSourceNames: #詳見讀寫分離部分
loadBalanceAlgorithmClassName: #詳見讀寫分離部分
loadBalanceAlgorithmType: #詳見讀寫分離部分
configMap: #用戶自定義配置
key1: value1
key2: value2
keyx: valuex
props: #屬性配置
sql.show: #是否開啟SQL顯示渐苏,默認(rèn)值: false
executor.size: #工作線程數(shù)量,默認(rèn)值: CPU核數(shù)
configMap: #用戶自定義配置
key1: value1
key2: value2
keyx: valuex