Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在生产MySQL数据库上更新400k行而不杀死它_Python_Mysql_Performance - Fatal编程技术网

Python 如何在生产MySQL数据库上更新400k行而不杀死它

Python 如何在生产MySQL数据库上更新400k行而不杀死它,python,mysql,performance,Python,Mysql,Performance,在我们的生产服务器上,我们需要将900k个图像分割成不同的dir,并使用InnoDB引擎更新400k行MySQL。我编写了一个python脚本,它将完成以下步骤: 从db 10行中选择一小块数据 制作新目录 将文件复制到已创建的目录并重命名 更新数据库更新时有一些触发器将加载服务器 重复 我的代码: import os, shutil import database # database.py from tornado LIMIT_START_OFFSET = 0 LIMIT_ROW_COU

在我们的生产服务器上,我们需要将900k个图像分割成不同的dir,并使用InnoDB引擎更新400k行MySQL。我编写了一个python脚本,它将完成以下步骤:

从db 10行中选择一小块数据 制作新目录 将文件复制到已创建的目录并重命名 更新数据库更新时有一些触发器将加载服务器 重复 我的代码:


import os, shutil
import database # database.py from tornado

LIMIT_START_OFFSET = 0
LIMIT_ROW_COUNT = 10
SRC_PATHS = ('/var/www/site/public/upload/images/',)
DST_PATH = '/var/www/site/public/upload/new_images/'

def main():
    offset = LIMIT_START_OFFSET
    while True:
        db = Connection(DB_HOST, DB_NAME, DB_USER, DB_PASSWD)
        db_data = db.query('''
            SELECT id AS news_id, image AS src_filename
            FROM emd_news
            ORDER BY id ASC
            LIMIT %s, %s''', offset, LIMIT_ROW_COUNT)
        offset = offset + LIMIT_ROW_COUNT
        news_images = get_news_images(db_data) # convert data to easy-to-use list
        make_dst_dirs(DST_PATH, [i['dst_dirname'] for i in news_images]) # make news dirs
        news_to_update = copy_news_images(SRC_PATHS, DST_PATH, news_images) # list of moved files
        db.executemany('''
            UPDATE emd_news
            SET image = %s
            WHERE id = %s
            LIMIT 1''', [(i['filename'], i['news_id']) for i in news_to_update])
        db.close()
        if not db_data: break

if __name__ == '__main__':
    main()

任务很简单,但我对性能有点紧张

如何使此脚本更高效

UPD: 毕竟我使用的是原始脚本,没有任何修改。大约花了5个小时。开始时速度很快,最后速度很慢。

我推荐的

将isProcessed列添加到表中。 让您的脚本在第一次运行时处理一大块(比如1k行),当然,只选择未处理的行。 对其进行基准测试。 如果需要,调整块大小。 构建另一个脚本,每隔一段时间调用此脚本。 别忘了在两个脚本中都添加一些睡眠时间

如果你的改变不需要持续,而且我认为不需要持续,这将起作用。如果必须同时执行所有操作,则应在脚本运行期间使数据库脱机。

我将添加:

为什么你要在每个循环中创建一个新的连接并关闭它呢

也许你可以专门为更新使用db.autocommitFalse,并对每100行或更多行执行一次db.commit

像Alin Purcaru一样,你也应该做一些基准测试


希望这能有所帮助:

完全取决于运行此代码的机器,但老实说,我不会太害怕。不过,我会尽量避免在数据库中存储绝对路径。此外,您可以增加批处理大小当然500不会是一个问题,批处理您的更新以减少查询总数。我们有2个服务器文件和数据库在大量使用。每天大约30万页面浏览量。任务很简单,但我对性能有点紧张。除非你运行它并测量它,否则你不会有问题。运行之后,看看您的实际性能如何。S.Lott:我应该添加睡眠时间吗?我不确定是否要添加新列,因为更改4GB表将花费大量时间。我怀疑MySQL是否有那么愚蠢。如果您不想这样做,您可以创建一个新的临时表,其中只包含id和与原始表联接的已处理的I。其思想是,您应该为处理过的行设置一个标记,这样就不会在同一行上运行两次脚本。同时,用户可能会添加更多内容,否则脚本可能会失败,您需要重新运行它。在我的情况下,脚本从下到上处理行,这就是为什么我不会在同一行上运行两次脚本的原因。如果要使用我建议的解决方案,您必须使用标记,因为它不会一次性完成所有处理。另外,如果脚本在中间的某个地方失败了,你的论点就站不住脚了。这就是为什么我要问这个问题:我们有一些触发器更新和更新很多行可能是一个瓶颈。但是创建一个到数据库的连接,你不必每次都重复它;就像S.Lott每次告诉你的那样检查我是否继续或通过continue并记住:过早优化是所有邪恶的根源Knuth;除非你需要在我们不知道之前为吉尼斯世界纪录创造一个新记录并做一个备份:@sergeik:我们有一些触发器正在更新中。请更新您的问题,以包括所有相关事实。请。@singularity:但是创建一个到数据库的连接,你不必每次都重复它-我知道这一点,但不理解持久连接的优点和缺点。好的,我可以在不重复连接上节省一些时间,但可能这个长时间运行的连接会消耗内存或其他东西。@sergeik:这是调用数据库连接池;这是非常有效的,因为如果每次创建一个新连接,那么脚本每次创建一个新实例连接时都需要更多的内存,DBMS也会保留一个新连接新线程。。对于这一点,您认为哪一个更好:。
    db_data = db.query('''
        SELECT id AS news_id, image AS src_filename
        FROM emd_news
        ORDER BY id ASC
        LIMIT %s, %s''', offset, LIMIT_ROW_COUNT)
     # Why is there any code here at all?  If there's no data, why proceed?
     if not db_data: break