背景
4CPU メモリ4Gの小規模サーバで数日掛かる処理を複数立ち上げて
CPU100% x 4を維持して計算していたら、数時間でメモリ不足が発生して
CPU使用率が激減した。どうやらメモリ不足に陥っていたようだ。
起きていた事
処理時間の増加ニトモナイ、プロセスのメモリ使用量が増加
→メモリ枯渇
→メモリswap発生
→メモリアクセス速度が大幅劣化
→メモリ待ちCPU使用率が1%以下
→いつまでたっても終わらない(性能大幅劣化)
対策0. お金で解決
たいていの場合、サーバ台数増やしたり、サーバの性能アップさせたら解決します。
個人のPJだったので選択しませんでしたが、メモリ32Gくらいのサーバに乗り換えたら
それだけで解決したんだろうなぁ。
対策1. 処理の垂直分割
pythonのメモリ管理は基本VM任せの全自動。メモリリークの根本解決はプロセス殺すしかない
処理にベキ等性もたせて、1つのコマンドで8種類のカテゴリの処理を行っていた箇所を分割した
改善前
class Category(Enum):
A = 1
B = 2
C = 3
for category in Category:
benchmark(category)
改善後
category = manage.get_category_by_priority()
benchmark(category)
対策2. supervisorを導入した
改善1のコードは8回にわけて実行する必要があったので、プロセスが停止したら
再度立ち上げる処理を各必要があった。そんなときsupervisor使うと便利
easy_install supervisor
echo_supervisord_conf > /etc/supervisord.conf
supervisord
supervisord status
alias sc='supervisorctl'
sc restart
sc reread
sc stop all
sc status
sc restart all
対策3. 手動GCに手を染めた
pythonのGCは詳しくないので副作用があるかも。
いまのところメモリリーク解消して安定していますが
黒魔術の可能性あるなので、あまりオススメできません。
クラスキャッシュを多用していると、python2系だとメモリリークが頻発して
消費メモリ量がどんどん増えていく問題は、python3にすると解消するのだろうか
class Category(Enum):
A = 1
B = 2
C = 3
for category in Category:
benchmark(category)
def benchmark(category):
bulk = []
tmp_data = Tmp.get_all()
for _tmp in tmp_data:
bulk.append(calc(_tmp))
DBTable.bulk_create(bulk) # バルク!
# メモリ解放
import gc
del tmp_data
del bulk
gc.collect()
参考: gc — ガベージコレクタインターフェース
http://docs.python.jp/2/library/gc.html