最近居然被 MySQL 主從同步的問題坑了, 簡直丟盡了老司機(jī)的臉, 總結(jié)一下.
問題很簡單, 一個業(yè)務(wù)由于 MySQL 主從同步延遲導(dǎo)致讀取的數(shù)據(jù)有問題. 問題解決了, 但如何在 AWS RDS 中獲取 MySQL 的延遲信息呢? 非 AWS RDS 的傳統(tǒng) MySQL 中, 可以直接連到 server 通過 SHOW SLAVE STATUS
獲取延遲信息.
RDS 呢?
0x00 無處不在的 Cloudwatch
AWS 中大多數(shù)(我也不確定是不是所有服務(wù))都接入了 Cloudwatch. Cloudwatch 的好處就是可以作為一個中間層抽象, 將不同系統(tǒng)的數(shù)據(jù)抽象成一個模型, 統(tǒng)一通過 Cloudwatch API 訪問. 就拿主從延遲來說, MySQL/MariaDB 和 PostgeSQL 的計算方法顯然是不一樣的:
- MySQL/MariaDB: the
Seconds_Behind_Master
field of theSHOW SLAVE STATUS
command - PostgreSQL:
SELECT extract(epoch from now() - pg_last_xact_replay_timestamp()) AS slave_lag
- 官方文檔參見 Working with PostgreSQL, MySQL, and MariaDB Read Replicas
因此, 只要通過 Cloudwatch API 獲取 ReplicaLag
這個 metric 的值就可以判斷主從同步延遲, 不管是哪種 DB
def get_cloudwatch_replica_lag(rds_id, start_time, end_time)
local_timezone = pytz.timezone('Asia/Shanghai')
cloudwatch = boto3.client('cloudwatch')
start_time_with_tz = local_timezone.localize(start_time)
end_time_with_tz = local_timezone.localize(end_time)
values = cloudwatch.get_metric_statistics(Namespace='AWS/RDS',
MetricName='ReplicaLag',
Dimensions=[dict(Name='DBInstanceIdentifier', Value=rds_id)],
StartTime=start_time_with_tz,
EndTime=end_time_with_tz,
Period=60,
Statistics=['Maximum'],
Unit='Seconds').get('Datapoints')
return values
0x01 Cloudwatch API "進(jìn)城手冊"
看上去挺簡單的 API, 還是需要"進(jìn)城手冊", 避免撓頭:
- Cloudwatch API 中的傳入的時間必須攜帶時區(qū), 默認(rèn)是 UTC, 當(dāng)然, 如果你廠高瞻遠(yuǎn)矚的都按照 UTC 來使用時間了就不用操這個心了
-
DBInstanceIdentifier
必須存在才會有數(shù)據(jù), 若傳入一個錯誤的DBInstanceIdentifier
返回結(jié)果是空, 不要以為是沒有 lag, 因此建議在 Cloudwatch API 前面加一個 RDS 的DescribeInstance
調(diào)用, 確定DBInstanceIdentifier
的值是正確的 - 返回結(jié)果的
datetime
也是 UTC - Cloudwatch 雖然是監(jiān)控數(shù)據(jù), 但也是需要 IAM 權(quán)限的, 需要加上 IAM 中加上
cloudwatch:GetMetricStatistics
權(quán)限 -
Period=60
代表獲取1分鐘粒度的數(shù)據(jù), 這是 Cloudwatch 支持的最細(xì)顆粒度的數(shù)據(jù)
{
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricStatistics"
],
"Resource": [
"*"
]
}
0x02 檢查 ReplicaLag 的時間間隔
由于 Cloudwatch 支持的最細(xì)顆粒度的 metric 是1分鐘, 因此僅僅獲取前一分鐘的數(shù)據(jù)可能會有 Cloudwatch 數(shù)據(jù)還未抓取到的問題.
建議是獲取前一段時間(比如10分鐘)的數(shù)據(jù), 確保前10分鐘的 ReplicaLag 都為0(或者小于一個可以接受的值), 則認(rèn)為現(xiàn)在的狀態(tài)是滿足數(shù)據(jù)需求的.
總結(jié)
MySQL 主從同步從入行就知道是需要重點關(guān)注的, 結(jié)果還是忽略了一下就掉坑里了. AWS Cloudwatch 也支持根據(jù) ReplicaLag 的值直接告警的, 建議一定要設(shè)置一個.