如何在生产MySQL数据库中更新40万行而不导致崩溃
在我们的生产服务器上,我们需要把90万张图片分到不同的文件夹里,并更新40万条记录(使用的是MySQL的InnoDB引擎)。我写了一个Python脚本,按照以下步骤进行操作:
- 从数据库中选择一小部分数据(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()
这个任务其实挺简单的,但我对性能有点担心。
我该如何让这个脚本运行得更高效呢?
更新:最后我还是用了原来的脚本,没有做任何修改。整个过程大约花了5个小时。一开始速度很快,但到最后就变得很慢了。
3 个回答
1
当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。
2
我给你的建议:
- 在你的表格里加一个
isProcessed
列。 - 让你的脚本第一次运行时处理一小部分,比如说1000行(当然只选择那些还没处理的行)。
- 测试一下这个过程的速度。
- 如果需要,可以调整一下每次处理的行数。
- 再写一个脚本,定时调用这个脚本。
别忘了在你的两个脚本里加一些休眠时间哦!
这样做是可行的,前提是你的更改不需要一直进行(我觉得其实不需要)。如果你必须一次性处理完,建议在脚本运行的时候把数据库下线。
3
我想补充一下:
你为什么在每次循环中都创建一个新的连接,然后又把它关闭呢,哈哈!!!
另外,你可以试试用 db.autocommit(False),特别是在进行更新操作时,然后每处理100行数据就执行一次 db.commit();
还有像 Alin Purcaru 那样,你也应该做一些性能测试。
希望这些能帮到你 :)