欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MySQL:非重复插入、重复更新语句ON DUPLICATE KEY UPDATE

程序员文章站 2022-06-01 17:06:22
...

需求背景

在《调用ZABBIX的API获取节点主机信息小记》(传送门:https://segmentfault.com/a/11...)这篇博客中,简单阐述了如何利用zabbix-api库来获取ZABBIX监控各节点主机的信息。需要将获取的信息录入数据库,由于节点数目不断增加,因此插入数据库时,要判断节点主机是否存在,存在则更新各主机信息,不存在则插入。

实现方案

首先需要建立一张表node_status:

+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| node_name    | varchar(100) | NO   | PRI | NULL    |       |
| ip           | varchar(100) | NO   |     | NULL    |       |
| site         | varchar(100) | NO   |     | NULL    |       |
| frp_num      | int(11)      | NO   |     | NULL    |       |
| home_free    | bigint(20)   | NO   |     | NULL    |       |
| mem_use      | varchar(100) | NO   |     | NULL    |       |
| last_time    | datetime     | NO   |     | NULL    |       |
| modify_time  | datetime     | NO   |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

该表中,将node_name即节点主机名称设为PRIMARY KEY。

一开始只是想到了用 if-else语句,判断node_name是否存在,存在则更新,不存在则插入。

if not exists (select node_name from node_status where node_name = target_name)
      insert into node_status(node_name,ip,...) values('target_name','ip',...)
else
      update node_status set ip = 'ip',site = 'site',... where node_name = target_name

但是这么写出来,出现了两个问题:
1、效率太差,每次都需要执行两条SQL语句,一条语句用来判断node_name是否在表中已经存在,另一条语句用来插入或更新表中数据。
2、高并发的情况下数据会出问题,不能保证原子性。

那么有没有更优雅高效的方法呢,通过查阅资料,发现MySQL一条语句很好的解决了这个问题:ON DUPLICATE KEY UPDATE
该语句的语法如下:

INSERT INTO tablename(field1,field2, field3, ...) VALUES(value1, value2, value3, ...) ON DUPLICATE KEYUPDATE field1=value1,field2=value2, field3=value3, ...;

这个语法的目的是为了解决重复性,当数据库中存在某个记录时,执行这条语句会更新它,而不存在这条记录时,会插入它。
该语句规则如下:如果你插入的记录导致一个UNIQUE索引或者primary key(主键)出现重复,那么就会认为该条记录存在,则执行update语句而不是insert语句,反之,则执行insert语句而不是更新语句
得到了这个插入语句的”八倍镜“,我们可以将原来那个很LOW的方式替换掉了:

sql = """ INSERT INTO node_status(node_name,ip,site,frp_num, \
+                        home_free,mem_use,last_time,modify_time) \
+                        VALUES('%s','%s','%s','%d','%d','%s','%s','%s') \
+                        ON DUPLICATE KEY UPDATE  \
+                        ip='%s', site='%s',frp_num='%d',home_free='%d', \
+                        mem_use='%s',last_time='%s',modify_time='%s' """ % \
+                (id_value['host'], ip, site, frp_num, home_size, mem_use, last_time, modify_time),
+                 (ip, site, frp_num, home_size, mem_use, last_time, modify_time)