> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# 误用gorm引起的一宗血案
- URL: https://blog.hacking.pub/2018/09/05/wu-yong-gormyin-qi-de-yi-zong-xie-an/
- Published: 2018-09-05T17:18:10.000Z
- Updated: 2018-09-05T17:20:14.000Z
- Author: chainhelen
- Tags: mysql, 血案, gorm

#### 现象

某服务A从晚上6点开始，监控一直出现`mysql`调用延迟超高，http接口500飙升

#### 原因

经查发现是一条全局update整张表的sql引起的，全局lock表

#### 最小化

使用下面操作进行最小化

```
建库 create databses test;
建表 CREATE TABLE `Record` ( `id`  BIGINT NOT NULL, `count` BIGINT NOT NULL, PRIMARY KEY(`id`) ) ENGINE = InnoDB；
插入一条数据 CREATE TABLE `Record` ( `id`  BIGINT NOT NULL, `count` BIGINT NOT NULL, PRIMARY KEY(`id`) ) ENGINE = InnoDB；

代码如下：
package main

import (
        "bufio"
        "github.com/jinzhu/gorm"
        _ "github.com/jinzhu/gorm/dialects/mysql"
        "log"
        "os"
)

type Record struct {
        Id    int64 `gorm:"column:id;type:bigint;primary_key",db:"id"`
        Count int64 `gorm:"column:count;type:bigint",db:"count"`
}

func (Record) TableName() string {
        return "Record"
}

func main() {
        var bio *bufio.Reader
        var db *gorm.DB
        var err error
        log.Printf("############# main")
        bio = bufio.NewReader(os.Stdin)
        bio.ReadString('\n')

        // 连接数据库
        db, err = gorm.Open("mysql", "root:123456@/test?charset=utf8&parseTime=True&loc=Local")
        if err != nil {
                panic("db link failed")
        }
        defer db.Close()

        log.Printf("db link")
        bio = bufio.NewReader(os.Stdin)
        bio.ReadString('\n')

        // 查找数据
        var record Record
        queryResult := db.Where("id = ?", 20).Find(&record)
        if queryResult.RecordNotFound() {
                log.Printf("Not found")
                return
        }

        log.Printf("############ pass NOTFOUND")
        bio = bufio.NewReader(os.Stdin)
        bio.ReadString('\n')

        //if queryResult.Error != nil {
        //      log.Fatalf("Error %s ", queryResult.Error)
        //      return
        //}
        log.Printf("search ok ")

        // 更新数据
        if err = db.Model(&record).UpdateColumn("count", record.Count+1).Error; err != nil {
                panic(err)
        }
        log.Printf("update ok ")
}

```

复现步骤，就是  
1.运行程序，连接db  
2.关闭数据库  
3.回车继续程序，程序执行到查询`id = 20`，这时候db连接是有错误的，但是找到数据、非`RecordNotFound`的错误都是返回`true`  
4.开启数据库  
5.回车继续程序，程序执行到更新数据库的数据，传入`record`主键`id`是一个空，导致会更新全表，并且覆盖所有`count = 1`

![update.png](https://blog.hacking.pub/qiniu/%E8%AF%AF%E7%94%A8gorm%E5%BC%95%E8%B5%B7%E7%9A%84%E4%B8%80%E5%AE%97%E8%A1%80%E6%A1%88/update.png)

> 幸好表很大，导致超时，该类操作的数据都被回滚了