《High Performance Django》閱讀筆記

第一章:The Big Picture

作者開篇就提到大家總說 Django 性能不行,但是實際上 有很多高性能的站點是使用 Django 開發的。

Django’s scaling success stories are almost too numerous to list at this point. It backs Disqus, Instagram, and Pinterest. Want some more proof? Instagram was able to sustain over 30 million users on Django with only 3 engineers (2 of which had no back-end development experience). Disqus serves over 8 billion page views per month. Those are some huge numbers. These teams have proven Django most certainly does scale. Our experience here at Lincoln Loop backs it up. We’ve built big Django sites capable of spending the day on the Reddit homepage without breaking a sweat.

在作者的公司,他們開發高性能 Django 站點的準則就是 simplicity :

  • Using as few moving parts as possible to make it all work. “Moving parts” may be servers, services or third-party software.

  • Choosing proven and dependable moving parts instead of the new hotness.

  • Using a proven and dependable architecture instead of blazing your own trail.

  • Deflecting traffic away from complex parts and toward fast, scalable, and simple parts .

Simple systems are easier to scale, easier to understand, and easier to develop.

構建高性能 Web 應用通常需要關注一下幾點:

  • 數據庫。關係型數據庫通常是整個技術棧中最慢最複雜的部分,一個辦法是改用 NoSQL 數據庫,不過 大多數情況下都可以通過緩存解決。

  • 模板。我們可以用一個更快的模板引擎替換 Django 自帶的模板引擎,不過即便是這樣模板仍舊是 整個技術棧中第二慢的部分。我們仍然可以通過緩存解決這個問題。

  • Python。Python 在通常情況下已經足夠快了。我們可以使用 Web 加速器(比如:Varnish)緩存服務器響應, 在請求進入到 Python 那一層之前就返回相應。

這章作者一直在強調緩存,”CACHE ALL THE THINGS”。無論你怎麼優化你的技術棧,沒有比緩存更快的優化方案。 說到緩存可能大家可能會顧慮緩存過期的問題,作者說了現在先別關心這個問題,之後會給出解決方案。

作者提到一般的網站都保護這幾層:負載均衡器,Web 加速器,APP 服務器,緩存,數據庫。

順便提了一個 HTTPS 的負載均衡配置方法:客戶端與負載均衡器之間使用 HTTPS,負載均衡器與後端服務之間使用 HTTP。這樣既保證了安全又可以減少 HTTPS 對性能的影響。

第二章 The Build

小技巧

  • 本地開發環境應該儘可能的與線上環境一致:相同的數據庫,相同的操作系統以及相同的軟件版本。。。

  • 組織 settings 文件,創建一個基礎的配置文件 settings/base.py ,然後再爲開發,測試,部署分別創建一個配置文件,一些重要的配置信息可以通過環境變量獲取

    • settings/base.py

    • settings/dev.py

    • settings/deploy.py

這裏作者有提到一個小技巧,那就是環境變量的值都是字符串,那麼如果將值轉換爲布爾值,元組甚至字典呢?答案就是可以使用 ast 模塊:

1
2
3
4
5
6
7
8
>>> ast.literal_eval('True')
True

>>> ast.literal_eval('1, 2, 3')
(1, 2, 3)

>>> ast.literal_eval('{"foo": "bar"}')
{'foo': 'bar'}

小心第三方 APP

在決定使用某個第三方 APP 之前,先回答下面幾個問題:

  • 它真的符合你的需求嗎?還是只是有點相近?

  • 它是個健康的項目嗎?

    • 維護者有一個好的追蹤記錄嗎?

    • 文檔寫的好嗎?

    • 測試覆蓋率夠嗎?

    • 社區怎樣(貢獻者,pull requests 等等)?

    • 還在處於活躍開發嗎?

    • 有很多舊的 issues 和 pull requests 嗎?

  • 性能咋樣?

    • 它會產生多少數據庫查詢?

    • 易於緩存嗎?

  • 跟你項目的其他部分有衝突嗎?

  • 它的授權協議跟你的項目兼容嗎?

不再維護以及不穩定的第三方應用很快就會成爲你的項目的負債。 可以嘗試閱讀源代碼,然後從中找出你的項目需要的代碼然後應用到項目中。

找出性能問題

可以使用下面這些工具

  • Django Debug Toolbar

  • django-debug-panel

  • django-devserver

觀察頁面性能:

執行了多少條 SQL 語句?
有多少時間花費在數據庫上?
執行了什麼特殊的查詢操作,每次查詢花費多長時間?
這些查詢是有什麼代碼生成的?
渲染頁面都用到了哪些模板?
冷/熱緩存是如果影響性能的?(提示:可以使用 settings 來切換緩存)

哪裏需要優化

數據庫優化

  • 減少查詢次數

    • 使用 select_relatedprefetch_related, (提示:prefetch_related 要放在查詢的最後,不然會沒有效果。)

  • 減少查詢時間

    • 不要忘記加索引(索引也是有代價的,每次對數據庫進行寫操作都需要更新索引)

    • 某些情況下 join 查詢性能很差,在這種情況下兩條查詢語句比一條 join 耗時更少。

  • 限制結果數

    • 留意 .all() 只取需要的結果數, queryset[:20]

  • count 查詢很慢。可以的話,不要使用 count。比如使用 .exists() 代替 count 進行判斷記錄是否存在。

  • generic 外鍵。generic 外鍵是個很 cool 的功能,但是它會生成一些特別複雜的查詢,所以可能的話,不要使用它。如果你一定要用的話,記得這是個需要緩存的地方。

  • 優化 model 方法。如果某個 model 方法在一個請求內會多次被調用,可以使用 cache_property 緩存方法解決(緩存只在該請求內有效)

1
2
3
4
5
6
7
8
from django.utils.functional import cached_property

class TheModel(models.Model):

   @cached_property
   def expensive(self):
      # ...
      return result
  • 結果太大了,包含了不需要的字段。使用 deferonlyvaluesvalues_list 限制結果大小:

1
2
3
4
5
posts = Post.objects.all().defer('body')
posts = Post.objects.all().only('title')
posts = Post.objects.all().values('id')
posts = Post.objects.all().values_list('id')
posts = Post.objects.all().values_list('id', flat=True)
  • 緩存查詢結果。這裏提到兩個庫: Johnny Cache 和 Cache Machine 這兩個庫的原理都是在 ORM 和數據庫中間加了一個緩存層,將 ORM 生成的 SQL 作爲 key 來緩存查詢結果。

  • 只讀 replicas。對那些讀遠遠大於寫的站點,可以考慮從 只讀 replicas 中讀取數據,實現讀寫分離。減少主庫的負擔優化性能。

  • raw 查詢。如果感覺 ORM 有點慢話,可以考慮使用 raw 方法執行原生的 SQL 語句。

  • 反範式。這種方法有個問題就是每次更新的時候都需要同時更新其他表中相關的冗餘字段。

  • 使用其他數據存儲方式。比如: Postgres, redis, mongodb,使用 Elasticsearch 進行全文檢索等。 需要注意的是,在生產環境下新增一個服務並無法沒有代價的。作爲開發者我們可以不太在意這個,但是系統需要 支持,配置,監控,備份等。新增服務的時候要考慮到這些代價以及你的系統管理員的意見。

  • sharding。99.9% 的網站都不需要用到數據庫的 sharding 功能,所以只有在你確信你遇到了那 0.1% 的時候 再使用 sharding 功能。

模板優化

應該緩存模板中一切可以緩存的東西。

  • 俄羅斯套娃式緩存。在一個模板裏緩存嵌套緩存,就像俄羅斯套娃一樣,一層套一層。比如:

1
2
3
4
5
6
7
8
9
10
{% cache MIDDLE_TTL "post_list" request.GET.page %}
   {% include "inc/post/header.html" %}
   <div class="post-list">
   {% for post in post_list %}
       {% cache LONG_TTL "post_teaser_" post.id post.last_modified %}
           {% include "inc/post/teaser.html" %}
       {% endcache %}
   {% endfor %}
   </div>
{% endcache %}
  • 自定義一個支持通過 url 參數刷新模板緩存的 cache 標籤,這樣就可以隨時刷新緩存了

隨後處理耗時的任務

可以把耗時的,不需要同步知道結果的任務放到類似 celery 的任務隊列中異步執行, 從而減少請求——響應的處理時間。下面這些任務可以考慮放到 celery 中:

  • 第三方 API 的調用

  • 發郵件

  • 非常複雜的計算(視頻處理,大量的數字處理等)

對於使用 celery 作者提到了一下小提示:

  • 不要將 model 實例作爲任務的參數,可以改用傳主鍵的方式。因爲在這期間 model 的數據可能已經發生改變了, 還有就是那個 model 實例可能不支持序列化。

  • 保持任務小,不要再一個任務中執行太多的工作。把一個任務分割成多個任務,一方面可以使用多核或多 worker 的 方式加速任務執行,另一方面,單個任務可以很快的執行完方便安全快速的重啓 worker,因爲一個 worker 重啓時 會等待正在執行的問題完成,保持任務小巧的話,可以加快部署時間。

  • 可以考慮使用 celery 的 beat 功能去自習定時任務。

前段優化

  • 壓縮 css 和 javascript(min, gzip, 版本化靜態文件)。(個人建議:版本化文件應該類似這樣 foo-xxyy.js 而不是這樣 foo.js?v=xxyy ,主要是方便使用 CDN,防止出現緩存未過期的情況。)

  • 壓縮圖片。

  • 使用 CDN 服務靜態文件。

文件上傳

可以考慮使用分佈式文件系統或者雲存儲來存儲上傳的文件。使用雲存儲的時候要考慮備用方案,萬一服務不可用怎麼辦。

測試

好的測試用例是健康代碼的強有力的基石。測試應該覆蓋到你代碼中最複雜,最重要,最容易出問題的地方。

自動化測試以及持續集成

一個持續集成系統可以讓開發者在開發進度的早期就發現問題,通過持續集成系統來執行 自動化測試以及檢查你的代碼的健康度。作者提到了他們的檢查點:

  • 單元測試

  • 代碼覆蓋率

  • PEP8/linting

  • 使用 Selenium 進行功能測試

  • 所以 Jmeter 進行性能測試

第三章:The Deployment

先決條件

操作系統

作者推薦使用 Ubuntu。同時作者給出了選擇其他操作系統時,需要考慮的事情:

  • 能夠很容易的就使用 Python 2.7+ 。有些操作系統要安裝 2.7+ 版本的 Python 非常的麻煩,如果是這樣的話你就要慎重考慮了。

  • 有長久的安全更新支持。

配置管理

  • Chef, Puppet, Ansible, Salt 都是比較好的配置管理工具

  • Fabric 不是配置管理工具,如果你把它當作配置管理工具的話,會有你頭疼的時候。你可以在 Fabric 的基礎上 構建你自己的配置管理工具。

進程管理

  • 系統默認的工具:upstart, systemd

  • 第三方軟件:deamontools, supervisord, circus

更新代碼

更新代碼一般需要進行下面幾步:

  1. 從版本控制服務器上拉取最新的代碼

  2. 更新依賴

  3. 合併數據庫更改(migrate)

  4. 收集,壓縮,推送靜態文件到 CDN

  5. reload WSGI 服務器

  6. 重啓後臺 workers

推薦寫個腳本自動執行這些操作,這樣不容易出錯。如果要更新多臺服務器的話,可以使用遠程執行 框架來做這種事情,比如: Salt, Fabric。

有一點特別要注意的是,一旦服務上線就應該儘可能的使用平滑 reload 的方式來代替重啓進程的暴力方式。

多個遠程環境

至少要有兩個環境吧:打包/開發環境,生產環境。 同時多個環境儘量保證一致性,尤其是多個生產環境之間(設置,軟件,系統,等等)。 不過有些東西還行要區分的,比如:

第三方服務配置。比如,你肯定不希望在開發環境下觸發支付操作或發送文件到生產環境下的 CDN 上。
獲取數據的問題。經常看到某些人在開發環境下使用線上數據庫的副本,但是這裏有幾個問題要考慮:
你的開發環境更生產環境一樣安全嗎?監控鬆散的開發機器是黑帽***經常會***的目標。
有可能會從你的應用中發送郵件或通知嗎?從你的開發環境下向你的用戶發送數千封郵件不僅是非常 尷尬的事情,同時也會影響你們的商業。

避免單點故障

要經常備份,確保你知道所有存儲的數據(數據庫,用戶上傳的數據,等等)都有備份。這樣出現故障的時候 其他其他機器的時候丟失的數據會少一點。

高可用是一個可以考慮的方法,如果對你的商業來說他是非常重要的話。 HA 可以保證在出現服務掛掉 的情況下能夠無縫自動切換到備用服務或者說不用手動切換。不過需要注意的是通常構建高可用 的花費比服務當掉的花費還高。

在考慮架構的時候要考慮到如何解決單點故障的問題。舉個例子,當使用第三方的 Amazon EC2 的時候, 你是否能夠接受某些區域的設備宕機,整個地區呢?如果服務商 Amazone 當了怎麼辦? 越早考慮這些問題就能在災難實際發生的時候更好的應對。

服務器佈局

  • 負載均衡器: 可以使用雲服務商提供的負載均衡服務或者使用 nginx, Haproxy。對於負載均衡器網絡 帶寬是個非常重要的注意點。

  • Web 加速器:網絡帶寬和內存是值得關注的點

  • 應用:CPU 和內存值得關注。

  • 後臺運行的 workers:後臺運行的任務通常都是 CPU 密集型的任務並且運行在獨立的服務器上。一個服務器上可以運行 多個 workers。

  • 緩存:你的緩存服務器需要更多的內存。另一個需要注意就是網絡帶寬,它可能會在內存之前成爲瓶頸。如果網絡開始 擁堵的時候, Django 可以配置多個緩存服務器。

  • 數據庫:足夠的內存非常重要,最用是足夠把你的數據都裝載在內存裏。 如果你預期會有 64GB 的數據,那麼至少要有 64GB 的內存。 磁盤速度也非常重要。購買你能夠負擔得起的最快的磁盤。 如果你用的是虛擬機的話,你需要留意你其實是在跟你的鄰居共享一個物理磁盤,常規的實踐是儘可能的買最大的虛擬機。

優化技術棧

優化數據庫

優化 uWSGI

優化 Django

CACHES

如果你使用 Memcached,使用 pylibmc 這個庫會有更高的性能。 redis 的話可以使用 django-redis 。

緩存過期是第一個需要面臨的問題,一個緩存 key 過期或者被刷新都有可能摧毀你的數據庫。 幸運的是有個簡單包可以解決這個問題: django-newcache

還有一個問題就是如果緩存服務器宕機了會到期用戶收到 “500 Server Error”的響應。 將使用一個緩存服務器改爲三個可以降低出錯的機率。 你需要考慮當緩存服務器宕機的時候,你是需要你的網站也跟着宕機,還是希望你的應用繼續 運行良好只是把它當作緩存未命中來處理?作者創建了一個 django-ft-cache 包用來 解決這個問題,它會將任何的 memcached 操作用 try/except 包裹,捕獲這裏的異常, 這樣緩存服務器當掉時請求依然可以被正確處理。

SESSION_ENGINE

把 session 保存在數據庫中非常影響性能,一個好的辦法是保存在緩存中。如果用 redis 的話 可以使用 cache backend, 用 memcached 的話 cached_db backend 也還行。 另一個辦法就是使用 signed_cookies backend, 讓客戶端存儲 session 數據。

DATABASES

可以通過 DATABASES 設置增加 CNN_MAX_AG key 選項來定義重用數據庫連接。 比如 300, 這將告訴 Django 保持打開和重用數據庫連接 5 分鐘。

LOGGING

不要定義 LOGGING 將日誌保存到文件中,而是應該輸出到 STDERR ,讓進程管理器來處理日誌。

MIDDLEWARE_CLASSES

不要輕易自定義中間件,因爲中間件會在每個請求中執行。所以確保你知道每個中間件都做了什麼, 以及儘可能的不要在中間件中執行數據庫查詢操作。

常規安全問題

需要注意 clickjacking 和 XSS(Cross Site Scripting)。最簡單的方法是使用 django-secure 項目來檢查安全問題。 另一個安全問題是,確保你的 admin 後臺處於保護中,如果你把它開放出去了,確保使用了非常 複雜的管理員密碼。最好是把它變成一個內網服務,讓它不能從外部互聯網訪問。

配置你的服務器

安全

  • 調整 SSH 配置:禁用 root 登錄,禁用密碼登錄,更改默認端口。

  • 應用安全補丁:關注一下系統安全問題,及時更新 zer-day 補丁(比如,Hearbleed)

  • 使用私有網絡:大多數雲服務器都提供私有網絡服務,只允許訪問你帳號下的服務。在私有網絡 中訪問你的服務器可以加大被人***你的難度。

  • 保護內網服務:內網服務包括控制檯,開發服務器,持續集成系統。它們會成爲你安全網絡的一道暗門。 將它們用 ××× 或認證代理鎖起來,如果你沒有使用 ××× 的話,確保傳輸數據以及登錄時總是使用 SSL/HTTPS。 鎖住你的開發環境可以確保 google 不會抓取它從而傷害你的 SEO)

  • 防火牆:只允許指定端口和 IP 訪問你的服務器。硬件防火牆很棒,軟件防火牆比如 iptables 也不錯。

  • 不要在 root 下運行:不在 root 下運行可以防止某些人運用 RCE(remote code execution)獲取你服務器的 root 訪問權限。使用標準用戶登錄服務器,只在必要時使用 sudo。

  • 保護你的第三方服務的賬戶:使用強壯的密碼,儘可能的開啓兩步驗證。

備份

對於數據庫,有一個運行的 replica 可以很方便的執行全備份。推薦在半夜執行全備份。 進行備份的時候有幾個問題需要問一下你自己:

  • 如果有人黑進了你的服務器,他們能夠刪掉或破壞你的備份嗎? 基於這個原因拉取到備份服務器比推送要更好。

  • 如果有人拿到了你的備份會有多糟糕?加密備份文件並且確保***沒法在同一個服務器上找到解密的方法(注:比如可以使用公鑰進行加密,同時服務器上不要存有私鑰,這樣就不會被***在服務器上找到解密的方法)。

  • 你有測試過備份是否可用嗎?定期測試你的備份,驗證它們是否真的有效。

監控

沒有監控的話,線上網站就會成爲一個大黑盒。你沒法知道實際情況是怎樣的,也就沒法進行性能優化了。

instrumentation

對於應用,你需要知道下面這個問題:

  • 我的系統中最慢的部分是什麼?

  • Django 處理響應的平均耗時是多少?

  • 哪個視圖是最慢的?或者花費最多的時間?

  • 哪個數據庫查詢是最慢的?或者花費最多的時間?

  • 這些數據一段時間內是如何變化的?

NewRelic 是個探測這些問題的比較好的服務,它可以很方便的安裝。 然而, NewRelic 是閉源的,專有的系統,同時也非常的貴。有一些開源產品可以替代它。 比如:Graphite

服務器資源

關於服務器資源需要監控如下數據:

  • 平均負載

  • CPU 負載

  • 物理內存使用情況

  • 磁盤相關數據

  • 網絡 I/O

告警

下列情況需要發送告警:

超過 X% 的請求出現錯誤
服務器宕機
服務器資源佔用過高:負載,虛擬內存,磁盤等等
服務未響應

日誌

需要收集如下日誌:

  • 從你的負載均衡器都應用的 Apache 風格的日誌

  • 任何應用內的日誌

  • 相關服務的 syslog 日誌

  • 數據庫慢查詢日誌,以及在不會導致數據庫連接的 I/O 問題(尤其是磁盤或網絡)的前提下收集所有數據庫查詢的日誌。

錯誤彙報

在生成環境下 Django 默認會在出現異常時給網站管理員發送異常郵件。這個功能有時也會導致一些問題:

  • Email 不利於追蹤錯誤

  • 如果你的錯誤發生在一個高訪問的頁面的話,你實際上是在 DoS 你的郵件服務器, 可能導致被加入黑名單,或者你的郵件服務商會關掉你的服務(他們不想在幾秒內發送超過 10K 的郵件)。

幸運的是,更好的錯誤彙報方式已經存在了,開源的 Sentry 項目是個非常好的解決方案。 Sentry 並不會發送 10K 的郵件,它之後在第一次出現錯誤時郵件通知你,之後收集並在 Web 頁面上 暫時其他時候的錯誤用於分析問題。

還有一個關於錯誤的主題就是確保有一個漂亮的不引來應用服務的 500.html 文件,並且已經在服務器上配置好了出錯是使用 這個頁面。

第四章:預備

使用 Jmeter 進行負載測試

這一部分主要講解了 Jmeter 的各種是否方法及配置。

啓動計劃

啓動的時候有些東西需要考慮

  • 使用負載均衡器在新的和舊的之間分流

  • 使用“dark launch”,這樣用戶就不會感覺到他們訪問的是新的服務器

  • 使用代理功能分一部分流量到新的服務器

  • 使用特性標誌來發佈一個新的特性。

發送流量到一個新的沒有緩存的服務器可能會導致臨時的高負載 從而在緩存熱和起來前擊垮你的服務器。預熱你的緩存可以解決這個問題。一個比較簡單的辦法是使用一個腳本在有真實請求前去抓取你的網站上的熱門 URLs。

不要在一天的最後時間段或週五的時候升啓動,除非你想讓你的 整個團隊在晚上或週末加班。 你應該在大家都在並且有幾個小時或幾天的時間來處理 啓動過程中出現的問題時候啓動,同時也要確保你的成員有時間休息。 如果你的網站訪問量比較高的話,嘗試在訪問量比較低的時間段進行升級操作。

啓動前的檢查事項

Django 配置項

  • DEBUG 和 TEMPLATE_DEBUG 都設爲 False

  • SECRET_KEY 是個非常大的隨機字符串並且保密

  • ALLOWED_HOSTS 包含了訪問者可能會使用的有效域名

  • TEMPLATE_LOADERS: Cached template loader 已啓用

  • SESSION_ENGINE 比默認設置更快

  • CACHES: 使用 Memcached 或 Redis 後端

  • MEDIA_ROOT 和 MEDIA_URL 接受並顯示文件上傳

  • 管理員賬戶被限制並且有一個強壯的密碼

部署

  • 通過點擊各種頁面和鏈接的方式來確認網站是否按預期的結果 工作(沒有掛掉的圖片和鏈接)

  • Django 日誌是否正常工作

  • 監控平臺是否接收到數據。確保你能看到整個技術棧中各級 的錯誤信息。

  • 錯誤被彙報並且觸發了通知

  • 第三方服務能夠接收到數據(支付,分析等等)

  • 從你的應用服務和 Celery workers 中發出郵件的功能 能夠正常運行

  • 自定義的錯誤頁面(400,500)已經在各個級別中被設置(負載均衡器,web 加速器,Django)

  • Django admin 沒法通過 /admin/ 公開訪問

  • SSL 證書有效並且設置是安全的。

  • Django-secure 的 manage.py checksecure 運行起來沒有錯誤輸出

基礎設施

  • 服務器和服務是安全的,已經鎖好了大門

  • 有個簡單,正式的程序用來部署新的代碼

  • 你有一個可以在需要的時候快速水平擴展服務的計劃

  • DNS TTL 可以被修改爲 5 分鐘或更短的時間在需要更改的時候

#第五章 The Launch

監控整個 Launch

服務器資源

可以使用如下工具參考服務器資源使用情況

  • htop

  • varnishstat

  • varnishhist

  • varnishtop

  • varnishlop

  • uwsgitop

  • celery inspect

  • celery events

  • flower

  • memcache-top

  • pg_top

  • pg_stat_statements

  • pt-query-digest

  • mytop

當災難來臨的時候

應用服務器過載

最簡單的辦法就是通過增加服務器的方式進行水平擴展。 不過你需要注意這將導致你的數據庫承受更大的壓力, 可能會把數據庫搞掛。

當你通過增加服務器的方式把負載降低到可以接受的級別後, 你就需要使用你的低級別的工具來查看爲什麼會出現負載過高的 情況。你的 web 加速器端緩存命中率過低是一個需要考慮的因素。

數據庫服務器過載

如果你的網站是 讀多寫少的話,可以通過增加 replica 的方式來簡單解決響應時間問題。 同時也看看是否在數據庫優化時遺漏某些可以優化的項。

應用和數據庫服務器過載

你可以從自底向上優化你的數據庫,減輕數據庫的壓力可以讓你的應用擁有更高的性能。 你也可以通過優化你的 web 加速器從而減輕應用服務器的壓力,進而減輕數據庫服務器的壓力。

前方的路

恭喜你的網站已經啓動了!現在你需要確保它能夠持續穩定的運行。在這個戰爭中你需要與下列事物做鬥爭:

  • 你的用戶(流量增長)

  • 你的軟件(一點點腐爛)

  • 你(錯誤的決定)

第一個沒啥好驚訝的,最後兩個可能會讓你驚訝同時也是讓你網站宕機的一些因素。

流量增長

正常情況下你的網站不應該在技術棧的任何層次佔用 100% 的資源,一旦出現超過 70%(CPU,內存,磁盤等等) 的資源佔用,那就說明某些地方需要優化了或者增加更多的資源。當流量突增的時候如果你有額外的資源的話就可以很好 的應對。

有些時候的流量激增可能是商業上進行了某些吸引用戶的活動,確保開發者知道這些商業活動,以便應對激增的流量。 一個好的主意是像剛啓動時那樣,大家在桌子上一起討論分享這些商業信息。

一點點腐爛

由於高性能網站使用了很多不同的服務,所以需要保持這些服務打上了最新的安全補丁。 “如果沒出現問題,就不要去修復它”是個非常危險的準則。

跳過幾個小版本是沒什麼問題的,但是不要落下太遠,不然的話到時就會失去升級動力。 定期小步升級你的依賴(你的操作系統,主要的服務以及 Python 庫)。

作者的小貼士:

當 review 升級的時候,我們通常不會使用最新的版本,一般最新版本也意味着有新的 bug ,我想 你不希望當小白鼠吧。給新版本幾個月的成熟期。 升級到第一個穩定的版本或者你已經確保這個版本沒什麼大的問題了。

錯誤的決定

意外的刷新了緩存

在流量特別高的時候重啓緩存或 web 加速器可能會擊垮你的網站。這個問題叫做 dog-piling/cache stampede 所以儘量使用 reload 的方式來更新服務。對於使用 memcached/redis 的緩存可以通過給不同的緩存定義不同的 KEY_PREFIX 的方式來實現逐個清理緩存(通過 VERSION)而不是一下子刪除所有緩存的功能。 如果一定要重啓的話,進行選擇在流量低的時候重啓服務。

數據庫鎖表

數據庫鎖會臨時阻塞主寫操作,如果這個操作特別長的話就會成爲一個問題。兩個比較常見的長時間鎖表的場景是: schema migration 和備份。

在開發的時候, South 和內置的 migration 可以很方便的應用 Model 更改, 但是在生成環境下, migrate 操作可能會導致長時間的鎖表現象,這個對你的用戶來說就是個糟糕的消息了。 對於 ADD COLUMN 操作,如果你使用的是 MySQL 數據庫的話你需要留意一下這個,但是 PostgreSQL 很少會出現這個問題。

無論你使用哪種數據庫,migrations 都需要被 review 並且使用最近的線上數據的副本數據測試過後才能在生產環境使用。

備份是另一個會導致長時間鎖住數據庫的操作。最好是在一個只讀的 replica 上進行備份操作。

大量的緩存過期

跟刷新緩存差不多糟糕的事情就是有大量的緩存過期,這將導致數據庫壓力倍增。 如果你不是很確定的話,應該選擇在流量比較低的時候進行部署操作,避免擊垮你的站點。

昂貴的 admin 視圖

構建一個未優化的 admin 視圖會導致上千的數據庫查詢。 如果你正在使用一個查詢緩存比如 johnny-cache, 每一次在 admin 中保存都將 導致與所在表相關的緩存都將失效。所以 admin 視圖也需要像普通視圖那樣進行優化。

昂貴的後臺任務

未優化,數據庫依賴的任務會導致非預期的負載負擔。所有應該像優化視圖一樣優化你的後臺任務。

逐漸惡化

隨着新功能的增加,網站的性能也在接受不同的考驗,你需要隨時關注性能問題。

每次發佈新版本的時候都關注一下性能的變化,如果響應時間變長了或者負載變高了, 立馬去解決它。這樣就不會出現在幾個月的提交過去後再去查找問題所在的情況。

complexity creep

如果你按照前面所說的步驟做了的話,你已經做的非常好了。 隨着你的網站的成長你可能會遇到各種各樣的新問題,你可以很容易的就構建自己 獨有的解決方法。 構建你自己的工具是件很有樂趣的事情,但是 not invented here 對於長時間運行 的站點是非常危險的。你最好去學習如何讓 Varnish 更高效而不是丟棄它轉而使用你自己的工具。 這個決定衍生的代價是非常巨大的:

  • 訓練新的開發者需要更高的花費。你可以找到一名擁有使用一個比如 Varnish 這種服務經驗的開發者, 但是如果使用你自定義的方案的話,你需要訓練每一位在門外徘徊的開發者。

  • 開發低級別的基礎設施工具將會導致開發時間遠離了你的核心產品。使用一個支持良好的開源服務, 你的團隊的開發者的效率會很高效。

  • 寫你自己的軟件代碼不是一錘子買賣。所有的軟件都需要不斷的開發,測試,文檔,等等。

最後的想法

現在你可以在看看那個老問題“Django doesn't scale”,你是怎麼認爲的呢? 如果只用 manage.py runserver,使用 SQLite 在一個非常小的雲服務器上跑 當然不會很快。

讓我們回到 2012 年,當 Instagram 只有3個的 Django 團隊卻支撐起了 1千4百多萬的用戶的時候, 他們在博客上是 這麼說的 :

我們選擇一個系統的核心宗旨是:

  • 保持非常簡單

  • 不要重複造輪子

  • 可能的話,使用久經考驗的成熟的技術

我們非常同意。因此,在你繼續你的 Django 旅程的時候,不要忘了你在這裏學到的東西。 簡單是指導原則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章