如何在 MySQL 中限制特定时间段内数据的唯一性插入?

我记得有一次,我在一个电商平台上工作,那时候系统后台的订单处理系统经常遇到数据重复的问题。
有一次,一个订单号被重复提交了三次,这让我意识到我们需要对订单插入进行时间段的唯一性控制。

那时候,我们并没有直接在数据库层面实现这个功能,而是选择了在应用层通过Redis分布式锁来控制。
我写了一个简单的脚本,首先尝试获取Redis的锁,如果获取失败,就直接拒绝订单的插入。
如果成功获取锁,接下来就是检查订单时间是否在允许的范围内。

我记得当时是这样的,我们设置了锁的过期时间为一个小时,然后查询数据库中该用户最近一次的订单时间,如果这个时间加上一个小时小于当前时间,我们就允许插入新订单,并更新数据库中该用户的最大订单时间。
这个过程就像一场接力赛,每个订单都是接力棒,我们通过锁来确保同一时间只有一个订单在传递。

但这个过程也不是没有问题。
有一次,我在监控日志时发现,某个订单处理的时间比预期长了很多。
我检查了一下,发现是因为某个订单在处理过程中锁被意外释放了,导致后面的订单都在等待这个锁。
这个问题让我意识到,锁的粒度和时间设置非常重要,一旦设置不当,就会影响系统的性能。

还有个事,我突然想到,如果时间段是固定的,比如每天上午1 0点到1 1 点,我们其实可以不用这么复杂的逻辑,直接在数据库层面通过唯一索引来实现。
但我们的业务场景是动态的,所以这种方法并不适用。

那么,你们在处理类似问题时,都是怎么做的呢?有没有什么更好的方法或者经验可以分享?

MySQL中max_execution_time引发的血案

哎哟,这事儿我去年在杭州遇到过,真挺闹心的。

当时我们那个存储节点,CPU直接飙到1 00%,卡了好几个小时。
一查show processlist,好家伙,全是同一个SQL,跑了好久好久。
这SQL是个复杂查询,查的数据量也大。

当时就琢磨,为啥HTTP请求断开了,SQL还在跑呢?后来查资料才知道,MySQL里头,查询没超时或者没被手动kill,它就真会跑到底,不管你前端断没断开。
你想想,用户等不及,又点了一遍,那SQL不就又跑一遍,越积越多,CPU能不炸吗?
所以啊,max_execution_time这玩意儿,虽然是个救星,但得用对地方。
默认是0,没限制,那就麻烦了。
我们后来就把数据库层的给设了,会话级别的, SELECT查询超过3 秒就停,避免它跑飞了。
应用层也加了个Druid,全局设置了一下超时。
关键的地方,还加了JDBC的setQueryTimeout,特别长的查询单独控制。

哦对了,还跟前端提了个要求,点了查询得有个“处理中”的提示,别用户傻乎乎地重复点,那真是雪上加霜。

你看,这事儿吧,光靠数据库一个地方不行,得数据库、应用、前端都搭把手,才能彻底解决。
监控也得跟上,CPU飙起来赶紧告警,早发现早处理。
哎,这CPU1 00%的滋味,谁尝谁知道,真难受。

数据量过大查询超时,如何优化 SQL 查询?

哎哟,这优化策略啊,得说两句。
首先,咱们得知道,函数用多了,就像电脑里装了太多插件,有时候会卡。
所以,能不用函数就别用,尤其是在索引列上操作,这样能保证索引不失效。
记得啊,索引建立的时候,最好遵循前缀原则,就是只建立前几个字符的索引,别整太多,太浪费。

然后,复合索引这东西,得好好利用。
比如,你要是得根据几个列来连接或者排序,那就得建个复合索引。
这样,MySQL就能知道怎么走索引了,效率自然就上去了。
不过,要注意,MySQL可能只会用其中一个索引,所以你得好好设计你的复合索引。

再来,EXPLAIN命令这东西,你得经常用。
它能告诉你查询的执行计划,看看哪里慢,然后你就能针对性地优化。
比如,我之前就遇到过,用CONCAT函数连接字符串,结果索引失效了。
后来,我就把CONCAT换成了DATE_FORMAT,这样索引就保持有效了。

具体到应用,我举个例子。
我之前在一个项目里,得根据邮件记录的创建时间来查询。
我就建了个复合索引,样子是这样的:CREATE INDEX idx_mail_record ON mail_record (create_time, to)。
这样,查询效率就提高了。

最后,就是用EXPLAIN分析查询计划。
我那次用EXPLAIN一看,发现瓶颈在某个地方,然后我就针对性地优化了那个部分。
说实话,当时我也没想明白,后来慢慢摸索,才搞明白了这个优化策略。