innodb自增id删除重用问题
背景:测试在掉电场景中,先掉电订阅服务节点,再掉电数据库节点,上电后监控界面有一个应用始终没有数据,采集确认一直在上报。
根因:在删除最大id的指标项后,mysql发生重启,导致AUTO_INCREMENT自增id重用之前分配过的id。指标同步采用>lastMaxId条件查询导致查不出来,丢失数据。
查看资料,mysql采用执行类似select max(id)+1 from t1;方法来得到AUTO_INCREMENT,而这种方法就会造成自增id重复的原因。而这个是innodb的特性,不是bug,为了快速产生自增序列,保存在内存里,重启就没了。而myisam引擎没有这个问题,我们使用的是innodb引擎。
方案一:修改创表语句,把innodb改为myisam。
由于数据库组宣称不支持任何innodb以外的mysql,虽然myisam在不使用事务时比innodb快,还是不能使用。
方案二:修改指标同步记录的id为utc时间戳,采用utc>=lastMaxUtc查询需要同步的指标项。
由于utc每次使用>=都能命中最后一条已经同步过的记录,重复操作。并且,utc是在插入前组织参数时使用系统当前时间,由于网络延迟等因素会出现utc小的后插入的现象,在并发的世界里,再小的概率也能发生。还是会漏网之鱼发生。
方案三:和方案二逻辑相同,utc字段使用mysql默认系统时间 DEFAULT CURRENT_TIMESTAMP。
没有漏网之鱼,但还是会出现方案二里重复查询的问题。
方案四:不使用mysql自增id,自己分配id。在redis里存储1000个id,每次取出一个;没有了再申请1000个id,把max id存到mysql表里记录起来。
由于id自分配,解决了id重用问题,不过在并发场景下,同方案二,会出现id小的后插入。
方案五:创建触发器,在mysql端插入前指定AUTO_INCREMENT。创建表记录max id,查询自增序列,如果小于max id,就设置AUTO_INCREMENT为maxId+1。插入后每次更新max id。
并发场景下,更新maxId延迟,mysql重启,更新操作没有及时触发。自增序列小于maxId也可能会误判,导致在没有发生重启的情况下指定了自增初始值,id重复问题。
方案六:不使用mysql自增id,创建触发器,在mysql端生成id,插入前把id指定。创建表记录maxId:锁记录maxId的表,更新maxId=maxId+1,查询maxId并指定id,释放锁。
能保证id分配不重复也是顺序插入的,但是在并发场景下,排它锁表会导致单线程操作,性能下降明显。在异常场景下,如果没有释放锁表,会导致插入卡住,需要人工干预释放锁表。
方案七:在网上看到设置innodb参数控制,全局参数innodb_autoinc_persistent=on,可以解决innodb重启采用maxId问题。
实时更新存储自增序列到文件里,会导致性能1%下降,数据库组也不会开特例增加参数。
方案八:晚上在班车上突然想到设置id为DEFAULT CURRENT_TIMESTAMP,不过马上就担心会重复,果然百度一下,有人遇到重复id问题,mysql端并发时系统设置时间戳也会重复。
想了各种绕开innodb自身维护自增序列方案,感觉都有漏洞或者重复操作问题。本身就是可靠性问题,不敢抱有一点侥幸心理。早上在班车上又想到一个比较挫的方案(请叫我方案“挫”):
看到淘宝一个大拿写到:大型网站架构设计方案,有时往往常规的高大上的理论都失效了,一些比较笨拙的方法往往能解决问题。
分析:innodb重启采用表里maxId+1作为自增序列初始值,那么不要删这个老大了,惹不起它,有它顶雷小弟们都安全。君子报仇十年不晚,柿子捡软的捏,那么在老化删除指标时,只删除软柿子,老大暂时不动,标记下次再收拾它(柿子变软,秋后算账)。
具体做法:
1.在calc老化删除里,先查询maxId以及其它字段,再查询上次没删掉标记过的老大们(使用条件reserved3=1)。
2.使用maxId记录的hash条件,在本次删除条件里过滤掉,如果有就标记maxId这条记录需要删除,具体update reserved3=1;比较没删掉的老大们是否小于maxId,小于则加入软柿子列表。
3.删除软柿子列表。这个之前逻辑就有删除。