分頁優化+表鎖和庫存優化+數據庫的備份和導入
一.分頁優化技術
代碼參看: php/classic.php
把50331651記錄進行分頁,每頁顯示2條記錄,於是我們用傳統php編碼方式,編寫分頁代碼如下:
上傳到/var/www/html下進行測試,結果如下:
如果訪問第1頁和第4頁,返回語句:
使用explain執行計劃查詢比較靠前的頁數,發覺速度很快因為可以使用上索引:
如果訪問第4100000頁,返回語句:
使用explain分析結果如下:
發覺這時如果分頁到了中間的頁數,這時我們既需要排序又要分頁檢索數據的時候,就會出現Using filesort的選項,這選項的出現導致分頁在中間頁面的時候使用不上索引,因此出現全表掃描的過程,所以這個語句的查詢效率就大大的降低了,我們在工作中如果遇到ALL的全表掃描我們就必須優化這條語句,優化語句必須遵循一個理念:讓慢語句使用上索引,使得ALL消失,但功能不能發生改變,這時我們應該如何優化,這時我們可以把分頁語句調整如下優化:
分頁傳統的公式如下: order by id asc + limit 偏移量,長度
分頁優化的公式如下: where id>偏移量 + order by id asc+ limit 長度
查詢第4100000頁得到的結果如下:
經過優化後的算法,使用explain進行分析,可以得到如下結果:
發覺Using filesort被修改為Using Where因此分頁優化得到提升,所以我們將來在開發的過程中如果需要分頁的數據達到千萬的級別時,我們就需要使用以下優化分頁公式:
分頁優化的公式如下: where id>偏移量 + order by id asc + limit 長度
但是什麽還要知道當前的算法查詢的記錄是否準確:
二.MyISAM的表鎖和庫存優化
在現實開發當中,電子商務網站會有一個搶購的活動,如果有一個id=4並且庫存量只有1的商品給用戶進行搶購,那麽這時有可能當所有的用戶同時進行搶購的時候就會發生一種叫做並發的行為,這時會導致很多的用戶同時搶購一個商品成功,為了防止這個情況,確保只能有一個人搶購成功,我們就需要使用Myisam中的表鎖,建立表結構和數據如下:
代碼參考: code/StoreGoods.sql
插入相關的測試數據如下:
於是我們傳統編寫的搶購代碼如下所示:
代碼參考: code/goods.php
問題來了:如果這時發生了並非的行為,那麽其實有可能有大部分的用戶,可以進入搶購的update代碼當中,那麽這時有可能發生多人搶購成功,並且庫存變為負數的情況,這時如果需要避免這個結果的產生,我們需要加上表鎖,把代碼修改如下:
把代碼上傳到/var/www/html下,測試如下:
如果成功搶購,那麽繼續刷新就會提示以下結果:
三.數據的列(字段)數據類型的正確選擇
1.數值數據類型
在表的建立中數值數據選取原則
人的年齡可以選擇:
人一般很難超過139歲,如果你選擇int類型來存儲就是占用4個字節,如果1000條記錄的大小就是4*1000=4000字節,如果你選擇tinyint就只有1個字節,1000條記錄只有1000字節的大小比原來int節約了4倍的存儲空間,因此人的年齡選擇tinyint
烏龜的年齡可以選擇:
烏龜最短年齡也可以超過500年,所以tinyint無法存儲烏龜的年齡,因此我們可以選擇smallint來存儲,這時其實理論上可以保存萬年神龜的年齡
主鍵可以選擇:
假設是一個小學生的博客系統,那麽其實主鍵id選擇tinyint即可,因為小學生很難寫超過100篇文章,如果是技術人員的技術博客都屬於一些個人的筆記,所以我們選擇smallint作為主鍵的id就夠了,因為一般人很難寫超過2000篇文章,就算是金庸寫的博客也很難超過,假設你個人開發了一個商城網站,專賣手機產品,那麽應該選擇smallint,如果是一個間小公司的電子商務(中山壹加壹連鎖超市)可以選擇MEDIUMINT,如果你開發是京東商城那麽請選擇int做主鍵
金錢可以選擇:
如果遇到存儲貨幣或者金錢類型的字段,想都不用想使用DECIMAL(6,2)
2.時間和日期數據類型的大小
如果將來還需要用到時間的字段,建議直接使用timestamp,如果你使用int雖然也是4個字節,然而int存儲的是Unix時間戳,那麽就意味你有一個php代碼的轉化過程,非常的麻煩,而timestamp依然是4個字節但這個字段不需要你做任何的轉化,只需要使用一個mysql中的數據庫函數叫now()就可以完成.
對mysql的timestamp字段插入數據,會發覺存在2個問題:
代碼參考: code/timestamp.sql
這時以上的時間有可能不對的,因為在linux當中我們安裝時候,安裝一般是系統時間,在linux修改時間需要同時修改系統時間和硬件時間,修改Linux的時間,需要使用以下兩個命令:
假設我們需要把Linux的時間修改為2017-08-22 08:13:52秒
1)使用date命令
date在沒有任何的選項的時候,是用於查詢系統時間,發覺這個時間不是我們想要的時間,因此我們可以使用date -s來進行時間的設置,-s是修改系統時間的選項
CST的意思是中國沿海時間的時區,一般在linux當中我們使用中國時區都使用CST
2)使用clock --show命令顯示linux硬件時間
以上問題,可以會在開發當中獲取的時間產生bug,因此我們需要同步系統和硬件時間為一致的時間,使用clock --systohc同步,--systohc就是把系統時間和硬件時間進行同步的選項:
執行命令,如下 :
這時再次對比系統時間和硬件時間如下:
如果我們把字段的時間類型修改為timestamp也同時調整了Linux系統時間和硬件時間,那麽這時我們如果使用php進行該字段的插入,那麽我們在php中應該怎麽做呢?
代碼參考:php/news.php
上傳到/var/www/html下,測試效果如下:
上述代碼必須設置 時區,now()不要進行預處理
3.字符數據類型
如果你做博客或者新聞發布信息的系統,標題字段選擇varchar,內容選擇text,這個進本上是一種常見的應用,BLOB的數據一般可能會產生一些亂碼的情況所以建議不要使用在中文內容當中.
在php開發我們經常需要做時間字段的優化還有日誌記錄系統當中Ip字段的優化.
255.255.255.255 = 15字節(varhcar(15))
192.168.1.1 = 15字節(varchar(15)) = 15 * 1000 = 15000 字節
ip字段其實可以優化成為4個字節進行存儲,那麽就是4*1000=4000字節
所以如果使用varchar(15)或者char(15)來存儲ip的時候,你就有優化ip字段存儲的必要性了,如果希望把ip地址優化成為4個字節的大小,我們應該選擇int數據類型對ip進行存儲,但是如果把ip轉化成為int呢,那麽這時我們要學習兩個函數:
4.IP數據存儲
①為ip字段選擇的數據類型(ip2long)
根據轉換的結果可以使用以下在設計表時來選擇Ip字段的數據類型選擇Int
代碼詳細請參考:code/ip.sql
編寫php代碼如下:
代碼詳細請參考:php/ip2long.php
在默認情況下,ip2long函數會把ip轉化成有簽名整型,因此我們在php當中會獲取到負數的情況,如下所示:
ip如果為負數的轉化是屬於失敗的轉化,那我們就需要把ip轉成無簽名整型獲取正整數
我們就需要使用sprintf函數,函數描述如下:
編寫修改代碼如下:
上傳到/var/www/html下測試結果如下所示:
獲取了ip轉化無符號的結果,這時如果你希望直到當前的整數是否轉化成功,那麽就需要把整型轉化為ip地址來進行驗證,使用long2ip
②long2ip函數(把整型轉換為ip地址)
php代碼參看:php/long2ip.php(該代碼需要上傳到linux的/var/www/html下測試)
執行結果如下:
⑥ip優化的綜合實例,把ip為192.168.84.88插入的數據表ipAddress當中,並且把插入的數在該表當中讀取出來,代碼如下所示:
php代碼參看:php/caseIp.php(該代碼需要上傳到linux的/var/www/html下測試)
第1步:先完成pdo對ip地址轉化為整型數據插入工作,代碼如下:
執行結果如下所示:
第2步:把ip地址從數據庫讀取出來並且把整型轉化成為ip地址,代碼如下:
執行結果如下所示:
四.物理垂直分表和數據庫三範式
1.物理分表的概念
物理分表又稱為手工分表,故名思議物理分表的工作是靠自己手動進行完成的,它的目的是把數據進行拆分管理.在物理分表中主要的分表思想有垂直分表和水平分表兩種,這種分表是一種數據庫的優化思想,所以需要引入數據庫範式的概念進行支持.
2.數據庫的三範式
在現實開發當中我們一般是針對需求來滿足需求,於是如果有以下需求我們如果你看到眼前而不考慮長遠,我們可以把需求分析和設計做出以下決定,如下圖所示:
這樣的表設計自然能夠滿足需求1,然而它有利於長遠的發展嗎?如果你想知道是否適合長遠的發展最好就是繼續提出一個新的需求看看是否容易拓展或者直接滿足,繼續提出需求如下:
要解決需求2如果你只看到眼前,那麽你可能會做出以下解決的,就是增加一個字段來添加歷史學科的成績:
這時又一個新的需求提出,這時問題就再次出現了,如下圖所示:
因為不斷字段去滿足需求就會導致字段不斷增加,造成數據的冗余,如果你希望解決以上這些問題的出現就需要使用物理垂直分表進行解決,然而垂直分表一般發生開發者開發項目和設計階段出現較多,所以與垂直分表相關的又一個叫做數據庫範式的理論進行支持,範式理論如下:
數據庫第1範式:把數據合理的進行拆分讓他們的共同點形成一個整體,並且形成後沒有繼續拆分必要 性,那麽就證明該整體一個合理的整體成為符合數據庫第1範式(1NF),如上述問題,解決方案符合第1範式如下:
數據庫第2範式:由於數據表又可能會存在相同並且重復的記錄可能,我們需要解決整個問題就是加入主鍵,如上述問題,符合數據庫第2範式如下:
數據庫第3範式:由於我們把數據分別成為了一個整體,那麽就需要把整體進行聯合,我們把擁有主鍵的整體部分成為主表,把關聯主表的整體部分成為附屬表,那麽關聯他們的標識就是數據庫的第3方式的核心也就是外鍵,所以可以認為認為數據庫第3範式的核心就是關聯性,如上述問題,符合第3範式設計如下:
數據庫三範式一般人也有把理解為物理垂直分表的標準,只要符合數據庫三範式所有的規則,那麽都是垂直分表的過程而整個過程一般是我們手動完成,因此物理垂直分表也稱為手工垂直分表。
3.垂直分表的概念
分表時候,每一張表都是結構獨立的,表與表之間通過外鍵的形式把數據鏈接在一起進行查詢。只要符合數據庫三範式的設計都屬於垂直分表的思想.
代碼詳細請參考:code/students.sql
1)業務需求
2)根據三範式設計表的結構
首先滿足1NF和2NF,設計表結構如下:
然而滿足3NF,設置關聯和外鍵,設計表結構如下:
3)為表插入對應的數據
4)使用連表查詢獲得數據
如果希望查找學號為s123的人我們就需要把學號字段進行主表和附屬表的關聯,原理如下:
編寫以下查詢語句連表查詢操作:
執行結果如下所示:
5)使用explain執行計劃查看索引使用情況
執行結果發覺,垂直分表可以使用到索引,因為符合數據庫3範式的設計就一定能夠使用上索引。
4.數據庫的逆範式
逆範式化指的是通過增加冗余或重復的數據來提高數據庫的讀性能.逆範式通俗的理解其實就是通過違反數據庫三範式的定義達到某種合理的設計的一種應用.其中水平分表就是一種逆範式的應用
五.物理水平分表實驗(37遊戲報團實驗)
在早期的網頁遊戲當中有一種叫組團攻城的活動,每一個遊戲的玩家可以在某一遊戲中進行報名組建其他的玩家一起玩遊戲,有時候可能不是只有一個遊戲開展攻城的活動,而是多個遊戲開展攻城的活動,這時為了更好地維護遊戲,這時我們就可以使用到水平分表,其原理圖如下所示:
我們可以把案例,按照如下步驟,部署到Linux當中
第1步:把代碼通過ftp上傳/var/www/html
第2步:建立一個名為37games的數據如下:
第3步:切換到/var/www/html下執行數據庫表的安裝,執行install_db.php文件
在執行腳本之前先確認數據庫鏈接的用戶名和密碼以及數據庫名稱,打開db.php文件,內容如下,可以根據自己喜歡修改:
確認無誤後,就在/var/www/html下使用php命令執行install_db.php文件,如下圖所示:
查看數據庫37games當中發覺有2張表分別:tlcs_users和cycs_users
2張表的字段都是一樣的,只是表名不一樣
第4步:在瀏覽中訪問linux中ip地址,出現以下界面,代表案例部署成功
第5步:分析案例的目錄和文件如下:
js目錄 : 放置javascript庫文件的,jq.js是一個jquery框架
smarty目錄:用於放置smarty類庫的目錄
templates:是html模板的目錄
templates_c:是smarty編譯模板後的目錄
common.php : 一個數據庫鏈接,smarty實例化,遊戲產品列表數據引導的核心文件
count.php : 統計報團攻城的人數信息的文件
db.php : 數據庫的鏈接配置文件
games.php:遊戲產品的數組文件
index.php:網站報團首頁文件
install_db.php : linux安裝時執行的腳本文件
register.php: 報團插入數據庫的php文件
第6步:分析首頁可知,頁面點機報團按鈕後,會把以下表單信息傳至register.php頁面
第7步:在register.php代碼當中編寫如下代碼,獲取信息並插入數據庫當中
如果用戶報的屠龍傳說遊戲,那麽就會在tlcs_users當中添加數據:
如果用戶報的赤月傳說遊戲,那麽就會在cycs_users當中添加數據:
第8步:實現水平分表的統計,我們需要在count.php文件中編寫代碼如下:
測試結果如下:
這時問題來了,如果這時老板提出一個新的需求,把一個叫做傳奇霸業的遊戲(mir)也進行攻城活動,那麽我們就需要修改代碼如下:
第9步:在games.php文件中加入對應遊戲代碼和遊戲名稱
在首頁就會顯示多一個遊戲
同時我們還需要建立一張名為mir_users的水平分表,來存儲傳奇霸業的用戶信息,使用命令如下:
第10步:修改count.php的統計報表數據,代碼如下所示:
第11步:修改smarty的模板文件templates/count.html,代碼如下:
測試如下:
物理水平分表自定義和拆分大數據的能力雖然非常的強,然而它有一個缺點就是由於我們造成數據的冗余分布,導致數據的統計變得麻煩,只能不同不斷修改代碼來滿足需求,因此物理水平分表有優點也有缺點,因此水平分表的物理優化我們要付出一定的php邏輯編寫的時間代價,有沒有更好取代這種分表的,讓我們的統計變得簡單的方法呢?在mysql5.1之後,其官方開發者提供了一種叫邏輯水平分表的手段
六. MySql的邏輯水平分表
物理水平分表的好處在於自定義能力很強,但物理分表不利於數據的統計,因此MySql為了解決物理水平分表的缺陷提出了邏輯水平分表的概念,邏輯水平分表是MySql自帶功能,它的好處就是分表的過程是系統自動完成的,而我們寫的代碼不會像物理水平分表那樣疲於應對需求,邏輯水平分表主要有兩種。一種稱為Range分表,一種稱為List分表。
業界有些說法也叫Range分區和List分區。
1.使用Range形式的邏輯分表(切蛋糕)
RANGE分表其實好比生活中的切蛋糕中的概念,是基於範圍的數據切分,RANGE主要是基於整數的分表。一般按主鍵進行範圍分表,假設當前主鍵的最大值為5000,有時希望用一個範圍對數據進行拆分,比如把數據拆分為5個等份:
1-1000:是5000的第1個範圍,把該範圍的數據作為一張表
1001-2000:是5000的第2個範圍,把該範圍的數據作為一張表
2001-3000:是5000的第3個範圍,把該範圍的數據作為一張表
3001-4000:是5000的第3個範圍,把該範圍的數據作為一張表
4001-5000:是5000的第4個範圍,把該範圍的數據作為一張表
如果有以上的需求,那麽就可以使用MySql的range分表技術,其原理圖如下:
如果把5000條記錄看成蛋糕,那麽拆分的數據就是把蛋糕切成了5等份
①創建表的時候指定range分表的範圍
代碼詳細請參考:code/range.sql
語法規則:
partition by range (字段)(
partition 分表名稱 values less than (範圍)
)
發覺系統自動幫我們產生2個分表的區域
假設我希望有一張一些數據處於cake1000這張分表當中,那麽我應該如何插入?
分析可知:由於這個分表是從id的範圍來分,如果id的範圍是1-999那麽就會處於cake1000當中,所以我們插入數據如下:
執行結果如下:
發覺你插入數據其實可以有效控制範圍
假設我們插入的數據的id=2001,那麽會出現怎麽樣的情況呢?
然而我們的數據是分布在不同的分表當中,如果我們使用select count(*) from cakes能整成統計出記錄總數嗎?
這個答案是肯定的,因為邏輯分表的功能和算法是開發者幫您實現的
假設我們刪除了cake2000這個分表,那麽統計會改變嗎?也就是說數據會丟失嗎?
②刪除range分表
代碼詳細請參考:code/del_range.sql
語法規則:alter table 表名 drop partition 分表名稱;
如果我們刪除cake2000這個分表,那麽我們查詢id=1000是可以查出來的
如果執行分表刪除,如下;
執行結果如下:
如果使用select count(*)進行統計查詢會發覺數據丟失了
註意:分表刪除數據會丟失
③添加range分表
代碼詳細請參考:code/add_range.sql
如果我們添加一個數據超過了分表範圍,那麽就會報錯
語法規則:
alter table 表名 add partition (
partition 分表名稱 values less than (範圍)
)
執行的結果如下所示:
2.使用List形式的邏輯分表(分季度)
List分表其實簡單地理解為把一年按季度的進行分表的應用即可,因為實際開發當中List分表主要用於年度季度報表的應用比較多,List分表也是按範圍的分表技術。假設當前要把1年的數據進行季度拆分,那麽1年可以分為4個季度:
3,4,5月分為春季
6,7,8月分為夏季
9,10,11月分為秋季
12,1,2月份分為冬季
這時range分表其實無法達到這個功能,如果需要滿足當前這個需求則可以選擇List分表技術,其原理圖如下:
list分表一般用於財務報表的統計系統當中
①創建表的時候指定list分表的範圍
代碼詳細請參考:code/list.sql
語法規則:
partition by list (條件語句)(
partition 分表名稱 values in (範圍)
)
執行結果如下:
②嘗試插入數據到夏季的分表當中
執行結果如下:
④刪除list分表
代碼詳細請參考:code/del_list.sql
語法規則:alter table 表名 drop partition 分表名稱;
執行結果會發覺,數據跟range一樣會丟失:
註意:分表刪除數據會丟失,list分表最好不要刪除分表,因為list的分表應用多數用於財務系統,財務系統的數據一般不建議刪除,所以最好不要刪除list分表種的分表
⑤添加list分表
代碼詳細請參考:code/add_list.sql
語法規則:
alter table 表名 add partition (
partition 分表名稱 values in (範圍)
)
執行結果如下:
list分表有1個特點,就是在mysql5.1.73版本,建議id字段設置為普通索引,因為唯一性索引和主鍵在list分表當中不起作用,且無法添加
這時我們如果建立的是普通索引,那麽就不會出錯了
執行結果如下:
七.數據庫的備份和導入
①備份命令mysqldump
命令格式: mysqldump -uroot -p123456 [數據庫名稱] > 保存文件的路徑和名稱
在/root/桌面就會存在37wan.sql這個文件,內容如下:
這時假設我們刪除了37games這個數據庫,那麽就可以利用備份的文件進行導入恢復
使用導入37wan.sql的方法進行數據庫恢復,步驟如下:
①建立37games的數據庫
退出mysql
②執行導入
分頁優化+表鎖和庫存優化+數據庫的備份和導入