我有一个 Python 脚本,可以删除 X 天之前的文件和目录。然而,该脚本运行在一个包含数百万个文件和目录的巨大目录上。按照目前的速度,完成删除过程大约需要六周时间(查看磁盘空间指标)。
看来主要瓶颈在于列出文件和目录。任何人都可以建议代码更改或优化,以帮助减少运行时间?
不确定它是否相关,但对于上下文来说,它在 k8s 中作为作业运行,因此资源不是问题。
def delete_files(root_directory, delete_time_threshold):
global EXAMINED_FILES
global DELETED_FILES
try:
for dirpath, dirnames, filenames in os.walk(root_directory):
for file in filenames:
file_path = os.path.join(dirpath, file)
try:
file_mtime = os.path.getmtime(file_path)
EXAMINED_FILES += 1
if file_mtime < delete_time_threshold:
os.remove(file_path)
logging.debug(f"File {file} deleted because mtime {file_mtime} is older than threshold {delete_time_threshold}")
DELETED_FILES += 1
except Exception as e:
logging.error(f"Error deleting file {file_path}")
except Exception as e:
logging.error(f"Error walking root directory {root_directory}: {e}")
def delete_empty_directories(root_directory, allowed_empty_dirs):
global EXAMINED_DIRS
global DELETED_DIRS
global SKIPPED_DELETE_DIRS
try:
for dirpath, dirnames, filenames in os.walk(root_directory):
if dirpath != root_directory: #don't look at the root directory
EXAMINED_DIRS += 1
try:
if not dirnames and not filenames:
relative_dirpath = re.sub(f'^{root_directory}/', '', dirpath)
if relative_dirpath and relative_dirpath in allowed_empty_dirs:
logging.debug(f"Skipping deletion of allowed empty directory: {dirpath}")
SKIPPED_DELETE_DIRS += 1
else:
os.rmdir(dirpath)
logging.debug(f"Deleted empty directory: {dirpath}")
DELETED_DIRS += 1
except Exception as e:
logging.error(f"Error deleting directory {dirpath}")
except Exception as e:
logging.error(f"Error walking root directory {root_directory}: {e}")
谢谢!
当然,我可以帮助你优化你的 Python 脚本。在处理数百万个文件时,效率至关重要,而你的分析(瓶颈在于列出文件)很可能是准确的。
让我们探讨一下可以提高脚本性能的几种方法:
-
使用
os.scandir()
而不是os.walk()
:os.walk()
对于大型目录来说可能很慢,因为它会生成所有文件名和目录名的完整列表,然后才会遍历它们。而os.scandir()
返回一个迭代器,该迭代器按需生成目录条目。这可能会显着减少内存使用量并加快处理速度,尤其是在目录较深的情况下。
```python import os
def delete_files(root_directory, delete_time_threshold): for entry in os.scandir(root_directory): if entry.is_file(): file_path = entry.path try: file_mtime = entry.stat().st_mtime if file_mtime < delete_time_threshold: os.remove(file_path) logging.debug(f"已删除文件 {entry.name},因为修改时间 {file_mtime} 早于阈值 {delete_time_threshold}") except Exception as e: logging.error(f"删除文件 {file_path} 时出错") elif entry.is_dir(): delete_files(entry.path, delete_time_threshold) # 递归删除子目录中的文件 ```
-
并行处理:
由于你提到资源不是问题,因此可以考虑使用多处理来并行删除文件。Python 的
multiprocessing
模块可以帮助你实现这一点。你可以将目录拆分为多个块,并让多个进程同时处理这些块。
```python import os import multiprocessing
def delete_files_in_directory(directory, delete_time_threshold): for entry in os.scandir(directory): # ... [与之前的代码相同的删除逻辑]
def delete_files(root_directory, delete_time_threshold): with multiprocessing.Pool() as pool: for dirpath, dirnames, filenames in os.walk(root_directory): pool.apply_async(delete_files_in_directory, args=(dirpath, delete_time_threshold)) pool.close() pool.join() ```
- 删除空目录优化: 你的当前脚本在删除文件后会遍历所有目录。为了优化这一点,你可以在删除文件时跟踪空目录,然后仅尝试删除这些目录。
```python def delete_files(root_directory, delete_time_threshold): empty_dirs = set() # ... [使用 os.scandir() 删除文件的代码]
elif entry.is_dir():
delete_files(entry.path, delete_time_threshold)
if not os.listdir(entry.path): # 检查目录是否为空
empty_dirs.add(entry.path)
# 删除文件后删除空目录
for dirpath in empty_dirs:
try:
os.rmdir(dirpath)
logging.debug(f"已删除空目录:{dirpath}")
except Exception as e:
logging.error(f"删除目录 {dirpath} 时出错")
```
-
考虑使用
find
: 对于基于时间的文件删除,find
命令通常比使用纯 Python 更快。你可以使用 Python 的subprocess
模块来调用find
。
```python import subprocess
def delete_files(root_directory, days_old): subprocess.run(["find", root_directory, "-type", "f", "-mtime", f"+{days_old}", "-delete"]) ```
选择正确的方法:
-
如果你的瓶颈确实是文件系统访问,那么
os.scandir()
、并行处理和空目录优化将会非常有益。 -
如果你主要删除旧文件,那么使用
find
可能是最快的选择,因为它经过了此类任务的优化。
对性能进行基准测试: 对任何更改进行基准测试,以了解它们对你的特定用例的影响非常重要。每个环境都是独一无二的,并且一种方法可能比另一种方法表现得更好,具体取决于你的数据和系统。
请记住,在对生产系统进行任何更改之前,始终要备份数据并在测试环境中彻底测试你的代码。祝你好运,优化愉快!
标签:python,python-3.x,performance,runtime From: 78842402