<?php
$dbAdapter = new PDO("mysql:host=localhost;dbname=test", "root", "1234");
$dbAdapter->exec("SET NAMES 'utf8';");
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_ASSOC);
//var_dump($data);
/*
array(
array(
'id' => '1',
'name' => 'HBO',
'method' => 'service',
),
array(
'id' => '2',
'name' => '本週新片',
'method' => 'movie',
),
array(
'id' => '3',
'name' => '熱映中',
'method' => 'movie',
),
)
*/
$data = $dbAdapter->query("
SELECT name, method FROM category
")->fetchAll(PDO::FETCH_COLUMN);
//var_dump($data);
/*
array(
'HBO',
'本週新片',
'熱映中',
)
*/
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC);
//var_dump($data);
/*
array(
'1' => array(
'name' => 'HBO',
'method' => 'service',
),
'2' => array(
'name' => '本週新片',
'method' => 'movie',
),
'3' => array(
'name' => '熱映中',
'method' => 'movie',
),
)
*/
$data = $dbAdapter->query("
SELECT method, id, name FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC);
//var_dump($data);
/*
array(
'service' => array(
'id' => '1',
'name' => 'HBO',
),
'movie' => array(
'id' => '3',
'name' => '熱映中',
),
)
*/
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_COLUMN);
//var_dump($data);
/*
array(
'1' => 'HBO',
'2' => '本週新片',
'3' => '熱映中',
)
*/
$data = $dbAdapter->query("
SELECT method, name, id FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_COLUMN);
//var_dump($data);
/*
array(
'service' => 'HBO',
'movie' => '熱映中',
)
*/
$data = $dbAdapter->query("
SELECT method, id, name FROM category
")->fetchAll( PDO::FETCH_ASSOC | PDO::FETCH_GROUP);
//var_dump($data);
/*
array(
'service' => array(
array(
'id' => '1'
'name' => 'HBO'
),
)
'movie' => array(
array(
'id' => '2'
'name' => '本週新片'
),
array(
'id' => '3'
'name' => '熱映中'
),
)
)
*/
$data = $dbAdapter->query("
SELECT method, name, id FROM category
")->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_COLUMN);
//var_dump($data);
/*
array(
'service' => array(
'HBO'
),
'movie' => array(
'本週新片'
'熱映中'
),
)
*/
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_OBJ);
//var_dump($data);
/*
array(
stdClass{
public $id = '1';
public $name = 'HBO';
public $method = 'service';
},
stdClass{
public $id = '2';
public $name = '本週新片';
public $method = 'movie';
},
stdClass{
public $id = '3';
public $name = '熱映中';
public $method = 'movie';
},
)
*/
class Category_1 {}
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Category_1");
//var_dump($data);
/*
array(
Category_1{
public $id = '1';
public $name = 'HBO';
public $method = 'service';
},
Category_1{
public $id = '2';
public $name = '本週新片';
public $method = 'movie';
},
Category_1{
public $id = '3';
public $name = '熱映中';
public $method = 'movie';
},
),
*/
class Category_2 {
public $name;
public $method;
public function __construct() {}
public function __set($name, $value ){}
}
$data = $dbAdapter->query("
SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Category_2");
//var_dump($data);
/*
array(
Category_2{
public $name = 'HBO';
public $method = 'service';
},
Category_2{
public $name = '本週新片';
public $method = 'movie';
},
Category_2{
public $name = '熱映中';
public $method = 'movie';
},
)
*/
2013-04-04 22:05
PDO fetch 模式的回傳結果
PDO 的 fetch 模式功能實在是太方便了,但每次要產生想要的結果都要試太麻煩了,這裡列出可能的組合。
2013-02-26 08:37
[JSX] 串接圖片
由於工作上的需要將圖片分類串接成好幾組圖片,所以寫了一個 PhotoShop Scripting 來完成這件事。
圖片名稱是:
a001-01.jpg
a001-02.jpg
a002-01.jpg
a002-02.jpg
b001-01.jpg
b001-02.jpg
b001-03.jpg
而希望可以依 a001.jpg, a002.jpg, b001.jpg 分類串接在一起。
執行方式:
圖片名稱是:
a001-01.jpg
a001-02.jpg
a002-01.jpg
a002-02.jpg
b001-01.jpg
b001-02.jpg
b001-03.jpg
而希望可以依 a001.jpg, a002.jpg, b001.jpg 分類串接在一起。
執行方式:
- 檔案 -> 指令碼 -> 瀏覽 -> 選擇 photo_join.jsx
- 在選擇多張要串接的圖片
/** 圖片串接 for (PhotoShop Script)
* author : Jax
* email : weskerjax[at]gmail.com
* website : http://jax-work-archive.blogspot.com/
* history : 2013.02.23
*/
// photo_join.jsx
//#target photoshop
/**水平邊界px*/
var levMargin = 10;
/**垂直邊界px*/
var verMargin = 10;
var WebJpegOptions = new ExportOptionsSaveForWeb()
WebJpegOptions.format = SaveDocumentType.JPEG;
WebJpegOptions.quality=80;
function main(){
/*取得多個的檔案路徑*/
var fileList = File.openDialog(
"開啟 JPG 檔案",
"JPG Files:*.jpg",
true
);
if(fileList.length == 0){ alert("沒有任何檔案"); return; }
/*依檔名排序*/
fileList.sort();
/*檔案依前綴分群*/
var groupList = {};
var filePath, i=0;
while(filePath=fileList[i++]){
var prefix=filePath.name.match(/([a-z0-9]+)[-_].+\.jpg/i);
if(!prefix || !prefix[1]){continue;}
if(!groupList[prefix[1]]){ groupList[prefix[1]]=[]; }
groupList[prefix[1]].push(filePath);
}
/*依分群串接圖片*/
for(prefix in groupList){
photoJoin(prefix,groupList[prefix]);
}
}
function copyArt(toArt, fromArt){
/*複製圖層*/
app.activeDocument=fromArt;
fromArt.selection.selectAll()
fromArt.selection.copy()
/*貼上圖層*/
app.activeDocument=toArt;
return toArt.paste();
}
function photoJoin(prefix, fileList){
/*新增目標圖片文件*/
var newPic = app.documents.add(
1, 1, 72,
prefix,
NewDocumentMode.RGB,
DocumentFill.TRANSPARENT
);
workSpace=fileList[0].parent.fsName.replace(/\\/ig,'/');;
var height=0;
var width = newPic.width;
var correspond = [];
/*複製所有圖檔至新建立的圖檔*/
for (var i=0; i<fileList.length; i++){
try {
var atDoc=open(new File(fileList[i]));
} catch (e) { continue; }
/*累計高度*/
height += atDoc.height.value + verMargin;
/*最大寬度*/
width = Math.max(width,atDoc.width);
/*複製圖層*/
var layer = copyArt(newPic, atDoc);
correspond.push({
'layer':layer,
'height':atDoc.height.value
});
/*關閉檔案*/
atDoc.close(SaveOptions.DONOTSAVECHANGES);
};
app.activeDocument=newPic;
/*變更圖片大小*/
newPic.resizeCanvas(
width+levMargin*2,
height+verMargin,
AnchorPosition.TOPLEFT
);
/*變更圖層定位*/
var top=verMargin;
for (var i=0,l=correspond.length; i<l; i++){
if(correspond[i].layer===undefined){continue;}
correspond[i].layer.translate(levMargin, top); /*移動圖層*/
top += correspond[i].height+verMargin; /*定位累計*/
};
/*匯出網頁JPEG*/
newPic.exportDocument(
new File(File.encode(workSpace+"/"+prefix+".jpg")),
ExportType.SAVEFORWEB,
WebJpegOptions
);
/*關閉 newArt 文件*/
newPic.close(SaveOptions.DONOTSAVECHANGES);
}
/*把Photoshop推到最上層*/
app.bringToFront();
/*設定使用的單位為「像素(Pixel)」*/
app.preferences.rulerUnits = Units.PIXELS;
/*執行主程式*/
main();
alert('成功執行!');
2012-09-30 22:24
[轉載][PHP] preg_replace 效能測試 (將兩個空白字元以上取代成一個)
轉載自:[PHP] preg_replace 效能測試 (將兩個空白字元以上取代成一個) 小惡魔 – 電腦技術 – 工作筆記 – AppleBOY
preg_replace 可以使用正規語法來取代字串任何字元,,今天探討取代空白字元的效能,雖然這是個不起眼的效能評估,一般人不太會這樣去改,不過這是國外 PHP Framework 有人提出來修正的,經過許多人的測試一致同意。功能就是一篇文章內如果有多餘的空白能空取代成一個,一般人都會用 \s+ 正規語法,畢竟大家都知道 \s 代表單一空白或 \r 等符號,但是國外有人提出用 {2,} 方式來取代空白。程式碼如下,大家可以測試看看。
測試結果(1萬次)
結論至少快蠻多的,如果整體資料量再大一點,我想差別會更大,那至於要不要用呢,就看個人了 XD。
preg_replace 可以使用正規語法來取代字串任何字元,,今天探討取代空白字元的效能,雖然這是個不起眼的效能評估,一般人不太會這樣去改,不過這是國外 PHP Framework 有人提出來修正的,經過許多人的測試一致同意。功能就是一篇文章內如果有多餘的空白能空取代成一個,一般人都會用 \s+ 正規語法,畢竟大家都知道 \s 代表單一空白或 \r 等符號,但是國外有人提出用 {2,} 方式來取代空白。程式碼如下,大家可以測試看看。
<?php
$nb = 10000;
$str = str_repeat('Hi, I am appleboy ' . "\n", 10);
$t1 = microtime(true);
for ($i = $nb; $i--; ) {
preg_replace('/\s+/', ' ', $str);
}
$t2 = microtime(true);
for ($i = $nb; $i--; ) {
preg_replace('/ {2,}/', ' ',
str_replace(array("\r","\n","\t","\x0B","\x0C"),' ',$str)
);
}
$t3 = microtime(true);
echo $t2 - $t1;
echo "\n";
echo $t3 - $t2;
測試結果(1萬次)
PHP 5.3.3 old: 0.13053798675537 new: 0.058536052703857 PHP 5.3.15 old: 0.11732506752014 new: 0.071418046951294 PHP 5.3.17 old: 0.11612010002136 new: 0.07065486907959 PHP 5.4.5 old: 0.1185781955719 new: 0.066012859344482 PHP 5.4.7 old: 0.11343121528625 new: 0.066931962966919
結論至少快蠻多的,如果整體資料量再大一點,我想差別會更大,那至於要不要用呢,就看個人了 XD。
分類:
轉載,
PHP,
Regular Expression
1 回應
2012-09-05 13:32
Apache 消失的 ServerLimit 參數
在某些版本的 Apache 中如果單純加大 MaxClients 的設定值,就會得到以下的錯誤訊息:
這時必須再增加 ServerLimit 這個設定,而這個設定在設定檔中並沒有提示,所以需要額外加入。
停止並啟動(單純 restart 無法套用設定)
service apache2 stop
service apache2 start
參考來源:
Gilbert.tw > Apache 之謎樣 ServerLimit 指令 (directive)
WARNING: MaxClients of 300 exceeds ServerLimit value of 256 servers,
lowering MaxClients to 256. To increase, please see the ServerLimit
directive.
這時必須再增加 ServerLimit 這個設定,而這個設定在設定檔中並沒有提示,所以需要額外加入。
<IfModule mpm_prefork_module>
ServerLimit 500 # 額外加入
StartServers 10
MinSpareServers 10
MaxSpareServers 20
MaxClients 300
MaxRequestsPerChild 0
</IfModule>
停止並啟動(單純 restart 無法套用設定)
service apache2 stop
service apache2 start
參考來源:
Gilbert.tw > Apache 之謎樣 ServerLimit 指令 (directive)
2012-09-05 13:01
[Ubuntu] 用 logrotate 定期切割 Apache log 檔
Ubuntu 預設已經為 Apache 建立了 log 分割管理,但在建立虛擬主機(Virtual Host),但又很龜毛的將 log 獨立到個別的子目錄下的話,預設的 logrotate 是沒有辦法處理到的,所以請記得為這龜毛的動作加上額外設定。
編輯屬於 Apache 的設定檔,在檔案最後面加上額外的設定。
vim /etc/logrotate.d/apache2
參數說明:
daily 每天整理,其他還有 weekly, monthly, yearly
missingok 允許 log 檔不存在
nomissingok 不允許 log 檔不存在,會丟出錯誤 (預設)
rotate 7 保留 7 次的分割
size 1M 大小超過 1M 就執行分割,單位可以是 1024, 100k, 4M, 1G
dateext 用日期來建立分割檔名,而非流水號
dateformat -%Y%m%d 日期格式
compress 壓縮分割,預設用 gzip (預設)
nocompress 不壓縮分割
delaycompress 延後壓縮直到下一次 rotate
notifempty 如果 log 是空的就不執行
copytruncate 以[複製清空]的方式分割 log,這樣可以用不到 postrotate 設定
mail xxx@address 將分割內容用 mail 寄出
prerotate/endscript 分割前要執行的指令
postrotate/endscript 分割後要執行的指令
測試設定檔是否正確
logrotate -f /etc/logrotate.conf
參考來源:
使用 logrotate 定期整理 Rails Log 檔案
[ubuntu] 使用 logrotate 定期切割壓縮 apache2 的 log 檔
編輯屬於 Apache 的設定檔,在檔案最後面加上額外的設定。
vim /etc/logrotate.d/apache2
/var/log/apache2/blog/*.log {
daily
missingok
rotate 52
dateext
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
/etc/init.d/apache2 reload > /dev/null
endscript
}
參數說明:
daily 每天整理,其他還有 weekly, monthly, yearly
missingok 允許 log 檔不存在
nomissingok 不允許 log 檔不存在,會丟出錯誤 (預設)
rotate 7 保留 7 次的分割
size 1M 大小超過 1M 就執行分割,單位可以是 1024, 100k, 4M, 1G
dateext 用日期來建立分割檔名,而非流水號
dateformat -%Y%m%d 日期格式
compress 壓縮分割,預設用 gzip (預設)
nocompress 不壓縮分割
delaycompress 延後壓縮直到下一次 rotate
notifempty 如果 log 是空的就不執行
copytruncate 以[複製清空]的方式分割 log,這樣可以用不到 postrotate 設定
mail xxx@address 將分割內容用 mail 寄出
prerotate/endscript 分割前要執行的指令
postrotate/endscript 分割後要執行的指令
測試設定檔是否正確
logrotate -f /etc/logrotate.conf
參考來源:
使用 logrotate 定期整理 Rails Log 檔案
[ubuntu] 使用 logrotate 定期切割壓縮 apache2 的 log 檔
2012-09-05 11:46
[轉載] [MySQL] QueryCache原理
轉載自:PHP 程式 學習 筆記本 [Mysql]QueryCache原理
原理
QueryCache(下面簡稱QC)是根據SQL語句來cache的。一個SQL查詢如果以select開頭,那麼MySQL服務器將嘗試對其使用QC。每個Cache都是以SQL文本作為key來存的。在應用QC之前,SQL文本不會被作任何處理。也就是說,兩個SQL語句,只要相差哪怕是一個字符(例如大小寫不一樣;多一個空格等),那麼這兩個SQL將使用不同的一個CACHE。
不過SQL文本有可能會被客戶端做一些處理。例如在官方的命令行客戶端裡,在發送SQL給服務器之前,會做如下處理:
下面的三條SQL裡,因為SELECT大小寫的關係,最後一條和其他兩條在QC裡肯定是用的不一樣的存儲位置。而第一條和第二條,區別在於後者有個註釋,在不同客戶端,會有不一樣的結果。所以,保險起見,請儘量不要使用動態的註釋。在PHP的mysql擴展裡,SQL的註釋是不會被去掉的。也就是三條SQL會被存儲在三個不同的緩存裡,雖然它們的結果都是一樣的。
目前只有select語句會被cache,其他類似show,use的語句則不會被cache。
因為QC是如此前端,如此簡單的一個緩存系統,所以如果一個表被更新,那麼和這個表相關的SQL的所有QC都會被失效。假設一個聯合查詢裡涉及到了表A和表B,如果表A或者表B的其中一個被更新(update或者delete),這個查詢的QC將會失效。
也就是說,如果一個表被頻繁更新,那麼就要考慮清楚究竟是否應該對相關的一些SQL進行QC了。一個被頻繁更新的表如果被應用了QC,可能會加重數據庫的負擔,而不是減輕負擔。我一般的做法是默認打開QC,而對一些涉及頻繁更新的表的SQL語句加上SQL_NO_CACHE關鍵詞來對其禁用CACHE。這樣可以儘可能避免不必要的內存操作,儘可能保持內存的連續性。
那些查詢很分散的SQL語句,也不應該使用QC。例如用來查詢用戶和密碼的語句——「select pass from user where name='surfchen'」。這樣的語句,在一個系統裡,很有可能只在一個用戶登陸的時候被使用。每個用戶的登陸所用到的查詢,都是不一樣的SQL文本,QC在這裡就幾乎不起作用了,因為緩存的數據幾乎是不會被用到的,它們只會在內存裡佔地方。
存儲塊
在本節裡「存儲塊」和「block」是同一個意思
QC緩存一個查詢結果的時候,一般情況下不是一次性地分配足夠多的內存來緩存結果的。而是在查詢結果獲得的過程中,逐塊存儲。當一個存儲塊被填滿之後,一個新的存儲塊將會被創建,並分配內存(allocate)。單個存儲塊的內存分配大小通過query_cache_min_res_unit參數控制,默認為4KB。最後一個存儲塊,如果不能被全部利用,那麼沒使用的內存將會被釋放。如果被緩存的結果很大,那麼會可能會導致分配內存操作太頻繁,系統系能也隨之下降;而如果被緩存的結果都很小,那麼可能會導致內存碎片過多,這些碎片如果太小,就很有可能不能再被分配使用。
除了查詢結果需要存儲塊之外,每個SQL文本也需要一個存儲塊,而涉及到的表也需要一個存儲塊(表的存儲塊是所有線程共享的,每個表只需要一個存儲塊)。存儲塊總數量=查詢結果數量*2+涉及的數據庫表數量。也就是說,第一個緩存生成的時候,至少需要三個存儲塊:表信息存儲塊,SQL文本存儲塊,查詢結果存儲塊。而第二個查詢如果用的是同一個表,那麼最少只需要兩個存儲塊:SQL文本存儲塊,查詢結果存儲塊。
通過觀察Qcache_queries_in_cache和Qcache_total_blocks可以知道平均每個緩存結果占用的存儲塊。它們的比例如果接近1:2,則說明當前的query_cache_min_res_unit參數已經足夠大了。如果Qcache_total_blocks比Qcache_queries_in_cache多很多,則需要增加query_cache_min_res_unit的大小。
Qcache_queries_in_cache * query_cache_min_res_unit(sql文本和表信息所在的block佔用的內存很小,可以忽略)如果遠遠大於query_cache_size-Qcache_free_memory,那麼可以嘗試減小query_cache_min_res_unit的值。
調整大小
如果Qcache_lowmem_prunes增長迅速,意味著很多緩存因為內存不夠而被釋放,而不是因為相關表被更新。嘗試加大query_cache_size,儘量使Qcache_lowmem_prunes零增長。
啟動參數
show variables like 'query_cache%' 可以看到這些信息。
query_cache_limit:如果單個查詢結果大於這個值,則不Cache
query_cache_size:分配給QC的內存。如果設為0,則相當于禁用QC。要注意QC必須使用大約40KB來存儲它的結構,如果設定小於40KB,則相當于禁用QC。QC存儲的最小單位是1024 byte,所以如果你設定了一個不是1024的倍數的值,這個值會被四捨五入到最接近當前值的等於1024的倍數的值。
query_cache_type:
0 完全禁止QC,不受SQL語句控制(另外可能要注意的是,即使這裡禁用,上面一個參數所設定的內存大小還是會被分配);
1啟用QC,可以在SQL語句使用SQL_NO_CACHE禁用;
2可以在SQL語句使用SQL_CACHE啟用。
query_cache_min_res_unit:每次給QC結果分配內存的大小
狀態
show status like 'Qcache%' 可以看到這些信息。
Qcache_free_blocks:當一個表被更新之後,和它相關的cache blocks將被free。但是這個block依然可能存在隊列中,除非是在隊列的尾部。這些blocks將會被統計到這個值來。可以用FLUSH QUERY CACHE語句來清空free blocks。
其他幾個狀態變量的意義:
Qcache_free_memory 表示查詢緩存區現在還有多少的可用內存,如果很小,考慮增加query_cache_size
Qcache_hits 表示查詢緩存區的命中個數,也就是直接從查詢緩存區作出響應處理的查詢個數
Qcache_inserts 表示查詢緩存區此前總過緩存過多少條查詢命令的結果
Qcache_lowmem_prunes 表示查詢緩存區已滿而從其中溢出和刪除的查詢結果的個數
Qcache_not_cached 自mysql進程啟動起,沒有被cache的只讀查詢數量(包括select,show,use,desc等)
Qcache_queries_in_cache 當前被cache的SQL數量
Qcache_total_blocks:在QC中的blocks數。一個query可能被多個blocks存儲,而這幾個blocks中的最後一個,未用滿的內存將會被釋放掉。例如一個QC結果要佔6KB內存,如果query_cache_min_res_unit是4KB,則最後將會生成3個blocks,第一個block用來存儲sql語句文本,這個不會被統計到query+cache_size裡,第二個block為4KB,第三個block為2KB(先allocate4KB,然後釋放多餘的2KB)。每個表,當第一個和它有關的SQL查詢被CACHE的時候,會使用一個block來存儲表信息。也就是說,block會被用在三處地方:表信息,SQL文本,查詢結果。
優化提示:如果Qcache_lowmem_prunes 值比較大,表示查詢緩存區大小設置太小,需要增大。如果Qcache_free_blocks 較多,表示內存碎片較多,需要清理,flush query cache
根據我看的 《High Performance MySQL》中所述,關於query_cache_min_res_unit大小的調優,書中給出了一個計算公式,可以供調優設置參考:
query_cache_min_res_unit = (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
還要注意一點的是,FLUSH QUERY CACHE 命令可以用來整理查詢緩存區的碎片,改善內存使用狀況,但不會清理查詢緩存區的內容,這個要和RESET QUERY CACHE相區別,不要混淆,後者才是清除查詢緩存區中
的所有的內容。
原理
QueryCache(下面簡稱QC)是根據SQL語句來cache的。一個SQL查詢如果以select開頭,那麼MySQL服務器將嘗試對其使用QC。每個Cache都是以SQL文本作為key來存的。在應用QC之前,SQL文本不會被作任何處理。也就是說,兩個SQL語句,只要相差哪怕是一個字符(例如大小寫不一樣;多一個空格等),那麼這兩個SQL將使用不同的一個CACHE。
不過SQL文本有可能會被客戶端做一些處理。例如在官方的命令行客戶端裡,在發送SQL給服務器之前,會做如下處理:
- 過濾所有註釋
- 去掉SQL文本前後的空格,TAB等字符。注意,是文本前面和後面的。中間的不會被去掉。
下面的三條SQL裡,因為SELECT大小寫的關係,最後一條和其他兩條在QC裡肯定是用的不一樣的存儲位置。而第一條和第二條,區別在於後者有個註釋,在不同客戶端,會有不一樣的結果。所以,保險起見,請儘量不要使用動態的註釋。在PHP的mysql擴展裡,SQL的註釋是不會被去掉的。也就是三條SQL會被存儲在三個不同的緩存裡,雖然它們的結果都是一樣的。
SELECT * FROM people where name='surfchen';
SELECT * FROM people where /*hey~*/name='surfchen';
SELECT * FROM people where name='surfchen';
目前只有select語句會被cache,其他類似show,use的語句則不會被cache。
因為QC是如此前端,如此簡單的一個緩存系統,所以如果一個表被更新,那麼和這個表相關的SQL的所有QC都會被失效。假設一個聯合查詢裡涉及到了表A和表B,如果表A或者表B的其中一個被更新(update或者delete),這個查詢的QC將會失效。
也就是說,如果一個表被頻繁更新,那麼就要考慮清楚究竟是否應該對相關的一些SQL進行QC了。一個被頻繁更新的表如果被應用了QC,可能會加重數據庫的負擔,而不是減輕負擔。我一般的做法是默認打開QC,而對一些涉及頻繁更新的表的SQL語句加上SQL_NO_CACHE關鍵詞來對其禁用CACHE。這樣可以儘可能避免不必要的內存操作,儘可能保持內存的連續性。
那些查詢很分散的SQL語句,也不應該使用QC。例如用來查詢用戶和密碼的語句——「select pass from user where name='surfchen'」。這樣的語句,在一個系統裡,很有可能只在一個用戶登陸的時候被使用。每個用戶的登陸所用到的查詢,都是不一樣的SQL文本,QC在這裡就幾乎不起作用了,因為緩存的數據幾乎是不會被用到的,它們只會在內存裡佔地方。
存儲塊
在本節裡「存儲塊」和「block」是同一個意思
QC緩存一個查詢結果的時候,一般情況下不是一次性地分配足夠多的內存來緩存結果的。而是在查詢結果獲得的過程中,逐塊存儲。當一個存儲塊被填滿之後,一個新的存儲塊將會被創建,並分配內存(allocate)。單個存儲塊的內存分配大小通過query_cache_min_res_unit參數控制,默認為4KB。最後一個存儲塊,如果不能被全部利用,那麼沒使用的內存將會被釋放。如果被緩存的結果很大,那麼會可能會導致分配內存操作太頻繁,系統系能也隨之下降;而如果被緩存的結果都很小,那麼可能會導致內存碎片過多,這些碎片如果太小,就很有可能不能再被分配使用。
除了查詢結果需要存儲塊之外,每個SQL文本也需要一個存儲塊,而涉及到的表也需要一個存儲塊(表的存儲塊是所有線程共享的,每個表只需要一個存儲塊)。存儲塊總數量=查詢結果數量*2+涉及的數據庫表數量。也就是說,第一個緩存生成的時候,至少需要三個存儲塊:表信息存儲塊,SQL文本存儲塊,查詢結果存儲塊。而第二個查詢如果用的是同一個表,那麼最少只需要兩個存儲塊:SQL文本存儲塊,查詢結果存儲塊。
通過觀察Qcache_queries_in_cache和Qcache_total_blocks可以知道平均每個緩存結果占用的存儲塊。它們的比例如果接近1:2,則說明當前的query_cache_min_res_unit參數已經足夠大了。如果Qcache_total_blocks比Qcache_queries_in_cache多很多,則需要增加query_cache_min_res_unit的大小。
Qcache_queries_in_cache * query_cache_min_res_unit(sql文本和表信息所在的block佔用的內存很小,可以忽略)如果遠遠大於query_cache_size-Qcache_free_memory,那麼可以嘗試減小query_cache_min_res_unit的值。
調整大小
如果Qcache_lowmem_prunes增長迅速,意味著很多緩存因為內存不夠而被釋放,而不是因為相關表被更新。嘗試加大query_cache_size,儘量使Qcache_lowmem_prunes零增長。
啟動參數
show variables like 'query_cache%' 可以看到這些信息。
query_cache_limit:如果單個查詢結果大於這個值,則不Cache
query_cache_size:分配給QC的內存。如果設為0,則相當于禁用QC。要注意QC必須使用大約40KB來存儲它的結構,如果設定小於40KB,則相當于禁用QC。QC存儲的最小單位是1024 byte,所以如果你設定了一個不是1024的倍數的值,這個值會被四捨五入到最接近當前值的等於1024的倍數的值。
query_cache_type:
0 完全禁止QC,不受SQL語句控制(另外可能要注意的是,即使這裡禁用,上面一個參數所設定的內存大小還是會被分配);
1啟用QC,可以在SQL語句使用SQL_NO_CACHE禁用;
2可以在SQL語句使用SQL_CACHE啟用。
query_cache_min_res_unit:每次給QC結果分配內存的大小
狀態
show status like 'Qcache%' 可以看到這些信息。
Qcache_free_blocks:當一個表被更新之後,和它相關的cache blocks將被free。但是這個block依然可能存在隊列中,除非是在隊列的尾部。這些blocks將會被統計到這個值來。可以用FLUSH QUERY CACHE語句來清空free blocks。
其他幾個狀態變量的意義:
Qcache_free_memory 表示查詢緩存區現在還有多少的可用內存,如果很小,考慮增加query_cache_size
Qcache_hits 表示查詢緩存區的命中個數,也就是直接從查詢緩存區作出響應處理的查詢個數
Qcache_inserts 表示查詢緩存區此前總過緩存過多少條查詢命令的結果
Qcache_lowmem_prunes 表示查詢緩存區已滿而從其中溢出和刪除的查詢結果的個數
Qcache_not_cached 自mysql進程啟動起,沒有被cache的只讀查詢數量(包括select,show,use,desc等)
Qcache_queries_in_cache 當前被cache的SQL數量
Qcache_total_blocks:在QC中的blocks數。一個query可能被多個blocks存儲,而這幾個blocks中的最後一個,未用滿的內存將會被釋放掉。例如一個QC結果要佔6KB內存,如果query_cache_min_res_unit是4KB,則最後將會生成3個blocks,第一個block用來存儲sql語句文本,這個不會被統計到query+cache_size裡,第二個block為4KB,第三個block為2KB(先allocate4KB,然後釋放多餘的2KB)。每個表,當第一個和它有關的SQL查詢被CACHE的時候,會使用一個block來存儲表信息。也就是說,block會被用在三處地方:表信息,SQL文本,查詢結果。
優化提示:如果Qcache_lowmem_prunes 值比較大,表示查詢緩存區大小設置太小,需要增大。如果Qcache_free_blocks 較多,表示內存碎片較多,需要清理,flush query cache
根據我看的 《High Performance MySQL》中所述,關於query_cache_min_res_unit大小的調優,書中給出了一個計算公式,可以供調優設置參考:
query_cache_min_res_unit = (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
還要注意一點的是,FLUSH QUERY CACHE 命令可以用來整理查詢緩存區的碎片,改善內存使用狀況,但不會清理查詢緩存區的內容,這個要和RESET QUERY CACHE相區別,不要混淆,後者才是清除查詢緩存區中
的所有的內容。
2012-06-04 01:29
[轉載] 自適應網頁設計
轉載自:自适应网页设计(Responsive Web Design) 阮一峰
随着 3G 的普及,越来越多的人使用手机上网。
移动设备正超过桌面设备,成为访问互联网的最常见终端。于是,网页设计师不得不面对一个难题:如何才能在不同大小的设备上呈现同样的网页?

手机的屏幕比较小,宽度通常在 600 像素以下,PC 的屏幕宽度,一般都在 1000 像素以上(目前主流宽度是1366×768),有的还达到了 2000 像素。同样的内容,要在大小迥异的屏幕上,都呈现出满意的效果,并不是一件容易的事。
很多网站的解决方法,是为不同的设备提供不同的网页,比如专门提供一个 mobile 版本,或者 iPhone / iPad 版本。这样做固然保证了效果,但是比较麻烦,同时要维护好几个版本,而且如果一个网站有多个 portal(入口),会大大增加架构设计的复杂度。
于是,很早就有人设想,能不能"一次设计,普遍适用",让同一张网页自动适应不同大小的屏幕,根据屏幕宽度,自动调整布局(layout)?

一、"自适应网页设计"的概念
2010 年,Ethan Marcotte 提出了"自适应网页设计"(Responsive Web Design)这个名词,指可以自动识别屏幕宽度、并做出相应调整的网页设计。
他制作了一个范例,里面是《福尔摩斯历险记》六个主人公的头像。如果屏幕宽度大于 1300 像素,则 6 张图片并排在一行。

如果屏幕宽度在 600 像素到 1300 像素之间,则 6 张图片分成两行。

如果屏幕宽度在 400 像素到 600 像素之间,则导航栏移到网页头部。

如果屏幕宽度在 400 像素以下,则 6 张图片分成三行。

mediaqueri.es 上面有更多这样的例子。
这里还有一个测试小工具,可以在一张网页上,同时显示不同分辨率屏幕的测试效果,我推荐安装。
二、允许网页宽度自动调整
"自适应网页设计"到底是怎么做到的?其实并不难。
首先,在网页代码的头部,加入一行 viewport元标签。
viewport 是网页默认的宽度和高度,上面这行代码的意思是,网页宽度默认等于屏幕宽度(width=device-width),原始缩放比例(initial-scale=1)为 1.0,即网页初始大小占屏幕面积的 100%。
所有主流浏览器都支持这个设置,包括 IE9。对于那些老式浏览器(主要是 IE6、7、8),需要使用 css3-mediaqueries.js。
三、不使用绝对宽度
由于网页会根据屏幕宽度调整布局,所以不能使用绝对宽度的布局,也不能使用具有绝对宽度的元素。这一条非常重要。
具体说,CSS代码不能指定像素宽度:
只能指定百分比宽度:
或者
四、相对大小的字体
字体也不能使用绝对大小(px),而只能使用相对大小(em)。
上面的代码指定,字体大小是页面默认大小的 100%,即 16 像素。
然后,h1 的大小是默认大小的 1.5 倍,即 24 像素(24/16=1.5)。
small 元素的大小是默认大小的 0.875 倍,即 14 像素(14/16=0.875)。
五、流动布局(fluid grid)
"流动布局"的含义是,各个区块的位置都是浮动的,不是固定不变的。
float 的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向 overflow(溢出),避免了水平滚动条的出现。
另外,绝对定位(position: absolute)的使用,也要非常小心。
六、选择加载CSS
"自适应网页设计"的核心,就是 CSS3 引入的 Media Query 模块。
它的意思就是,自动探测屏幕宽度,然后加载相应的 CSS 文件。
上面的代码意思是,如果屏幕宽度小于 400 像素(max-device-width: 400px),就加载 tinyScreen.css 文件。
如果屏幕宽度在 400 像素到 600 像素之间,则加载 smallScreen.css 文件。
除了用 html 标签加载 CSS 文件,还可以在现有 CSS 文件中加载。
七、CSS 的 @media 规则
同一个 CSS 文件中,也可以根据不同的屏幕分辨率,选择应用不同的 CSS 规则。
上面的代码意思是,如果屏幕宽度小于 400 像素,则 column 块取消浮动(float:none)、宽度自动调节(width:auto),sidebar 块不显示(display:none)。
八、图片的自适应(fluid image)
除了布局和文本,"自适应网页设计"还必须实现图片的自动缩放。
这只要一行 CSS 代码:
这行代码对于大多数嵌入网页的视频也有效,所以可以写成:
老版本的 IE 不支持 max-width,所以只好写成:
此外,windows 平台缩放图片时,可能出现图像失真现象。这时,可以尝试使用 IE 的专有命令:
或者,Ethan Marcotte 的 imgSizer.js。
不过,有条件的话,最好还是根据不同大小的屏幕,加载不同分辨率的图片。有很多方法可以做到这一条,服务器端和客户端都可以实现。
(完)
随着 3G 的普及,越来越多的人使用手机上网。
移动设备正超过桌面设备,成为访问互联网的最常见终端。于是,网页设计师不得不面对一个难题:如何才能在不同大小的设备上呈现同样的网页?

手机的屏幕比较小,宽度通常在 600 像素以下,PC 的屏幕宽度,一般都在 1000 像素以上(目前主流宽度是1366×768),有的还达到了 2000 像素。同样的内容,要在大小迥异的屏幕上,都呈现出满意的效果,并不是一件容易的事。
很多网站的解决方法,是为不同的设备提供不同的网页,比如专门提供一个 mobile 版本,或者 iPhone / iPad 版本。这样做固然保证了效果,但是比较麻烦,同时要维护好几个版本,而且如果一个网站有多个 portal(入口),会大大增加架构设计的复杂度。
于是,很早就有人设想,能不能"一次设计,普遍适用",让同一张网页自动适应不同大小的屏幕,根据屏幕宽度,自动调整布局(layout)?

一、"自适应网页设计"的概念
2010 年,Ethan Marcotte 提出了"自适应网页设计"(Responsive Web Design)这个名词,指可以自动识别屏幕宽度、并做出相应调整的网页设计。
他制作了一个范例,里面是《福尔摩斯历险记》六个主人公的头像。如果屏幕宽度大于 1300 像素,则 6 张图片并排在一行。

如果屏幕宽度在 600 像素到 1300 像素之间,则 6 张图片分成两行。

如果屏幕宽度在 400 像素到 600 像素之间,则导航栏移到网页头部。

如果屏幕宽度在 400 像素以下,则 6 张图片分成三行。

mediaqueri.es 上面有更多这样的例子。
这里还有一个测试小工具,可以在一张网页上,同时显示不同分辨率屏幕的测试效果,我推荐安装。
二、允许网页宽度自动调整
"自适应网页设计"到底是怎么做到的?其实并不难。
首先,在网页代码的头部,加入一行 viewport元标签。
<meta name="viewport" content="width=device-width, initial-scale=1" />
viewport 是网页默认的宽度和高度,上面这行代码的意思是,网页宽度默认等于屏幕宽度(width=device-width),原始缩放比例(initial-scale=1)为 1.0,即网页初始大小占屏幕面积的 100%。
所有主流浏览器都支持这个设置,包括 IE9。对于那些老式浏览器(主要是 IE6、7、8),需要使用 css3-mediaqueries.js。
<!--[if lt IE 9]>
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->
三、不使用绝对宽度
由于网页会根据屏幕宽度调整布局,所以不能使用绝对宽度的布局,也不能使用具有绝对宽度的元素。这一条非常重要。
具体说,CSS代码不能指定像素宽度:
width: xxx px;
只能指定百分比宽度:
width: xx%;
或者
width: auto;
四、相对大小的字体
字体也不能使用绝对大小(px),而只能使用相对大小(em)。
body {
font: normal 100% Helvetica, Arial, sans-serif;
}
上面的代码指定,字体大小是页面默认大小的 100%,即 16 像素。
h1 {
font-size: 1.5em;
}
然后,h1 的大小是默认大小的 1.5 倍,即 24 像素(24/16=1.5)。
small {
font-size: 0.875em;
}
small 元素的大小是默认大小的 0.875 倍,即 14 像素(14/16=0.875)。
五、流动布局(fluid grid)
"流动布局"的含义是,各个区块的位置都是浮动的,不是固定不变的。
.main {
float: right;
width: 70%;
}
.leftBar {
float: left;
width: 25%;
}
float 的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向 overflow(溢出),避免了水平滚动条的出现。
另外,绝对定位(position: absolute)的使用,也要非常小心。
六、选择加载CSS
"自适应网页设计"的核心,就是 CSS3 引入的 Media Query 模块。
它的意思就是,自动探测屏幕宽度,然后加载相应的 CSS 文件。
<link rel="stylesheet" type="text/css" media="screen and (max-device-width: 400px)" href="tinyScreen.css" />
上面的代码意思是,如果屏幕宽度小于 400 像素(max-device-width: 400px),就加载 tinyScreen.css 文件。
<link rel="stylesheet" type="text/css" media="screen and (min-width: 400px) and (max-device-width: 600px)" href="smallScreen.css" />
如果屏幕宽度在 400 像素到 600 像素之间,则加载 smallScreen.css 文件。
除了用 html 标签加载 CSS 文件,还可以在现有 CSS 文件中加载。
@import url("tinyScreen.css") screen and (max-device-width: 400px);
七、CSS 的 @media 规则
同一个 CSS 文件中,也可以根据不同的屏幕分辨率,选择应用不同的 CSS 规则。
@media screen and (max-device-width: 400px) {
.column {
float: none;
width: auto;
}
#sidebar {
display: none;
}
}
上面的代码意思是,如果屏幕宽度小于 400 像素,则 column 块取消浮动(float:none)、宽度自动调节(width:auto),sidebar 块不显示(display:none)。
八、图片的自适应(fluid image)
除了布局和文本,"自适应网页设计"还必须实现图片的自动缩放。
这只要一行 CSS 代码:
img { max-width: 100%; }
这行代码对于大多数嵌入网页的视频也有效,所以可以写成:
img, object { max-width: 100%; }
老版本的 IE 不支持 max-width,所以只好写成:
img { width: 100%; }
此外,windows 平台缩放图片时,可能出现图像失真现象。这时,可以尝试使用 IE 的专有命令:
img { -ms-interpolation-mode: bicubic; }
或者,Ethan Marcotte 的 imgSizer.js。
addLoadEvent(function() {
var imgs = document.getElementById("content").getElementsByTagName("img");
imgSizer.collate(imgs);
});
不过,有条件的话,最好还是根据不同大小的屏幕,加载不同分辨率的图片。有很多方法可以做到这一条,服务器端和客户端都可以实现。
(完)
2012-05-05 01:53
[轉載] 让PHP更快的提供文件下载
作者:Laruence
本文地址:http://www.laruence.com/2012/05/02/2613.html
一般来说, 我们可以通过直接让URL指向一个位于Document Root下面的文件, 来引导用户下载文件.
但是, 这样做, 就没办法做一些统计, 权限检查, 等等的工作. 于是, 很多时候, 我们采用让PHP来做转发, 为用户提供文件下载.
但是这个有一个问题, 就是如果文件是中文名的话, 有的用户可能下载后的文件名是乱码.
于是, 我们做一下修改(参考: :
恩, 现在看起来好多了, 不过还有一个问题, 那就是readfile, 虽然PHP的readfile尝试实现的尽量高效, 不占用PHP本身的内存, 但是实际上它还是需要采用MMAP(如果支持), 或者是一个固定的buffer去循环读取文件, 直接输出.
输出的时候, 如果是Apache + PHP mod, 那么还需要发送到Apache的输出缓冲区. 最后才发送给用户. 而对于Nginx + fpm如果他们分开部署的话, 那还会带来额外的网络IO.
那么, 能不能不经过PHP这层, 直接让Webserver直接把文件发送给用户呢?
今天, 我看到了一个有意思的文章: How I PHP: X-SendFile.
我们可以使用Apache的module mod_xsendfile, 让Apache直接发送这个文件给用户:
X-Sendfile头将被Apache处理, 并且把响应的文件直接发送给Client.
Lighttpd和Nginx也有类似的模块, 大家有兴趣的可以去找找看
mod-xsendfile 在 Ubuntu 的安裝方法:
sudo apt-get install libapache2-mod-xsendfile
本文地址:http://www.laruence.com/2012/05/02/2613.html
一般来说, 我们可以通过直接让URL指向一个位于Document Root下面的文件, 来引导用户下载文件.
但是, 这样做, 就没办法做一些统计, 权限检查, 等等的工作. 于是, 很多时候, 我们采用让PHP来做转发, 为用户提供文件下载.
<?php
$file = "/tmp/dummy.tar.gz";
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="'
. basename($file) . '"');
header("Content-Length: ". filesize($file));
readfile($file);
但是这个有一个问题, 就是如果文件是中文名的话, 有的用户可能下载后的文件名是乱码.
于是, 我们做一下修改(参考: :
<?php
$file = "/tmp/中文名.tar.gz";
$filename = basename($file);
header("Content-type: application/octet-stream");
//处理中文文件名
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="'
. $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''"
. $filename . '"');
} else {
header('Content-Disposition: attachment; filename="'
. $filename . '"');
}
header('Content-Disposition: attachment; filename="'
. $filename . '"');
header("Content-Length: ". filesize($file));
readfile($file);
恩, 现在看起来好多了, 不过还有一个问题, 那就是readfile, 虽然PHP的readfile尝试实现的尽量高效, 不占用PHP本身的内存, 但是实际上它还是需要采用MMAP(如果支持), 或者是一个固定的buffer去循环读取文件, 直接输出.
输出的时候, 如果是Apache + PHP mod, 那么还需要发送到Apache的输出缓冲区. 最后才发送给用户. 而对于Nginx + fpm如果他们分开部署的话, 那还会带来额外的网络IO.
那么, 能不能不经过PHP这层, 直接让Webserver直接把文件发送给用户呢?
今天, 我看到了一个有意思的文章: How I PHP: X-SendFile.
我们可以使用Apache的module mod_xsendfile, 让Apache直接发送这个文件给用户:
<?php
$file = "/tmp/中文名.tar.gz";
$filename = basename($file);
header("Content-type: application/octet-stream");
//处理中文文件名
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="'
. $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''"
. $filename . '"');
} else {
header('Content-Disposition: attachment; filename="'
. $filename . '"');
}
header('Content-Disposition: attachment; filename="'
. basename($file) . '"');
//让Xsendfile发送文件
header("X-Sendfile: $file");
X-Sendfile头将被Apache处理, 并且把响应的文件直接发送给Client.
Lighttpd和Nginx也有类似的模块, 大家有兴趣的可以去找找看
mod-xsendfile 在 Ubuntu 的安裝方法:
sudo apt-get install libapache2-mod-xsendfile
訂閱:
文章 (Atom)