django querysetのキャッシュ最適化を記憶する
原点
私たちの単一のテーブルのレコードを見てほぼ7000万行、画期的な億行は、角を曲がったところにされているので、データベーステーブルの意図は、アプリケーション層は比較的透明なアプローチは、データベースミドルウェアを導入することです、アリ、360などのいくつかのプログラムがありますが、それらのほとんどは、JavaやC、および最後に見ています。 https://github.com/flike/kingshard これは、golangによって書かれている、コードの品質は、私の好みに合わせて、まだ非常に高いので、使い切るためにプロキシとして最初のキングハードは、前に発見されていなかった問題を発見した。
django はデータベース接続プールさえ持っていません。
kingshardをプロキシとして使ってみたところ、kingshardに接続しているソケットのステータスがtime_waitで、kingshard to mysqlだけが常にestablishになっていたので、気になってdjangoがデータベース接続プーリングを使っていないのではないかと疑っています(もちろん、djangoは1.4という古いバージョンを使っているのですが)。CONN_MAX_AGE というパラメータは、バージョン 1.6 以降でのみ利用できるようです。詳しくは、以下を参照してください。 http://www.huochai.mobi/p/d/752667/?share_tid=85a118bfb04b&fmid=0
django get(filter) を queryset の中で再度実行すると、SQL クエリが再度起動します。
for register_day_obj in self.data['UserRegisterDayObj']:
xxxxxx
pay_day_obj = self.data['UserPayDayObj'].get(gameid=register_day_obj.gameid)
xxxxxx
self.data はすべて django queryset 内にあり、sql クエリの数だけ for ループが送られることになり、これは許容できません。
なぜ、私たちのプロジェクトでは、このようなことが発見されなかったのでしょうか?主に、私たちのシステムはバイシステムで、すべてのクエリが一度に多くのデータを取得し、すべてインデックス付きのクエリであるため、1つの大きなSQL文と100の分割SQL文はクエリにほぼ同じ時間がかかり、クエリが非常に高速であるため、遅いデータベースクエリも誘発されていないためです。これはまた、orm が発行された sql 文をチェックする必要があることを示しています、django はデバッグの設定も提供しています。
修正した場合はどうでしょうか?つまり、最初の for は sql リクエストを発行し、それ以降の for ループは要求されたキャッシュを使用します。
これは簡単で、カスタム cache_filter,cache_get メソッドを使用します。
def cache_filter(queryset, **option):
"""queryset cachefilter"""
return [query for query in queryset if all(getattr(query, k) == v for k, v in option.iteritems())]
def cache_get(queryset, **option):
"""queryset cache get"""
result = cache_filter(queryset, **option)
if len(result) ! = 1:
raise ValueError("get should return one record, but now:%s" % len(result))
return result[0]
特に、辞書を使って最適化できる外側のforループがあるため、forループで実行すると、時間計算がO(n)になります。
def fast_cache_filter(queryset, *keys):
"""Use dictionary to speed up cache filter"""
cache = defaultdict(list)
for query in queryset:
v = [getattr(query, key) for key in keys]
cache[tuples(v)].append(query)
def query_filter(**option):
assert len(keys)==len(option), "filter keys and option must same"
return cache.get([option[key] for key in keys], [])
return query_filter
def fast_cache_get(queryset, *keys):
"""Use dictionary to speed up cache get"""
query_filter = fast_cache_filter(queryset, *keys)
def query_get(**option):
result = query_filter(**option)
if len(result) ! = 1:
raise ValueError("get should return one record, but now:%s" % len(result))
return result[0]
return query_get
もちろん、2番目のオプションを使用すると、最初のものより侵襲的です。まず、fast_cache_get関数にクエリセットと取得キーを伝える必要があり、fast_cache_getは、典型的なクロージャであるcache_getの使用と同様の関数を返すことになります。
比較した結果、高速版の方が10%~20%ほど若干速いのですが、侵襲性が高いので、結局、高速版をやめてfor loop版を使っています。なぜ10%~20%しか速くならないのかについては、一言で説明できるものではなく、主観で判断せず、データで語ることが重要であることがわかります。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
ハートビート・エフェクトのためのHTML+CSS
-
HTML ホテル フォームによるフィルタリング
-
HTML+cssのボックスモデル例(円、半円など)「border-radius」使いやすい
-
HTMLテーブルのテーブル分割とマージ(colspan, rowspan)
-
ランダム・ネームドロッパーを実装するためのhtmlサンプルコード
-
Html階層型ボックスシャドウ効果サンプルコード
-
QQの一時的なダイアログボックスをポップアップし、友人を追加せずにオンラインで話す効果を達成する方法
-
sublime / vscodeショートカットHTMLコード生成の実装
-
HTMLページを縮小した後にスクロールバーを表示するサンプルコード
-
html のリストボックス、テキストフィールド、ファイルフィールドのコード例