这是学习笔记的第2278篇文章 今天提前下班了,突然有个开发同学急冲冲找过来说有个业务需求需要紧急支持下。 大体的背景是有一张表中的数据目前存在一些冗余的记录,从业务层面来看这些看起来冗余的数据是某些虚拟福利会被重复领取,所以需要马上做下限制,根据用户的基本属性(比如userid,usercode)进行唯一性标识。 因为这个服务的使用率不是很高,重复领取的情况确实存在,但是频率相对不高,如果活动大量推送后期会有很大的业务损失,所以修复这个潜在问题迫在眉睫。 所以直接的操作就是altertablexxxxadduniquekey(xxx) 但是显然会失败,因为表中存在冗余数据,需要先完成数据清理的工作才可行。 和开发同学沟通后,发现实际的数据清理需求比想象的要略复杂一些,一方面要按照业务特点删除一些已有的数据,然后才按照冗余数据的写入情况清理,数据表为rcuserinfo,数据量不是很大,大概是30万左右,转换为数据操作大体如下: 1)按照业务属性删除部分数据,删除ustatus2的数据 2)按照字段uuser,ucode组合清理冗余数据,只保留最新的数据记录(字段useinfoid是流水号)即可。 这个操作时间紧,数据质量要求高,而且需要保证整个过程可以追溯和回退,于是我快速设计了如下的操作方式。 在test数据库中复制数据,模拟整个数据清理和创建唯一性索引的完整过程,待验证确认后,在线上环境进行数据清理和变更。 比如样例数据如下: 经过一通清理之后,需要保留的数据仅为1条,即useinfoid61543的数据记录,其中红色框住的数据是ustatus2的数据,需要清理,然后按照useinfoid进行排序,取最新的值即可。 我和开发同学讨论后,整理的大体的步骤如下: 第一步:备份rcuseinfo createtabletest。rcuseinfolikeuserdb。rcuseinfo; insertintotest。rcuseinfoselectfromuserdb。rcuseinfo; 第二步:删除ustatus2的记录 deletefromtest。rcuseinfowhereustatus2; 第三步:删除ustatus1的(uuserucode)存在多条记录的数据,保留一条 1)在此创建了3张临时表,tmpdupuser是冗余的数据,直接提取max(useinfoid) createtabletmpdupuser(useinfoidbigint,uuservarchar(100),ucodevarchar(100)); insertintotest。tmpdupuserselectmax(useinfoid),uuser,ucodefromtest。rcuseinfogroupbyuuser,ucodehavingcount()2; 3978rowsinset(0。34sec) 2)tmpdupuseruniq1是当前数据中没有重复记录的用户信息 createtabletmpdupuseruniq1(useinfoidbigint,uuservarchar(100),ucodevarchar(100)); insertintotest。tmpdupuseruniq1selectmax(useinfoid),uuser,ucodefromtest。rcuseinfogroupbyuuser,ucodehavingcount()1; 3)deleteid仅存储需要删除的冗余数据id createtabledeleteid(useinfoidbigintprimarykey); insertintodeleteidselectuseinfoidfromtest。rcuseinfowhereuseinfoidnotin(selectuseinfoidfromtmpdupuseruniq1) anduseinfoidnotin(selectuseinfoidfromtmpdupuser) 4)按照id清理冗余数据 deletefromtest。rcuseinfowhereuseinfoidin(selectuseinfoidfromdeleteid); 5)快速验证,按照预期,输出结果应该是0条 selectuseinfoidfromtest。rcuseinfowhereuseinfoidnotin(selectuseinfoidfromtmpdupuseruniq1) anduseinfoidnotin(selectuseinfoidfromtmpdupuser) 第四步:建立唯一索引(uuserucode) altertabletest。rcuseinfoadduniquekeyidxuuserucode(uuser,ucode); 第五步:清理线上数据,建立唯一索引(uuserucode) 1)正式备份线上数据 createtabletest。rcuseinfobaklikeuserdb。rcuseinfo; insertintotest。rcuseinfobakselectfromuserdb。rcuseinfo; 2)清理线上的业务数据 deletefromuserdb。rcuseinfowhereustatus2; 3)按照deleteid清理冗余数据 deletefromuserdb。rcuseinfowhereuseinfoidin(selectuseinfoidfromtest。deleteid); 4)提前唯一性索引 altertableuserdb。rcuseinfoadduniquekeyidxuuserucode(uuser,ucode); 整个过程有很多改进之处,相对来说,每一步都可以衡量,而且可以根据整个推演的过程计算出应该变更的数据量,整个过程就像是做一道计算题。 一旦某个业务的数据按照现有逻辑清理存在问题,也可以及时进行追溯和调整。