zoukankan      html  css  js  c++  java
  • laravel 解决mysql插入相同数据的问题

    1.背景:

      每天0点定时任务统计数据,实现目标是统计时如果没有今天的统计数据,那就执行insert操作 如果存在那就执行update操作;

    代码逻辑

    1 if(报表存在){
    2             update();
    3         }else{
    4             insert();
    5         }

    上线跑了多天后 发现有时候会出现同一天的统计数据有2条

    经过分析后

      发现当0点定时任务跑的时候,存在用户登录(登录需要统计,需要更新报表),因为0点的时候,数据库都没记录,所以两者都判断了insert操作,造成数据库有2条相同数据(只有登录数量的字段不一样,其他字段都一样)

    2.解决方案:

      说实话,第一反应是给数据表加锁。laravel提供了2个锁:sharedLock和lockForUpdate.经过仔细研究分析后发现,这2个锁并不能解决我目前的需求,原因如下:

    • sharedLock 对应的是 LOCK IN SHARE MODE
    • lockForUpdate 对应的是 FOR UPDATE

      这2个锁都是避免同一行数据被其他transaction进行update()的。

    • sharedLock 不会阻止其他 transaction 读取同一行
    • lockForUpdate 会阻止其他 transaction 读取同一行 (需要特别注意的是,普通的非锁定读取读取依然可以读取到该行,只有 sharedLock 和 lockForUpdate 的读取会被阻止。)

      但是我现在的实际情况都没有读到数据,所以都不能满足。

      后来实在没办法想到了表锁,对,就是把全表都锁定了。但是这个效率性能实在太差了,而且在LOCK TABLE 和UNLOCK TABLE之间有异常,会导致锁无法释放。潜在问题很严重,所以还是不打算用这个方法。

    INSERT IGNORE

      经查阅大量资料(疯狂百度)后发现了insert ignore。insert ignore 如果存在数据,那么则忽略新数据

    REPLACE INTO

      replace into 表示插入替换数据,表中如果有PrimaryKey或者unique索引的话,数据库如果已存在数据,则用新数据替换,如果没有数据则和insert into一样。

     上面2个方法固然好,但是存在数据丢失的问题,还是放弃了。

    INSERT INTO

        最终的解决办法居然回到了最原始的sql语句。insert into表示插入数据,数据库会检查主键(PrimaryKey),如果出现重复会报错;

      对就是利用这个主键报错的机制,解决了的需求。就是在每次插入数据的时候把下一个自增id也插入进去。因为我们平时插入数据都是不用管自增id的,所以laravel都没有获取下一个自增id的封装方法。没办法,自己动手丰衣足食。

     //获取下一个自增id,方法还是很简单的

    1 public static function getIncrementId()
    2     {
    3         $id = self::query()
    4             ->orderByDesc('id')
    5             ->value('id');
    6         return ++$id;
    7     }
     1           try {
     2                     $report = DailyReport::getReport($customer->id, 1);
     3                     if (!$report) {
     4                         $id = DailyReport::getIncrementId();
     5                         DailyReport::create([
     6                             'customer_id' => $customer->id,
     7                             'report_date' => Carbon::today(),
     8                             'id' => $id,//把id也插入
     9                             ...
    10                         ]);
    11                     }
    12                 } catch (Exception $exception) {
    13                     if (23000 == $exception->getCode()){//这里捕获主键重复的错误,然后做相关逻辑的操作 23000后来发现好像也别的sql错误也会报,记得优化
    14                         //do something
    15                     }else{
    16                         LogManagers::error($exception);
    17                     }
    18                 }                

    上面就是解决的代码。最终的解决办法还是利用了主键重复的错误来解决问题。

    本文属于个人原创,欢迎转载,转载请附链接:https://www.cnblogs.com/x-x-j/p/13491564.html

  • 相关阅读:
    wp7订餐客户端源码
    AOP技术术语
    urlpattern详解
    .net 访问IBM DB2 数据库
    入驻博客园
    hadoop视频
    阿里OneData构建数据指标体系
    大数据领域全景解析
    PyTorch中梯度为什么默认自动累加,在反向传播前要手动将梯度清零?
    Pytorch:Dataloader和Dataset以及搭建数据部分的步骤
  • 原文地址:https://www.cnblogs.com/x-x-j/p/13491564.html
Copyright © 2011-2022 走看看