A、B数据库使用otter实时同步,但是有数据不一致情况,需要做数据修复。 因为没有找到合适的工具,所以自己写了些脚本来做处理,配合人工检查和过滤,99.9%的数据得以修复。
思路:
以A、B中的同一张表t举例,首先拿到 A.t 和 B.t 的INSERT语句,提取ID相同的不同数据;
拼接update语句: a. ID为奇数,取A.t中的数据; b. ID为偶数,取B.t中的数据;
在A、B中执行此update即可。
具体实现与思路不一致,下面是具体实现。
第一步,找不同
取表名;
导出每张表,提取数据,每行一条;
比较出两个库不同的部分; 统计结果在stat.txt中,只包含ID相同value不同的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #!/bin/bash . /Users/hero/.zshrc echo "step 1 : 取表名" echo `mysql -uroot -ppasswd -hlocalhost << EOF use shanghai; SELECT table_name FROM information_schema.tables where table_schema='shanghai' and table_name like 'nh%' and table_name not like '%_reference'; EOF ` > tables.txtsed -E -e 's/\t/ /g' -e 's/ +/\n/g' tables.txt | tail -n +2 > alltables rm tables.txtfor i in `cat alltables`;do echo $i done echo 'step 2 : 提取diff' url_from=localhost user_from=root password_from=passwd schema_from=shanghai url_to=localhost user_to=root password_to=passwd schema_to=hongkong rm -rf frommkdir fromrm -rf tomkdir torm -rf diffmkdir difffor i in `cat alltables`; do echo "`date '+%H:%M:%S'` compare table ---> " $i "\n" mysqldump -h$url_from -u$user_from -p$password_from --single-transaction $schema_from $i -r from/${i} mysqldump -h$url_to -u$user_to -p$password_to --single-transaction $schema_to $i -r to/${i} grep "^INSERT" from/${i} | sed -e 's/INSERT.*VALUES //g' -e 's/),/)\n/g' -e 's/);/)\n/g' > from/${i} .ok grep "^INSERT" to/${i} | sed -e 's/INSERT.*VALUES //g' -e 's/),/)\n/g' -e 's/);/)\n/g' > to/${i} .ok diff from/${i} .ok to/${i} .ok > diff/$i done find diff/ -empty -delete echo '统计' FILE=stat.txt TMP=stat.txt.tmp >$FILE >$TMP for i in `ls diff/`; do sh=`grep '^<' diff/$i | wc -l` ho=`grep '^>' diff/$i | wc -l` echo $i $sh $ho >> $TMP done sort -n -k 2 $TMP > $FILE rm $TMP echo '统计结果:stat.txt' mv stat.txt stat.txt.oldcp stat.txt.old stat.txt.`date +%Y%m%d%H%M%S`awk -F '[ ]' '{if($2==$3)printf("%s\n", $0)}' stat.txt.old >> stat.txt
第二步,根据ID奇偶取数 在取ID的时候有一个假设,即每个表的ID都是第一个字段。 这样有个问题,ID不在第一个的表无法拿到它的ID;不过问题不大,因为我会对batch.sql做筛选。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #!/bin/bash LOG=log >$LOG exec 1>>$LOG 2>&1rm -rf extrmkdir extrfor i in `ls diff/`; do grep '^>' diff/$i > extr/$i done find extr/ -empty -delete rm -rf idsmkdir idsfor i in `ls extr/`; do awk -F '[(,]' '{printf("%s\n", $2)}' extr/$i > ids/${i} .tmp cat ids/${i} .tmp | grep -v '^$' | sort | uniq > ids/$i rm ids/${i} .tmp done >batch.sql SHANG=shanghai HONG=hongkong rm -rf jioumkdir jioufor i in `ls ids/`; do while read id ; do echo "$id $i " if (( (( $id % 2 )) == 0 )); then schema=$HONG else schema=$SHANG fi echo "mysqldump -hlocalhost -uroot -ppasswd --single-transaction $schema $i --where=\"id=$id \" >> jiou/$i " >> batch.sql done < ids/$i done echo 'get batch.sql' rm -rf jioumkdir jiou echo '有问题的表:' grep -arl "NULL" ids/ grep '^NULL' $LOG echo '根据log筛选batch.sql, 然后执行 sh batch.sql'
一定要记得根据log对batch.sql做筛除,然后执行batch.sql,这样就拿到了根据ID的奇偶性获取的元数据
第三步,insert、update、remove_tmp脚本 对元数据处理,拿到insert语句。创建临时表xxx_tmp将数据插入临时表中,然后根据临时表来更新A(B),最后删除临时表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #!/bin/bash rm -rf insertmkdir insertfor i in `ls jiou/`; do grep -aoE 'INSERT .*);' jiou/$i > insert/$i done rm -rf structmkdir structfor i in `ls insert/`; do echo `mysql -uroot -ppasswd -hlocalhost <<-EOF select column_name from information_schema.columns where table_schema='hongkong' and table_name='$i' EOF ` > struct/$i gsed -e 's/ /\n/g' struct/$i | tail -n +3 > struct/${i} .ok done >update.sql >insert.sql >remove_tmp.sql for i in `ls insert/`; do echo "create table ${i} _tmp as select * from $i where 1!=1;" >> insert.sql echo "drop table ${i} _tmp;" >> remove_tmp.sql gsed -i "s/${i} /${i} _tmp/g" insert/$i cat insert/$i >> insert.sql printf %s "update $i a, ${i} _tmp b set " >> update.sql awk '{printf("a.%s=b.%s,", $1, $1)}' struct/${i} .ok >> update.sql echo " where a.id=b.id;" >> update.sql done gsed -i 's/, where/ where/g' update.sql echo '执行insert.sql-->update.sql--->remove_tmp.sql' echo "mysql -uroot -ppasswd --binary-mode=1 < insert.sql"
验证 新建一个子目录,然后执行第一个脚本,拿到stat.txt验证数据修复结果。