搜索

首页  >  问答  >  正文

调用存储过程比调用插入慢得多,并且批量插入基本相同,为什么?

我有一个表和一个存储过程,如下所示,

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 并没有提高我的情况下的性能。

P粉877719694P粉877719694233 天前399

全部回复(1)我来回复

  • P粉080643975

    P粉0806439752024-03-31 00:16:22

    您的示例不会归功于存储过程,因为它不会利用存储过程的任何优点。

    存储过程的主要优点是:

    • 已编译
    • 它节省了网络交换(因为计算在服务器端进行)

    假设您有一个足够复杂的逻辑,无法通过 UPDATE 进行操作,并且您希望进行操作,例如在Python中,它需要:

    • 选择行 -> 网络流量[服务器 -> 客户端]
    • 更新行 -> 相当慢:Python 被解释,如果您使用像 SQLAlchemy 这样的 ORM(必须在内存中创建对象),可能会更慢
    • 发回更新的行 -> 网络流量[客户端 -> 服务器]

    想象一下使用存储过程实现的相同示例。 在这种示例中,存储过程很有可能真正发挥作用。

    在您的示例中,您没有任何逻辑,只是插入行。 这是一个 I/O 绑定用例。拥有一个已编译的程序没有或几乎没有什么好处。 您将拥有与使用 INSERT 一样多的网络交换。 无论以何种方式,行都必须发送到服务器。 网络流量也没有增加。

    在您的示例中,也许批量插入可以帮助实现最佳性能。

    回复
    0
  • 取消回复