我有一个表和一个存储过程,如下所示,
CREATE TABLE `inspect_call` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `task_id` bigint(20) unsigned NOT NULL DEFAULT '0', `cc_number` varchar(63) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `created_at` bigint(20) unsigned NOT NULL DEFAULT '0', `updated_at` bigint(20) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `task_id` (`task_id`) ) ENGINE=InnoDB AUTO_INCREMENT=234031 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci CREATE PROCEDURE inspect_proc(IN task bigint,IN number varchar(63)) INSERT INTO inspect_call(task_id,cc_number) values (task, number)
我曾假设调用存储过程会比仅调用插入快得多。但令我惊讶的是,事实并非如此。当我插入 10000 行记录时,插入命令大约需要 4 分钟,而存储过程大约需要 15 分钟。
我已经多次运行测试来确认这一点。 MySQL服务器不是高端服务器,但我不明白为什么调用存储过程慢得多。
#using mysql-connector-python 8.0.31 command = ("INSERT INTO inspect_call (task_id,cc_number)" "VALUES (%s, %s)") for i in range(rows): cursor.execute(command, (task_id,f"{cc}{i}")) # cursor.callproc("inspect_proc", (task_id,f"{cc}{i}")) cnx.commit()
顺便说一句,我读到一些文章说我可以设置 innodb_flush_log_at_trx_commit = 2
来提高插入速度,但我不打算这样做。
--- 更新 ---
根据我得到的答案,我尝试批量插入(executemany
)以查看是否有任何改进,但令我惊讶的是没有。
cursor = cnx.cursor(buffered=True) for i in range(int(rows/1000)): data = [] for j in range(1000): data.append((task_id,f"{cc}{i*1000+j}")) cursor.executemany(command,data) cnx.commit() # no improvement compared to cursor = cnx.cursor() for i in range(rows): cursor.execute(command, (task_id,f"{cc}{i}"))
我尝试了很多次(还尝试了executemany
1次拍摄100条记录),发现他们的表现基本相同。
这是为什么?
--- 更新 2 ---
我终于明白为什么插入这么慢了!因为我从笔记本电脑运行脚本并从其外部主机名访问数据库。一旦我将脚本上传到服务器并从内网内部访问数据库,速度就会快得多。插入10000条记录大约需要3到4秒;插入 100,000 条记录大约需要 36 秒。我没有网络会造成这样的差异!
但是 executemany
并没有提高我的情况下的性能。
您的示例不会归功于存储过程,因为它不会利用存储过程的任何优点。
存储过程的主要优点是:
假设您有一个足够复杂的逻辑,无法通过 UPDATE 进行操作,并且您希望进行操作,例如在Python中,它需要:
想象一下使用存储过程实现的相同示例。 在这种示例中,存储过程很有可能真正发挥作用。
在您的示例中,您没有任何逻辑,只是插入行。 这是一个 I/O 绑定用例。拥有一个已编译的程序没有或几乎没有什么好处。 您将拥有与使用 INSERT 一样多的网络交换。 无论以何种方式,行都必须发送到服务器。 网络流量也没有增加。
在您的示例中,也许
批量插入
可以帮助实现最佳性能。