數據庫分類
關系型數據庫庫:Relational Database Management System (RDBMS)
- oracle
- mysql:web使用最廣泛的關系型數據庫
- sql server
- sqlite:輕量級數據庫
非關系型數據庫
- redis
- mysql
- mongodb
數據庫設計范式
設計關系數據庫時,遵從不同的規范要求,設計出合理的關系型數據庫,這些不同的規范要求被稱為不同的范式,各種范式呈遞次規范,越高的范式數據庫冗余越小。
目前關系數據庫有六種范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又稱完美范式)。
范式越高,冗余最低,一般到三范式,再往上,表越多,可能導致查詢效率下降。所以有時為了提高運行效率,可以讓數據冗余(反三范式,一般某個數據經常被訪問時,比如數據表里存放了語文數學英語成績,但是如果在某個時間經常要得到它的總分,每次都要進行計算會降低性能,可以加上總分這個冗余字段)。
后面的范式是在滿足前面范式的基礎上,比如滿足第二范式的一定滿足第一范式。
☆☆☆第一范式(1NF):確保每一列的原子性
☆☆☆第二范式:非主鍵字段必須依賴于主鍵字段
☆☆☆第三范式:在1NF基礎上,除了主鍵以外的其它列都不傳遞依賴于主鍵列
mysql的安裝(ubuntu)
- 安裝:sudo apt-get install mysql-server
- 啟動:sudo service mysql start
- 停止:sudo service mysql stop
- 重啟:sudo service mysql restart
- 設置密碼:mysqladmin -u root password mysql(window)
- 啟動:net start mysql(window)
配置
數據庫的創建與使用
- 連接:mysql -uroot -p
- 創建:在登錄的狀態下執行 create database 數據庫名 charset=utf8;
(☆☆☆不指定 charset 那么默認是拉丁字符集,會有下面的報錯信息)
在進行數據庫遷移、創建時報錯,信息(ERROR 1366 (HY000): Incorrect string value: '\xE5\xB0\x84\xE9\x9B\x95...' for column 'name' at row 1
) - 顯示:show databases;
- 使用:use 數據庫名;
- 刪除:drop database 數據庫名;
- 備份:mysqldump -uroot -p 數據庫名 > python.sql
- 恢復:mysql -uroot -p 新數據庫名 < python.sql (新數據庫名已經創建好了)
- 退出:exit 、ctrl+d(清屏ctrl+l、退出執行語句ctrl+c+enter、掛起ctrl + z、從掛起返回fg)
1、Ctrl+C比較暴力,就是發送Terminal到當前的程序,比如你正在運行一個查找功能,文件正在查找中,Ctrl+C就會強制結束當前的這個進程。
2、Ctrl+Z是把當前的程序掛起,暫停執行這個程序,比如你正在mysql終端中,需要出來搞點其他的文件操作,又不想退出mysql終端(因為下次還得輸入用戶名密碼進入,挺麻煩),于是可以ctrl+z將mysql掛起,然后進行其他操作,然后輸入fg回車后就可以回來,當然可以掛起好多進程到后臺,然后fg 加編號就能把掛起的進程返回到前臺。當然,配合bg和fg命令進行前后臺切換會非常方便。
3、Ctrl+D 是發送一個exit信號,沒有那么強烈,類似ctrl+C的操作,比如你從管理員root退回到你的普通用戶就可以這么用。
表的創建和使用
- 創建: create table table_name(字段 類型 約束,字段2 類型 約束....);
例 create table students(id tinyint unsigned not null primary key auto_increment, name varchar(20) default "", height decimal(5,2), ..... );
mysql中主鍵用auto_increment關鍵字避免沖突 - 查看表結構:desc 表名;
- 顯示:show tables;
- 刪除:drop table 表名;
- 表中字段的數據類型
整型:int, ,tinyint, bit(1byte = 8 bit);bit只有0和1
定長字符串: char;可變長度字符串: varchar
浮點數:decimal(5,2) 共五位數,2位小數
日期:date, time, datetime
大文件存儲:text 字符串類型
枚舉類型(enum)
用法:在創建表時,gender enum('男','女','保密','人妖') - 表中字段的常用約束條件
主鍵: primary key 作用:可以通過唯一字段確定一行記錄
非空 :not null 作用:不予許字段為空
唯一 :unique 作用:字段的值不允許重復
默認: default 作用:默認參數,用戶不指定則使用默認值
有符號和無符號: signed unsigned
自增長:auto_increment(一般用于id自動加1)
外鍵:foreign key - 表字段的增刪改查adcm(alter table 表名 ...)
添加:alter table 表名 add 字段 類型(長度) 約束 條件;例如(alter table student add id int primary key not null;)
修改(重命名):alter table 表名 change 舊字段 新字段 類型(長度) 約束 條件;例如(alter table student add id id1 int primary key not null;)
修改(不重命名):alter table 表名 modify 字段 類型(長度) 約束 條件;例如(alter table student add id int primary key not null;)
刪除:alter table 表名 drop 字段 ;例如(alter table student drop id;) - 表中記錄的增刪改查(crud)
curd的解釋: 代表創建(Create)、更新(Update)、讀取(Retrieve)和刪除(Delete)
查詢(全列): select * from table_name;
指定列:select 字段1,字段2 from table_name;
表(字段)重命名:select 字段1 as 字段2 from table_name as ta_na;
消除重復行:select distinct 列1,... from 表名;
修改:uptdate 表名 set 列1=值1,列2=值2,... where 條件;
增加:insert into 表名 values(),(),()...; 指定列增加 insert into 表名 (字段1, 字段2,...) values(),(),()...;
當用子查詢的方式獲取value時,寫法更改為insert into 表名 (字段) 子查詢表達式;(不要帶上values關鍵字)
實例:insert into bookinfo values(0,'新增2','2011-1-1',20,22,1)
指定列增加時按照自己指定的字段(順序可以和表中不一樣)按照順序插入值
邏輯刪除:isDelete ;當值為1時代表要刪除,update 表名 set isDelete=1 where 條件;
刪除:delete from 表名 where 條件;
概要 - 表中記錄的查詢命令
條件查詢:select * from 表名 where 字段判斷條件;
比較運算符: = , >, <, >=, <=, != <>不等于的兩種形式 ;
邏輯運算符:and, not, or;
模糊查詢:like %表示任意多個字符;_表示一個任意字符;
select * from students where name like '黃_'
;
范圍查詢:in()非連續范圍;between and 連續范圍;
空判斷:is null ; is not null
優先級:( ) > not > 比較運算符 > 邏輯運算符 ;
排序:select * from 表名 order by 列1 asc|desc [,列2 asc|desc,...]; asc 升序 desc 降序
應用;☆☆☆☆select gender,group_concat(name order by age) from students group by gender
; 分組后各組按照年齡排序后顯示對應的姓名
結果以行的方式顯示
select readcount,group_concat(name) from bookinfo group by readcount order by null\G;
explain select readcount,group_concat(name) from bookinfo group by readcount order by null\G;
- 聚合函數:select 聚合函數(
*
或者字段 ) from表名;
總數:count(*
)
最大值:max( )
最小值:min( )
求和:sum( )
平均值:avg( )
select gender,avg(age),group_concat(name) from students group by gender with rollup;
round(小數, 保留的位數) - 分組:select 字段 from 表名 group by 字段;將查詢結果按照1個或多個字段進行分組,字段值相同的為一組
1 輸出:group_concat(字段名)可以作為一個輸出字段來使用;
2 group by + group_concat(字段名) 表示分組之后,根據分組結果,使用group_concat()來放置每一組的某字段的值的集合;
3 例如:select gender,group_concat(name) from students group by gender;
group by + 集合函數;例如select gender,avg(age) from students group by gender;
group by + having;用法和where相同
group by + with rollup; with rollup的作用是:在最后新增一行,來記錄所顯示字段所有記錄的總和(平均值、最大值、最小值)
實例:☆☆☆☆select gender,avg(age) from students group by gender with rollup;
(顯示所有人年齡的平均值) - 獲取部分行:
select * from 表名 limit start,count;
start從0開始 - 分頁-顯示第n頁的m條數據;
select * from students limit (n-1)*m,m;
第一個數字可以理解為要顯示數據的id,從該id開始;
可以隨意指定,按上述規則是均分顯示;
select * from students order by age,id limit 3,3; (order by 和limit處理時有mysql的bug存在,需要在限定id字段,可以防止bug出現)
- 連接查詢select * from 左表 join 右表; (返回的笛卡爾積,用join實現)
內連接:select * from 左表 inner join 右表 on 條件;(在mysql中inner join和join是相同的,在其他數據庫中join是笛卡爾積,inner join 是內連接)
右連接:select * from 左表 right join 右表 on 條件;(在連接的基礎上,添加額外數據-來自右表,左表中沒有對應的數據用Null填充)
左連接:select * from 左表 right join 右表 on 條件;(在連接的基礎上,添加額外數據-來自左表,右表中沒有對應的數據用Null填充)
又稱外連接 - 自關聯:
select * from 表 join 表 on 條件 where 條件;
(相同的表進行笛卡爾積計算)
全局搜索-contains語法
1. 查詢住址在北京的學生
SELECT student_id,student_name FROM students WHERE CONTAINS( address, 'beijing' )
remark: beijing是一個單詞,要用單引號括起來。
2. 查詢住址在河北省的學生
SELECT student_id,student_name FROM students WHERE CONTAINS( address, '"HEIBEI province"' )
remark: HEBEI province是一個詞組,在單引號里還要用雙引號括起來。
3. 查詢住址在河北省或北京的學生
SELECT student_id,student_name FROM students WHERE CONTAINS( address, '"HEIBEI province" OR beijing' )
remark: 可以指定邏輯操作符(包括 AND ,AND NOT,OR )。
4. 查詢有 '南京路' 字樣的地址
SELECT student_id,student_name FROM students WHERE CONTAINS( address, 'nanjing NEAR road' )
remark: 上面的查詢將返回包含'nanjing road'
,'nanjing east road'
,'nanjing west road'
等字樣的地址。
A NEAR B,就表示條件: A 靠近 B
5. 查詢以 '湖' 開頭的地址
SELECT student_id,student_name FROM students WHERE CONTAINS( address, '"hu*"' )
remark: 上面的查詢將返回包含 'hubei','hunan' 等字樣的地址。
記住是 *,不是 %。
6. 類似加權的查詢
SELECT student_id,student_name FROM students WHERE CONTAINS( address, 'ISABOUT (city weight (.8), county wright (.4))' )
remark: ISABOUT 是這種查詢的關鍵字,weight 指定了一個介于 0~1之間的數,類似系數(我的理解)。表示不同條件有不同的側重。
7. 單詞的多態查詢
SELECT student_id,student_name FROM students WHERE CONTAINS( address, 'FORMSOF (INFLECTIONAL,street)' )
remark: 查詢將返回包含 'street','streets'等字樣的地址。
對于動詞將返回它的不同的時態,如:dry,將返回 dry,dried,drying 等等。
子主題 5 - 子查詢
標量子查詢:返回唯一值 select * from students where age > (select avg(age) from students);
列級子查詢:select * from classes where id in (select cls_id from students); (列級子查詢返回的數據是一個集合,有自動去重的作用)
行級子查詢:select * from students where (age,height) = (select max(age), max(height) from students); 行級子查詢返回的數據是一個元組
表子查詢:
mysql語句執行順序:完整的select 語句 - 格式 - 執行順序(各公司可能更改執行順序)select distinct * from 表 where 條件 group by 字段 having 條件 order by 條件 limit start,count; (執行順序從左到右)
子主題 1
模糊查詢用法總結
1,%
:表示任意0個或多個字符。可匹配任意類型和長度的字符,有些情況下若是中文,請使用兩個百分號(%%)表示。
比如SELECT * FROM [user] WHERE u_name LIKE '%三%'
將會把u_name為“張三”,“張貓三”、“三腳貓”,“唐三藏”等等有“三”的記錄全找出來。
另外,如果需要找出u_name中既有“三”又有“貓”的記錄,請使用and條件
SELECT * FROM [user] WHERE u_name LIKE '%三%' AND u_name LIKE '%貓%'
若使用SELECT * FROM [user] WHERE u_name LIKE '%三%貓%'
雖然能搜索出“三腳貓”,但不能搜索出符合條件的“張貓三”。
2,_
: 表示任意單個字符。匹配單個任意字符,它常用來限制表達式的字符長度語句:
比如SELECT * FROM [user] WHERE u_name LIKE '_三_'
只找出“唐三藏”這樣u_name為三個字且中間一個字是“三”的;
再比如SELECT * FROM [user] WHERE u_name LIKE '三__'
; 只找出“三腳貓”這樣name為三個字且第一個字是“三”的;
3,[ ]
:表示括號內所列字符中的一個(類似正則表達式)。指定一個字符、字符串或范圍,要求所匹配對象為它們中的任一個。
比如SELECT * FROM [user] WHERE u_name LIKE '[張李王]三'
將找出“張三”、“李三”、“王三”(而不是“張李王三”);
如 [ ] 內有一系列字符(01234、abcde之類的)則可略寫為“0-4”、“a-e”
SELECT * FROM [user] WHERE u_name LIKE '老[1-9]'
將找出“老1”、“老2”、……、“老9”;
4,[^ ]
:表示不在括號所列之內的單個字符。其取值和 [] 相同,但它要求所匹配對象為指定字符以外的任一個字符。
比如SELECT * FROM [user] WHERE u_name LIKE '[^張李王]三'
將找出不姓“張”、“李”、“王”的“趙三”、“孫三”等;
SELECT * FROM [user] WHERE u_name LIKE '老[^1-4]';
將排除“老1”到“老4”,尋找“老5”、“老6”、……
5,查詢內容包含通配符時
由于通配符的緣故,導致我們查詢特殊字符“%
”、“_
”、“[
”的語句無法正常實現,而把特殊字符用“[ ]
”括起便可正常查詢。據此我們寫出以下函數:
function sqlencode(str) str=replace(str,"';","';';")
str=replace(str,"[","[[]") ';
此句一定要在最先 str=replace(str,"","[]")str=replace(str,"%","[%]") sqlencode=str end function
python中操作mysql的步驟
開始:import pymysql
連接:con = mysql.connect(參數列表)
參數host:連接的mysql主機,如果本機是'localhost'
port:連接的mysql主機的端口,默認是3306
參數database:數據庫的名稱
參數user:連接的用戶名
參數password:連接的密碼
參數charset:通信采用的編碼方式,推薦使用utf8(必須指定,否則拉丁編碼格式,容易出錯)創建Cursor對象:
cur = con.cursor()
sql語句
例子:sql = 'insert into focus (info_id) select id from info where code =%s'
執行:cur.execute(sql,[參數列表]),除了執行,還會返回查到符合條件的記錄的條數,沒有查詢到記錄則返回0
例子:cur.execute(sql,[code])
提交:
con.commit()
在沒有提交前可以用con.rollback()進行回滾操作關閉:cur.close()
關閉:con.close()
☆☆防止參數化注入
1、問題引入# SQL注入問題 -> 后臺直接根據用戶給定的數據 直接用字符串方式進行拼接select * from hero where name='妲己' #' and id = 123456789;
(這樣就把and id = 123456789注釋掉了,默認不執行)2、實際實現形式# 實際是內置了一個函數mogrify, 對特殊字符進行轉義,打印出來結果
print(cur.mogrify(sql, [name, id])):select * from hero where name='妲己\' #' and id = '1234567';
3、解決方法# 參數化解決問題 - 防止SQL 對特殊字符進行\字符進行轉義
sql = "select * from hero where name=%s and id = %s;"
row_count = cur.execute(sql, [name, id])
msql高級
視圖
- 定義視圖:create view 試圖名稱 as select語句
- 查看視圖:show tables;
- 使用視圖:select * from 視圖名稱(一般定義以v_開頭)
- 刪除視圖:drop view 視圖名稱
- 視圖作用:1.提高重用性;2.不影響程序的基礎上對數據庫重構;3.提高了安全性;4.讓數據更清晰。
事務 - 原子性:事務不可分割,要么都成功,要么都失敗
- 一致性:總是從一個一致性狀態到另一個一致性狀態
- 隔離性:事務(在提交之前)對外不可見
- 持久性:一旦提交,永久保存
- 事務命令
開啟事務:begin;或者start transaction;
提交事務:commit
回滾事務:rollback;(在提交之前有效) - 注意點:
1、使用事務命令前提是表的引擎是innodb;
2、修改數據的命令會自動觸發事務;
3、在SQL語句中有手動開啟事務的原因是:可以進行多次數據的修改,如果成功一起成功,否則一起會滾到之前的數據。
索引 - 創建索引:
create index 索引名稱 on 表名(字段長度());
- 查看索引:
show index from 表名;
- 查看語句執行時間:set profiling=1; (操作后) show profiles;
- 刪除索引:drop index 索引名稱 on 表名; (索引名稱命名i_開頭)
- 作用:索引是一種特殊的文件,包含對數據表里所有記錄的引用指針。
索引能加快數據庫的查詢速度
賬戶管理 - 查看所有用戶:desc user; select host, user, authentication_string from user;
- 創建賬戶、授權:grant 權限名稱 on 數據庫名 to '用戶名'@'訪問主機' identified by '密碼';
all privileges: 所有權限
%:所有主機 - 修改權限: grant 權限名稱 on 數據庫名 to '用戶名'@'訪問主機' with grant option;
- 修改密碼:update user set authentication_string=password('新密碼') where user='用戶名';
- 刷新權限:flush privileges;
- 刪除用戶:drop user '用戶名'@'訪問主機';
- 為項目創建數據庫用戶
create user meiduo identified by 'meiduo';
grant all on meiduomail.* to 'meiduo'@'%';
flush privileges;
設置主從服務配置
常見問題
show full processlist ;
顯示的數據里有個id字段,就是sessionid,執行 kill id就可,關閉session
(注:應用程序一般和mysql都是做短連接的,執行完sql后都會關閉session,除非是卡在那,或者執行時間太長,才有機會在show processlist中看到)
mysql優化原理
mysql查詢過程圖解
優化原理
我們總是希望MySQL能夠獲得更高的查詢性能,最好的辦法是弄清楚MySQL是如何優化和執行查詢的。
很多的查詢優化工作實際上就是遵循一些原則讓MySQL的優化器能夠按照預想的合理方式運行而已。
MySQL邏輯架構
- 客戶端層:并非MySQL所獨有,諸如:連接處理、授權認證、安全等功能均在這一層處理。
- 核心服務層:包括查詢解析、分析、優化、緩存、內置函數(比如:時間、數學、加密等函數)。所有的跨存儲引擎的功能也在這一層實現:存儲過程、觸發器、視圖等。
- 存儲引擎:其負責MySQL中的數據存儲和提取。和Linux下的文件系統類似,每種存儲引擎都有其優勢和劣勢。中間的服務層通過API與存儲引擎通信,這些API接口屏蔽了不同存儲引擎間的差異。
具體查詢過程
- 客戶端向MySQL服務器發送一條查詢請求
在任一時刻,要么是服務器向客戶端發送數據,要么是客戶端向服務器發送數據,這兩個動作不能同時發生。
當服務器響應客戶端請求時,客戶端必須完整的接收整個返回結果,而不能簡單的只取前面幾條結果,然后讓服務器停止發送。因而在實際開發中,盡量保持查詢簡單且只返回必需的數據,減小通信間數據包的大小和數量是一個非常好的習慣,這也是查詢中盡量避免使用SELECT *以及加上LIMIT限制的原因之一 - 服務器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲在緩存中的結果。否則進入下一階段
- MySQL的查詢緩存系統會跟蹤查詢中涉及的每個表,如果這些表(數據或結構)發生變化,那么和這張表相關的所有緩存數據都將失效。
任何的查詢語句在開始之前都必須經過檢查,即使這條SQL語句永遠不會命中緩存 - 如果查詢結果可以被緩存,那么執行完成后,會將結果存入緩存,也會帶來額外的系統消耗
基于此,我們要知道并不是什么情況下查詢緩存都會提高系統性能,緩存和失效都會帶來額外消耗,只有當緩存帶來的資源節約大于其本身消耗的資源時,才會給系統帶來性能提升
數據庫設計上針對緩問題的優化
- 用多個小表代替一個大表,注意不要過度設計
- 批量插入代替循環單條插入
- 合理控制緩存空間大小,一般來說其大小設置為幾十兆比較合適
- 可以通過SQL_CACHE和SQL_NO_CACHE來控制某個查詢語句是否需要進行緩存
- 不要輕易打開查詢緩存,特別是寫密集型應用。如果你實在是忍不住,可以將query_cache_type設置為DEMAND,這時只有加入SQL_CACHE的查詢才會走緩存,其他查詢則不會,這樣可以非常自由地控制哪些查詢需要被緩存。
- 服務器進行SQL解析、預處理
MySQL通過關鍵字將SQL語句進行解析,并生成一顆對應的解析樹。
這個過程解析器主要通過語法規則來驗證和解析。
比如SQL中是否使用了錯誤的關鍵字或者關鍵字的順序是否正確等等。
預處理則會根據MySQL規則進一步檢查解析樹是否合法。比如檢查要查詢的數據表和數據列是否存在等等。
再由優化器生成對應的查詢計劃
MySQL使用基于成本的優化器,它嘗試預測一個查詢使用某種執行計劃時的成本,并選擇其中成本最小的一個。
在MySQL可以通過查詢當前會話的last_query_cost的值來得到其計算當前查詢的成本。
MySQL認為的最優跟我們想的不一樣(我們希望執行時間盡可能短,但MySQL值選擇它認為成本小的,但成本小并不意味著執行時間短)等等。 - MySQL的查詢優化器的優化策略
重新定義表的關聯順序(多張表關聯查詢時,并不一定按照SQL中指定的順序進行,但有一些技巧可以指定關聯順序)
優化MIN()和MAX()函數(找某列的最小值,如果該列有索引,只需要查找B+Tree索引最左端,反之則可以找到最大值
提前終止查詢(比如:使用Limit時,查找到滿足數量的結果集后會立即終止查詢)
優化排序(在老版本MySQL會使用兩次傳輸排序,即先讀取行指針和需要排序的字段在內存中對其排序,然后再根據排序結果去讀取數據行,而新版本采用的是單次傳輸排序,也就是一次讀取所有的數據行,然后根據給定的列排序。對于I/O密集型應用,效率會高很多)
等等
MySQL根據執行計劃,調用存儲引擎的API來執行查詢
查詢執行引擎根據執行計劃給出的指令逐步執行得出結果。整個執行過程的大部分操作均是通過調用存儲引擎實現的接口來完成,這些接口被稱為handler API。
將結果返回給客戶端,同時緩存查詢結果
性能優化建議
- Scheme設計與數據類型優化
選擇數據類型只要遵循小而簡單的原則就好,越小的數據類型通常會更快,占用更少的磁盤、內存,處理時需要的CPU周期也更少。 - 創建高性能索引
索引相關的數據結構和算法
通常我們所說的索引是指B-Tree索引,它是目前關系型數據庫中查找數據最為常用和有效的索引,大多數存儲引擎都支持這種索引。
InnoDB就是使用的B+Tree。
B+Tree中的B是指balance,意為平衡。
B+Tree就是一種多路搜索樹。
理解B+Tree時,只需要理解其最重要的兩個特征即可
第一,所有的關鍵字(可以理解為數據)都存儲在葉子節點(Leaf Page),非葉子節點(Index Page)并不存儲真正的數據,所有記錄節點都是按鍵值大小順序存放在同一層葉子節點上。
其次,所有的葉子節點由指針連接。如下圖為高度為2的簡化了的B+Tree。
高性能策略
MySQL不會使用索引的情況:非獨立的列
“獨立的列”是指索引列不能是表達式的一部分,也不能是函數的參數。
前綴索引
如果列很長,通常可以索引開始的部分字符,這樣可以有效節約索引空間,從而提高索引效率。
多列索引和索引順序
當出現多個索引做相交操作時(多個AND條件),通常來說一個包含所有相關列的索引要優于多個獨立索引。
當出現多個索引做聯合操作時(多個OR條件),對結果集的合并、排序等操作需要耗費大量的CPU和內存資源,特別是當其中的某些索引的選擇性不高,需要返回合并大量數據時,查詢成本更高。所以這種情況下還不如走全表掃描。
避免多個范圍條件
覆蓋索引
索引條目遠小于數據行大小,如果只讀取索引,極大減少數據訪問量
索引是有按照列值順序存儲的,對于I/O密集型的范圍查詢要比隨機從磁盤讀取每一行數據的IO要少的多
使用索引掃描來排序
避免冗余和重復索引
刪除長期未使用的索引
特定類型查詢優化
優化COUNT()查詢
如果要統計行數,直接使用COUNT(*),意義清晰,且性能更好。
優化關聯查詢
確保ON和USING字句中的列上有索引。
確保任何的GROUP BY和ORDER BY中的表達式只涉及到一個表中的列,這樣MySQL才有可能使用索引來優化。
優化LIMIT分頁
優化UNION
常見錯誤理解與技巧
通常來說把可為NULL的列改為NOT NULL不會對性能提升有多少幫助,只是如果計劃在列上創建索引,就應該將該列設置為NOT NULL。
對整數類型指定寬度,比如INT(11),沒有任何卵用。INT使用32位(4個字節)存儲空間,那么它的表示范圍已經確定,所以INT(1)和INT(20)對于存儲和計算是相同的。
UNSIGNED表示不允許負值,大致可以使正數的上限提高一倍。比如TINYINT存儲范圍是-128 ~ 127,而UNSIGNED TINYINT存儲的范圍卻是0 - 255。
通常來講,沒有太大的必要使用DECIMAL數據類型。即使是在需要存儲財務數據時,仍然可以使用BIGINT。比如需要精確到萬分之一,那么可以將數據乘以一百萬然后使用BIGINT存儲。這樣可以避免浮點數計算不準確和DECIMAL精確計算代價高的問題。
TIMESTAMP使用4個字節存儲空間,DATETIME使用8個字節存儲空間。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范圍小得多,而且TIMESTAMP的值因時區不同而不同。
大多數情況下沒有使用枚舉類型的必要,其中一個缺點是枚舉的字符串列表是固定的,添加和刪除字符串(枚舉選項)必須使用ALTER TABLE(如果只只是在列表末尾追加元素,不需要重建表)。
schema的列不要太多。原因是存儲引擎的API工作時需要在服務器層和存儲引擎層之間通過行緩沖格式拷貝數據,然后在服務器層將緩沖內容解碼成各個列,這個轉換過程的代價是非常高的。如果列太多而實際使用的列又很少的話,有可能會導致CPU占用過高。
大表ALTER TABLE非常耗時,MySQL執行大部分修改表結果操作的方法是用新的結構創建一個張空表,從舊表中查出所有的數據插入新表,然后再刪除舊表。尤其當內存不足而表又很大,而且還有很大索引的情況下,耗時更久。當然有一些奇技技巧可以解決這個問題,有興趣可自行查閱。