SlideShare a Scribd company logo
1 of 47
Download to read offline
Drupal 皮、 Web 骨:
我如何為鉅額效能耗損止血
並重新愛上 Drupal ?
DrupalCamp Taipei 2013
Huei-Horng Yo 游輝宏
中央研究院 生物多樣性研究中心
hiroshi@gate.sinica.edu.tw
本簡報檔案內容,除引用材料另依其個別授權規定,
歡迎諸方大德依 CC BY-SA 條款自由取用。
● The Druplicon image is licensed under the GPL License.
● Any trademarks herein are the property of their respective owners. 2
3
Drupal 不快?
現場調查
4
Drupal 不快,
但是 Drupal 很好用?
現場調查
本日案例
● 台 灣 生 物 多 樣 性 資 訊 入 口 網 【 開 發 版 】
http://dev.taibif.tw
● 讀比寫多
● 多種資料來源,衍生複雜的資料查詢、呈現要求
5
頭頭的熱烈請求
6
我們最終的開發成果,
需要可以和生物多樣性
社群共享!
開發者的燒腦運動
共享?那要做成模組?
用 Views 讓人自己選擇需要的欄位?
用 Entity 讓人彈性增減欄位?
便當還沒來嗎?
7
原本的如意算盤計畫
● 用 Drupal 7 Entity & Fields 的彈性規劃欄位
● 用 Views 選出資料
● 用別人已經寫好的模組喔拉喔拉喔拉把站台功能
拼搭出來
● 用 Panels 把資料輸出到版面
● 最後把自主開發的成果寫成 Drupal 模組
8
9
那我們上中二路的攻擊,
可說是毫無破綻嘛!
樂觀、開朗、朝著朝陽奔跑的開發者
喵。
Entity & Fields
● Fields 要先定義,定義後再實體化 (instancelize) ,
與指定的 Entity Type 組合成一個 Bundle
10
Entity & Fields
● Entity 負責描述這份 Content Type 有什麼行為,比
如 Taxonomy 有上下位階層關係
● Fields 負責資料儲存的欄位樣貌(清單、日期、
文字欄位…)
11
Entity & Fields
● 例 如 : 我 們 需 要 一 個 描 述 生 物 物 種 分 類 的
Taxonomy Vocabulary 稱為 Checklist
– 首先需要定義有哪些欄位
– 再把欄位嵌到這個 Vocabulary 後,最後才產生這個特
別用途的 Checklist 分類系統
● 在沒有 Entity 機制之前,得用很髒的方法來達成
同樣的需求
12
Entity & Fields
● 以物件導向程式設計 (OOP) 的觀念來講, Entity
像是在宣告 Class 與 Methods
● Fields 則像是在宣告 Properties, Attributes
● 一切看起來非常合理且優美,符合軟體工程
13
使徒襲來
● 直到…
– 我們並不需要特別儲存 language 欄位
– 我們並不需要 revision 來做版本管理
– 有很多東西我們發現實務上並不需要
– 但是 Views 都會過於好心幫我們一起撈出來
14
使徒襲來
● 直到…
– Fields 存在各自的資料表,欄位愈多, Views 背後跑
的 SQL 要 JOIN 的東西愈多,效能降幅愈恐怖
15
使徒襲來
● 直到…
– 我們為了「彈性」、「模組化」切開的 Views ,背後
的查詢幾乎一樣,僅輸出以及加料處理不同
● hook_views_pre_build()
● hook_views_pre_render()
● hook_views_post_render()
– 因此,重複的查詢、複雜的加工處理,讓整個系統慢
了下來
16
觀察效能瓶頸
● 伺服器端: https://drupal.org/project/devel
● 瀏覽器端:內建效能分析工具
● 找出是哪一個環節慢
– 先查伺服器端,再查瀏覽器端
– 伺服器端,根據經驗都是慢在資料庫這邊, PHP 本身
則隨著版本改進愈來愈快,通常不會是瓶頸
17
單是資料庫查詢就花了五秒多
眼花撩亂的 JOIN
18
頭頭的熱烈關切
19
太慢了。
嗚…
拉效能
● Cache ,快娶快取
– PHP APC https://drupal.org/project/apc
– Boost https://drupal.org/project/boost
– Drupal 與 Views 本身內建的 Cache 機制
● 在這個案例裡都是捨本逐末,沒有打到靶心
● 但是對於一般正式上線的 Drupal 網站來說,其實
都是效能調校的基本功
20
拉效能
● 問題癥結應在資料庫
– 先天不良:絕大部分預裝好的 MySQL 配置,都不是針
對做大事業的配置
– 請愛用 MySQL Performance Tuning Primer Script
http://www.day32.com/MySQL/
– 後天又太操勞:做了太多不必要的查詢
21
頭頭再度熱烈關切
22
還是太慢了。
沒辦法,太多重複、複雜的
資料庫操作了…
啟用各種快取手段後…
頭頭再度熱烈關切
23
你要不要考慮砍掉重練、
不要用 Drupal 了?
……
等價交換
● 人不付出犧牲,便得不到任何回報;如果要得到
什麼,就必須付出同等代價。這就是鍊金術的基
本原則──等價交換。當時我們深信這就是世界
的真理。
(摘自《鋼の錬金術師》片頭)
24
25
等價交換
如果不要堅持做成模組「共享」,
效能問題可以用程式寫死來改善。
你好大我好怕,
這樣幹嘛還要用 Drupal 呢?
Drupal 不快,但是 Drupal 很好用
● 退可守:
– 許多現成、經過多年考驗的機制(會員管理、內容管
理、多國語言…)
– 小網站專案可以不用寫任何一行程式
26
Drupal 不快,但是 Drupal 很好用
● 進可攻:
– 要做大案子, Drupal 也有設計得不錯的 API 和 coding
框架可遵守
– PHP 是個可以寫得太過自由奔放的網頁開發語言,對
於團隊專案的程式碼品質來說,是個隱憂
– Drupal 不只是 CMS ,亦是 framework
27
28
開發者的決定
砍掉重練?不!
好啦,你搞得定就好…(抖)
問題妍希研析
● 「做了太多不必要的查詢」
29
對策
● 精簡查詢
● 且要維持程式的可維護性
30
幾乎拋掉 Views 不用
● 不適合本案例這種過於複雜的場合
● 改用 EntityFieldQuery 來查詢資料
31
Use EntityFieldQuery!
● Why not SQL?
– 雖然 Drupal 有 db_query() 和 db_query_range() 這兩個做
參數化查詢的函式
– 但是 SQL 還是不易維護
32
Use EntityFieldQuery!
● Why EntityFieldQuery?
– 如字面上的意思,泛用的 Entity 查詢工具
– 取效能與可維護性的折衷
– 效能:實際跑的 SQL 較 Views 簡潔
– 可維護性:物件導向式用法,好寫、好讀
33
Use EntityFieldQuery!
● 常用的方法
– entityCondition() 給定選擇 Entity 的查詢條件
– propertyCondition() 給定針對 Entity 屬性的查詢條件
– fieldCondition() 給定針對 Fields 的查詢條件
– count() 只想知道符合查詢條件的筆數
– range() 只要符合指定筆數範圍的資料
34
Use EntityFieldQuery!
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
      ->propertyCondition('vid',
taxonomy_vocabulary_machine_name_load('checklist')->vid)
      ->fieldCondition('taxon_unique_id', 'value', $unique_id)
      ->range(0, 1);
 
$terms = $query->execute();
 
foreach($terms as $result => &$object) {
  foreach($object as &$term) {
    $term_entity = taxonomy_term_load($term->tid);
    /* ... */
  }
}
35
Use EntityFieldQuery!
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
      ->propertyCondition('vid',
taxonomy_vocabulary_machine_name_load('checklist')->vid)
      ->fieldCondition('taxon_unique_id', 'value', $unique_id)
      ->range(0, 1);
 
$terms = $query->execute();
 
foreach($terms as $result => &$object) {
  foreach($object as &$term) {
    $term_entity = taxonomy_term_load($term->tid);
    /* ... */
  }
}
36
Use EntityFieldQuery!
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
      ->propertyCondition('vid',
taxonomy_vocabulary_machine_name_load('checklist')->vid)
      ->fieldCondition('taxon_unique_id', 'value', $unique_id)
      ->range(0, 1);
 
$terms = $query->execute();
 
foreach($terms as $result => &$object) {
  foreach($object as &$term) {
    $term_entity = taxonomy_term_load($term->tid);
    /* ... */
  }
}
37
Use EntityFieldQuery!
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
      ->propertyCondition('vid',
taxonomy_vocabulary_machine_name_load('checklist')->vid)
      ->fieldCondition('taxon_unique_id', 'value', $unique_id)
      ->range(0, 1);
 
$terms = $query->execute();
 
foreach($terms as $result => &$object) {
  foreach($object as &$term) {
    $term_entity = taxonomy_term_load($term->tid);
    /* ... */
  }
}
38
Use EntityFieldQuery!
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
      ->propertyCondition('vid',
taxonomy_vocabulary_machine_name_load('checklist')->vid)
      ->fieldCondition('taxon_unique_id', 'value', $unique_id)
      ->range(0, 1);
 
$terms = $query->execute();
 
foreach($terms as $result => &$object) {
  foreach($object as &$term) {
    $term_entity = taxonomy_term_load($term->tid);
    /* ... */
  }
}
39
Use EntityFieldQuery!
● 盡可能以簡單的查詢條件把符合的 Entities 選出來
● 盡可能查出來的東西,放在變數裡,直到程式結
束前都可重複利用,不需要再反覆多做工
● 妥善利用 MySQL 本身的快取機制
– 別忘了前面提過的,很多為了 Drupal 安裝的 MySQL 都
沒有最佳化
– 查詢愈簡單,查詢愈快,建立快取同時也愈有效率
40
【友情宣傳】
● 請大家多多支持隔天的「六小時 View 一生:從
入門到精通」練功坊
● 別被我們這個案例嚇到,事實上 Views 處理絕大
部分資料查詢場合依然很好用
– 所以我們仍用在新知動態列表之類的地方
41
Don't use Entity, use Web Services
● 我們的資料來源太多太雜,一股腦地收納進 Drupal
不見得是好作法
● 逆向思考,將既有的資料來源用 Sinatra 包成 API ,
讓 Drupal 透過 Web Services 取得 JSON 格式資料
● 從「程式模組」轉而採取 Open Data 達成起初的
「成果資源共享」目的
● 開發行動 App 等需求因此也變方便
42
Layout, Frontend
● 即使手刻程式,也要 follow Drupal Way
● 使用 theme_*() 函數將資料所得結果輸出為 HTML
https://api.drupal.org/api/drupal/includes!theme.inc/7
– theme_image()
– theme_item_list()
– theme_link()
– theme_html_tag()
– …
43
Layout, Frontend
● 利用 Drupal 提供的函式,配合版型引擎,將程式
邏輯與結果輸出,分而治之
● 整個網站還是很優雅,不因為我們回歸手刻 PHP
而變得亂糟糟
44
Less is More, More is Less
5226.24 ms → 79.11 ms
� 66x Speedup!
※ 皆在已清除快取資料下量測
45
閉場黑
● 打到痛處,對症下藥
● 多寫幾行程式,您可以不用砍掉重練
● Drupal 進可攻、退可守,敬請愛用
46
感恩吶,我講完了
又到了大家喜聞樂見的發問時間
好怕被問倒…

More Related Content

Viewers also liked

Coscup 2013 Lightning Talk (was not really presented)
Coscup 2013 Lightning Talk (was not really presented)Coscup 2013 Lightning Talk (was not really presented)
Coscup 2013 Lightning Talk (was not really presented)Hui-Hong You
 
The scuba sutras: Ten lessons from under the sea
The scuba sutras: Ten lessons from under the seaThe scuba sutras: Ten lessons from under the sea
The scuba sutras: Ten lessons from under the seaGuhesh Ramanathan
 
Pinkoi 與 RWD @RGBA 構思
Pinkoi 與 RWD @RGBA 構思Pinkoi 與 RWD @RGBA 構思
Pinkoi 與 RWD @RGBA 構思Adam Wang
 
Google analytics教學手冊
Google analytics教學手冊Google analytics教學手冊
Google analytics教學手冊煜庭 邱
 

Viewers also liked (14)

Tobch Lecture
Tobch LectureTobch Lecture
Tobch Lecture
 
Pm104 Standard
Pm104 StandardPm104 Standard
Pm104 Standard
 
Lecture7
Lecture7Lecture7
Lecture7
 
Coscup 2013 Lightning Talk (was not really presented)
Coscup 2013 Lightning Talk (was not really presented)Coscup 2013 Lightning Talk (was not really presented)
Coscup 2013 Lightning Talk (was not really presented)
 
Lecture6
Lecture6Lecture6
Lecture6
 
Chap07alg
Chap07algChap07alg
Chap07alg
 
The scuba sutras: Ten lessons from under the sea
The scuba sutras: Ten lessons from under the seaThe scuba sutras: Ten lessons from under the sea
The scuba sutras: Ten lessons from under the sea
 
Lecture912
Lecture912Lecture912
Lecture912
 
Lecture911
Lecture911Lecture911
Lecture911
 
Lecture914
Lecture914Lecture914
Lecture914
 
Chap09alg
Chap09algChap09alg
Chap09alg
 
Chap08alg
Chap08algChap08alg
Chap08alg
 
Pinkoi 與 RWD @RGBA 構思
Pinkoi 與 RWD @RGBA 構思Pinkoi 與 RWD @RGBA 構思
Pinkoi 與 RWD @RGBA 構思
 
Google analytics教學手冊
Google analytics教學手冊Google analytics教學手冊
Google analytics教學手冊
 

Similar to Drupal 皮、Web 骨:我如何為鉅額效能耗損止血並重新愛上 Drupal?

數位科技工具在自我學習上的運用
數位科技工具在自我學習上的運用數位科技工具在自我學習上的運用
數位科技工具在自我學習上的運用基欽 劉
 
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇Drupal Taiwan
 
Angular js twmvc#17
Angular js twmvc#17Angular js twmvc#17
Angular js twmvc#17twMVC
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001rainx1982
 
山頂洞人日記 - 回歸到最純樸的開發
山頂洞人日記 -  回歸到最純樸的開發山頂洞人日記 -  回歸到最純樸的開發
山頂洞人日記 - 回歸到最純樸的開發koji lin
 
專案分層架構 twMVC#18
專案分層架構 twMVC#18專案分層架構 twMVC#18
專案分層架構 twMVC#18twMVC
 
twMVC#18 | 專案分層架構
twMVC#18 | 專案分層架構twMVC#18 | 專案分層架構
twMVC#18 | 專案分層架構twMVC
 
Java Tutorial:Learn Java in 06:00:00
Java Tutorial:Learn Java in 06:00:00Java Tutorial:Learn Java in 06:00:00
Java Tutorial:Learn Java in 06:00:00Justin Lin
 
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)Poy Chang
 
安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fbAlbert Hong
 
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...Souyi Yang
 
Azure Data Lake 簡介
Azure Data Lake 簡介Azure Data Lake 簡介
Azure Data Lake 簡介Herman Wu
 
AI for Everyone (Chinese)
AI for Everyone (Chinese)AI for Everyone (Chinese)
AI for Everyone (Chinese)Xiao-Wei CAO
 
Search engine
Search engineSearch engine
Search engineSamchu Li
 
Chapter 4 models
Chapter 4 modelsChapter 4 models
Chapter 4 modelsEkman Hsieh
 
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617Chia-chun Yeh
 
Drupal7第三堂
Drupal7第三堂Drupal7第三堂
Drupal7第三堂Hen Chen
 
網路2.0時代情報蒐集術
網路2.0時代情報蒐集術網路2.0時代情報蒐集術
網路2.0時代情報蒐集術基欽 劉
 

Similar to Drupal 皮、Web 骨:我如何為鉅額效能耗損止血並重新愛上 Drupal? (20)

數位科技工具在自我學習上的運用
數位科技工具在自我學習上的運用數位科技工具在自我學習上的運用
數位科技工具在自我學習上的運用
 
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
 
Angular js twmvc#17
Angular js twmvc#17Angular js twmvc#17
Angular js twmvc#17
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 
山頂洞人日記 - 回歸到最純樸的開發
山頂洞人日記 -  回歸到最純樸的開發山頂洞人日記 -  回歸到最純樸的開發
山頂洞人日記 - 回歸到最純樸的開發
 
專案分層架構 twMVC#18
專案分層架構 twMVC#18專案分層架構 twMVC#18
專案分層架構 twMVC#18
 
twMVC#18 | 專案分層架構
twMVC#18 | 專案分層架構twMVC#18 | 專案分層架構
twMVC#18 | 專案分層架構
 
Java Tutorial:Learn Java in 06:00:00
Java Tutorial:Learn Java in 06:00:00Java Tutorial:Learn Java in 06:00:00
Java Tutorial:Learn Java in 06:00:00
 
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)
使用 Dependency Injection 撰寫簡潔 C# 程式碼原來這麼簡單 (.NET Conf 2018)
 
安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb
 
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...
Drupal 是好的生財工具嗎?網站標案經驗分享 台灣i運動資訊平台(Drupal as a Cash Cow for Prodution House? ...
 
談 ORCID如何協助解決學術身分識別問題
談 ORCID如何協助解決學術身分識別問題談 ORCID如何協助解決學術身分識別問題
談 ORCID如何協助解決學術身分識別問題
 
Azure Data Lake 簡介
Azure Data Lake 簡介Azure Data Lake 簡介
Azure Data Lake 簡介
 
AI for Everyone (Chinese)
AI for Everyone (Chinese)AI for Everyone (Chinese)
AI for Everyone (Chinese)
 
Search engine
Search engineSearch engine
Search engine
 
Chapter 4 models
Chapter 4 modelsChapter 4 models
Chapter 4 models
 
CRUD 綜合運用
CRUD 綜合運用CRUD 綜合運用
CRUD 綜合運用
 
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617
「105年度政府開放資料研究案」資料結構化專家會議簡報 V1.2 20160617
 
Drupal7第三堂
Drupal7第三堂Drupal7第三堂
Drupal7第三堂
 
網路2.0時代情報蒐集術
網路2.0時代情報蒐集術網路2.0時代情報蒐集術
網路2.0時代情報蒐集術
 

More from Hui-Hong You

Arch Linux Taiwan Community Introduction - COSCUP 2017
Arch Linux Taiwan Community Introduction - COSCUP 2017Arch Linux Taiwan Community Introduction - COSCUP 2017
Arch Linux Taiwan Community Introduction - COSCUP 2017Hui-Hong You
 
Besides programming
Besides programmingBesides programming
Besides programmingHui-Hong You
 
fcitx-chewing 逐行審查
fcitx-chewing 逐行審查fcitx-chewing 逐行審查
fcitx-chewing 逐行審查Hui-Hong You
 
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例Hui-Hong You
 
Feeds & Node Import: Quick Importing Tools for Drupal
Feeds & Node Import: Quick Importing Tools for DrupalFeeds & Node Import: Quick Importing Tools for Drupal
Feeds & Node Import: Quick Importing Tools for DrupalHui-Hong You
 

More from Hui-Hong You (6)

Explore DVB-T SI
Explore DVB-T SIExplore DVB-T SI
Explore DVB-T SI
 
Arch Linux Taiwan Community Introduction - COSCUP 2017
Arch Linux Taiwan Community Introduction - COSCUP 2017Arch Linux Taiwan Community Introduction - COSCUP 2017
Arch Linux Taiwan Community Introduction - COSCUP 2017
 
Besides programming
Besides programmingBesides programming
Besides programming
 
fcitx-chewing 逐行審查
fcitx-chewing 逐行審查fcitx-chewing 逐行審查
fcitx-chewing 逐行審查
 
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例
用 Ruby 操作 OSM 資料批次匯入:以 YouBike 微笑單車租借站資料為例
 
Feeds & Node Import: Quick Importing Tools for Drupal
Feeds & Node Import: Quick Importing Tools for DrupalFeeds & Node Import: Quick Importing Tools for Drupal
Feeds & Node Import: Quick Importing Tools for Drupal
 

Drupal 皮、Web 骨:我如何為鉅額效能耗損止血並重新愛上 Drupal?