应用场景:游戏中经常需要给玩家回档或者通过玩家某个时间点的档来定位问题,常用的手段是通过binlog恢复一个备库,我们的游戏运营4年多,恢复一次要2-3小时,成本比较大。这样导致很多问题不能及时得到跟踪。
解决思路:确定好玩家具体的档案时间点,然后找到对应时间点binlog文件,直接从binlog中提取玩家数据
效果实测:500m的binlog分片文件,download + dump + trans 总共3分钟搞定,主要时间花在从云上下载binlog分片文件,实际的转换过程只需要不到10秒钟
备注:最初的提取版本是用php实现的,挺繁琐,最后用awk实现了一版,相当精巧,越来越喜欢awk了
直接上代码 ,外面调用 run.sh
#!/bin/bash
#提取binlog文件名,阿里云格式
target=$(echo "$1" | awk '{match($0, /mysql-bin\.[0-9]*/); print substr($0, RSTART, RLENGTH);}')
binlog=binlog/$target
#从云上拉取binlog文件到本地
wget -O $binlog "$1"
#将binlog导出为sql文件
mysqlbinlog -v $binlog > data/tmp
#提取玩家数据,转换为最初的玩家blob数据
LC_ALL=C awk -f read_binlog.awk -v target=$2 data/tmp
上面是总流程,具体用法
#给定binlog文件url,给定要提取玩家编号
bash run.sh 'binlog_url' 30741565
#read_binlog.awk
function init()
{
start_flag = 0;
real_start = 0;
bingo = 0;
patt = "### @1="target;
find_user = 0;
}
function trans_blob_by_mysql(target, blob)
{
print target","blob > "data/trans.txt";
cmd_replace = "php trans.php data/trans.txt";
system(cmd_replace);
}
BEGIN{
init();
}
{
if(/# at /)
{
init();
next;
}
if(/### UPDATE `sgame_user_db_1`.`user`/)
{
start_flag = 1;
next;
}
if(start_flag == 1 && /### SET/)
{
real_start = 1;
next;
}
#if(real_start == 1 && $0 == "### @1="target)
if(real_start == 1 && $0 ~ patt)
{
print "bingo";
bingo = 1;
next;
}
if(bingo == 1 && /### @6=/)
{
blob = substr($0, length("### @6='") + 1, length($0) - length("### @6='") - 1);
print "load success "target;
trans_blob_by_mysql(target, blob);
find_user = 1;
exit 1;
}
}
END{
if(find_user != 1)
{
print "no such user";
}
}
也就是定位到具体的update语句,从中提取出对应的字段内容,然后这段内容是转移过的数据,不能直接用,需要重新灌入mysql然后读取出来,类似 php实现从mysql备份sql文件中提取特定数据_cleanfield的专栏-CSDN博客案例场景:玩家数据打包为一个blob存在mysql中,需要g’nhttps://blog.csdn.net/cleanfield/article/details/30514289php做转换的代码如下(trans.php)
<?php
function write_data_detail($filename, $data)
{
$fp = fopen("./data/$filename", 'wb');
fwrite($fp, $data);
fclose($fp);
}
function trans_user_data($uid, $data)
{
$db_user = "root";
$db_password = "123456";
$db_host = "127.0.0.1";
$con = mysqli_connect($db_host,$db_user,$db_password);
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
$query_str = "replace into trans_user_data.user(iUserId, bUser) values($uid, '$data');";
$result = mysqli_query($con, $query_str);
if(!$result)
{
die("$query_str".mysqli_error($con));
}
$query_str = "select iUserId, bUser from trans_user_data.user where iUserId = $uid;";
$result = mysqli_query($con, $query_str);
if(!$result)
{
die("$query_str".mysqli_error($con));
}
else
{
if (mysqli_num_rows($result) == 0)
{
mysqli_free_result($result);
die( "no such uids ".$uid);
}
else
{
while($row = mysqli_fetch_assoc($result)) {
write_data_detail($row["iUserId"], $row["bUser"]);
}
mysqli_free_result($result);
}
}
}
function write_data($uid, $data)
{
write_data_detail("$uid"."_dump.rar", $data);
trans_user_data($uid, $data);
}
function read_data($file_name)
{
$content = file_get_contents($file_name);
$array = explode(",", $content, 2);
write_data($array[0], $array[1]);
}
read_data($argv[1]);
?>