數據保存在內存mysql
數據保存在文件程序員
數據保存在數據庫面試
結構化查詢語言(Structured Query Language)簡稱SQL,是一種數據庫查詢語言(12800字!SQL 語法速成手冊)。redis
做用:用於存取數據、查詢、更新和管理關係數據庫系統。算法
MySQL是一個關係型數據庫管理系統,由瑞典MySQL) AB 公司開發,屬於 Oracle 旗下產品。MySQL 是最流行的關係型數據庫管理系統之一,在 WEB 應用方面,MySQL是最好的 RDBMS (Relational Database Management System,關係數據庫管理系統) 應用軟件之一。在Java企業級開發中很是經常使用,由於 MySQL 是開源免費的,而且方便擴展。sql
在設計數據庫結構的時候,要儘可能遵照三範式,若是不遵照,必須有足夠的理由。好比性能。事實上咱們常常會爲了性能而妥協數據庫的設計。數據庫
MySQL服務器經過權限表來控制用戶對數據庫的訪問,權限表存放在mysql數據庫裏,由mysql_install_db
腳本初始化。這些權限表分別user,db,table_priv,columns_priv和host
。下面分別介紹一下這些表的結構和內容:編程
user權限表:記錄容許鏈接到服務器的用戶賬號信息,裏面的權限是全局級的。
db權限表:記錄各個賬號在各個數據庫上的操做權限。
table_priv權限表:記錄數據表級的操做權限。
columns_priv權限表:記錄數據列級的操做權限。
host權限表:配合db權限表對給定主機上數據庫級操做權限做更細緻的控制。這個權限表不受GRANT和REVOKE語句的影響。後端
有三種格式,statement,row和mixed
。ROW 仍是 STATEMENT?線上 MySQL Binlog 怎麼選?數組
此外,新版的MySQL中對row級別也作了一些優化,當表結構發生變化的時候,會記錄語句而不是逐行記錄。贊!7000 字學習筆記,MySQL 從入門到放棄
整數類型
實數類型
字符串類型
枚舉類型(ENUM),把不重複的數據存儲爲一個預約義的集合。
日期和時間類型,儘可能使用timestamp,空間效率高於datetime,
存儲引擎Storage engine:MySQL中的數據、索引以及其餘對象是如何存儲的,是一套文件系統的實現。
若是沒有特別的需求,使用默認的Innodb便可。
MyISAM:以讀寫插入爲主的應用程序,好比博客系統、新聞門戶網站。
Innodb:更新(刪除)操做頻率也高,或者要保證數據的完整性;併發量高,支持事務和外鍵。好比OA自動化辦公系統。
索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。
索引是一種數據結構。數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現一般使用B樹及其變種B+樹。
更通俗的說,索引就至關於目錄。爲了方便查找書中的內容,經過對內容創建索引造成目錄。索引是一個文件,它是要佔據物理空間的。
索引的優勢
索引的缺點
上圖中,根據id查詢記錄,由於id字段僅創建了主鍵索引,所以此SQL執行可選的索引只有主鍵索引,若是有多個,最終會選一個較優的做爲檢索的依據。
-- 增長一個沒有創建索引的字段 alter table innodb1 add sex char(1); -- 按sex檢索時可選的索引爲null EXPLAIN SELECT * from innodb1 where sex='男';
能夠嘗試在一個字段未創建索引時,根據該字段查詢的效率,而後對該字段創建索引(alter table 表名 add index(字段名)),一樣的SQL執行的效率,你會發現查詢效率會有明顯的提高(數據量越大越明顯)。
當咱們使用order by將查詢結果按照某個字段排序時,若是該字段沒有創建索引,那麼執行計劃會將查詢出的全部數據使用外部排序(將數據從硬盤分批讀取到內存使用內部排序,最後合併排序結果),這個操做是很影響性能的,由於須要將查詢涉及到的全部數據從磁盤中讀到內存(若是單條數據過大或者數據量過多都會下降效率),更不管讀到內存以後的排序了。
可是若是咱們對該字段創建索引alter table 表名 add index(字段名),那麼因爲索引自己是有序的,所以直接按照索引的順序和映射關係逐條取出數據便可。並且若是分頁的,那麼只用取出索引表某個範圍內的索引對應的數據,而不用像上述那取出全部數據進行排序再返回某個範圍內的數據。(從磁盤取數據是最影響性能的)
對join語句匹配關係(on)涉及的字段創建索引可以提升效率
若是要查詢的字段都創建過索引,那麼引擎會直接在索引表中查詢而不會訪問原始數據(不然只要有一個字段沒有創建索引就會作全表掃描),這叫索引覆蓋。所以咱們須要儘量的在select後只寫必要的查詢字段,以增長索引覆蓋的概率。
這裏值得注意的是不要想着爲每一個字段創建索引,由於優先使用索引的優點就在於其體積小。
惟一索引: 數據列不容許重複,容許爲NULL值,一個表容許多個列建立惟一索引。
能夠經過 ALTER TABLE table_name ADD UNIQUE (column); 建立惟一索引 能夠經過 ALTER TABLE table_name ADD UNIQUE (column1,column2); 建立惟一組合索引
普通索引: 基本的索引類型,沒有惟一性的限制,容許爲NULL值。
能夠經過ALTER TABLE table_name ADD INDEX index_name (column);建立普通索引 能夠經過ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);建立組合索引
全文索引: 是目前搜索引擎使用的一種關鍵技術。
能夠經過ALTER TABLE table_name ADD FULLTEXT (column);建立全文索引
索引的數據結構和具體存儲引擎的實現有關,在MySQL中使用較多的索引有Hash索引,B+樹索引等,而咱們常用的InnoDB存儲引擎的默認索引實現爲:B+樹索引。對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。
mysql經過存儲引擎取數據,基本上90%的人用的就是InnoDB了,按照實現方式分,InnoDB的索引類型目前只有兩種:BTREE(B樹)索引和HASH索引。B樹索引是Mysql數據庫中使用最頻繁的索引類型,基本全部存儲引擎都支持BTree索引。一般咱們說的索引不出意外指的就是(B樹)索引(實際是用B+樹實現的,由於在查看錶索引時,mysql一概打印BTREE,因此簡稱爲B樹索引)
查詢方式:
B+tree性質:
簡要說下,相似於數據結構中簡單實現的HASH表(散列表)同樣,當咱們在mysql中用哈希索引時,主要就是經過Hash算法(常見的Hash算法有直接定址法、平方取中法、摺疊法、除數取餘法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一併存入Hash表的對應位置;若是發生Hash碰撞(兩個不一樣關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。固然這只是簡略模擬圖。
索引用來快速地尋找那些具備特定值的記錄。若是沒有索引,通常來講執行查詢時遍歷整張表。
索引的原理很簡單,就是把無序的數據變成有序的查詢
索引算法有BTree算法和Hash算法
。
BTree是最經常使用的mysql數據庫索引算法,也是mysql默認的算法。由於它不只能夠被用在=,>,>=,<,<=和between這些比較操做符上,並且還能夠用於like操做符,只要它的查詢條件是一個不以通配符開頭的常量, 例如:
-- 只要它的查詢條件是一個不以通配符開頭的常量 select * from user where name like 'jack%'; -- 若是一通配符開頭,或者沒有使用常量,則不會使用索引,例如: select * from user where name like '%jack';
Hash Hash索引只能用於對等比較,例如=,<=>(至關於=)操做符。因爲是一次定位數據,不像BTree索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次IO訪問,因此檢索效率遠高於BTree索引。
索引雖好,但也不是無限制的使用,最好符合一下幾個原則
第一種方式:在執行CREATE TABLE時建立索引
CREATE TABLE user_index2 ( id INT auto_increment PRIMARY KEY, first_name VARCHAR (16), last_name VARCHAR (16), id_card VARCHAR (18), information text, KEY name (first_name, last_name), FULLTEXT KEY (information), UNIQUE KEY (id_card) );
第二種方式:使用ALTER TABLE命令去增長索引
ALTER TABLE table_name ADD INDEX index_name (column_list);
第三種方式:使用CREATE INDEX命令建立
CREATE INDEX index_name ON table_name (column_list);
CREATE INDEX可對錶增長普通索引或UNIQUE索引。(可是,不能建立PRIMARY KEY索引)
根據索引名刪除普通索引、惟一索引、全文索引:alter table 表名 drop KEY 索引名
alter table user_index drop KEY name; alter table user_index drop KEY id_card; alter table user_index drop KEY information;
刪除主鍵索引:alter table 表名 drop primary key(由於主鍵只有一個)。這裏值得注意的是,若是主鍵自增加,那麼不能直接執行此操做(自增加依賴於主鍵索引):
須要取消自增加再行刪除:
alter table user_index -- 從新定義字段 MODIFY id int, drop PRIMARY KEY
但一般不會刪除主鍵,由於設計主鍵必定與業務邏輯無關。
一般,經過索引查詢數據比全表掃描要快。可是咱們也必須注意到它的代價。
關於索引:因爲索引須要額外的維護成本,由於索引文件是單獨存在的文件,因此當咱們對數據的增長,修改,刪除,都會產生額外的對索引文件的操做,這些操做須要消耗額外的IO,會下降增/改/刪的執行效率。因此,在咱們刪除數據庫百萬級別數據的時候,查詢MySQL官方手冊得知刪除數據的速度和建立的索引數量是成正比的。
語法:index(field(10)),使用字段值的前10個字符創建索引,默認是使用字段的所有內容創建索引。
前提:前綴的標識度高。好比密碼就適合創建前綴索引,由於密碼幾乎各不相同。
實操的難度:在於前綴截取的長度。
咱們能夠利用select count(*)/count(distinct left(password,prefixLen));
,經過從調整prefixLen的值(從1自增)查看不一樣前綴長度的一個平均匹配度,接近1時就能夠了(表示一個密碼的前prefixLen個字符幾乎能肯定惟一一條記錄)
在B樹中,你能夠將鍵和值存放在內部節點和葉子節點;但在B+樹中,內部節點都是鍵,沒有值,葉子節點同時存放鍵和值。
B+樹的葉子節點有一條鏈相連,而B樹的葉子節點各自獨立。
B樹能夠在內部節點同時存儲鍵和值,所以,把頻繁訪問的數據放在靠近根節點的地方將會大大提升熱點數據的查詢效率。這種特性使得B樹在特定數據重複屢次查詢的場景中更加高效。
因爲B+樹的內部節點只存放鍵,不存放值,所以,一次讀取,能夠在內存頁中獲取更多的鍵,有利於更快地縮小查找範圍。 B+樹的葉節點由一條鏈相連,所以,當須要進行一次全數據遍歷的時候,B+樹只須要使用O(logN)時間找到最小的一個節點,而後經過鏈進行O(N)的順序遍歷便可。而B樹則須要對樹的每一層進行遍歷,這會須要更多的內存置換次數,所以也就須要花費更多的時間
首先要知道Hash索引和B+樹索引的底層實現原理:
hash索引底層就是hash表,進行查找時,調用一次hash函數就能夠獲取到相應的鍵值,以後進行回表查詢得到實際數據。B+樹底層實現是多路平衡查找樹。對於每一次的查詢都是從根節點出發,查找到葉子節點方能夠得到所查鍵值,而後根據查詢判斷是否須要回表查詢數據。
那麼能夠看出他們有如下的不一樣:
由於在hash索引中通過hash函數創建索引以後,索引的順序與原順序沒法保持一致,不能支持範圍查詢。而B+樹的的全部節點皆遵循(左節點小於父節點,右節點大於父節點,多叉樹也相似),自然支持範圍。
hash索引不支持使用索引進行排序,原理同上。
所以,在大多數狀況下,直接選擇B+樹索引能夠得到穩定且較好的查詢速度。而不須要使用hash索引。
在B+樹的索引中,葉子節點可能存儲了當前的key值,也可能存儲了當前的key值以及整行的數據,這就是聚簇索引和非聚簇索引。 在InnoDB中,只有主鍵索引是聚簇索引,若是沒有主鍵,則挑選一個惟一鍵創建聚簇索引。若是沒有惟一鍵,則隱式的生成一個鍵來創建聚簇索引。
當查詢使用聚簇索引時,在對應的葉子節點,能夠獲取到整行數據,所以不用再次進行回表查詢。
澄清一個概念:innodb中,在聚簇索引之上建立的索引稱之爲輔助索引,輔助索引訪問數據老是須要二次查找,非聚簇索引都是輔助索引,像複合索引、前綴索引、惟一索引,輔助索引葉子節點存儲的再也不是行的物理位置,而是主鍵值
什麼時候使用聚簇索引與非聚簇索引
不必定,這涉及到查詢語句所要求的字段是否所有命中了索引,若是所有命中了索引,那麼就沒必要再進行回表查詢。
舉個簡單的例子,假設咱們在員工表的年齡上創建了索引,那麼當進行select age from employee where age < 20的查詢時,在索引的葉子節點上,已經包含了age信息,不會再次進行回表查詢。
MySQL能夠使用多個字段同時創建一個索引,叫作聯合索引。在聯合索引中,若是想要命中索引,須要按照創建索引時的字段順序挨個使用,不然沒法命中索引。
具體緣由爲:
MySQL使用索引時須要索引有序,假設如今創建了"name,age,school"的聯合索引,那麼索引的排序爲: 先按照name排序,若是name相同,則按照age排序,若是age的值也相等,則按照school進行排序。
當進行查詢時,此時索引僅僅按照name嚴格有序,所以必須首先使用name字段進行等值查詢,以後對於匹配到的列而言,其按照age字段嚴格有序,此時能夠使用age字段用作索引查找,以此類推。所以在創建聯合索引的時候應該注意索引列的順序,通常狀況下,將查詢需求頻繁或者字段選擇性高的列放在前面。此外能夠根據特例的查詢或者表結構進行單獨的調整。
事務是一個不可分割的數據庫操做序列,也是數據庫併發控制的基本單位,其執行的結果必須使數據庫從一種一致性狀態變到另外一種一致性狀態。事務是邏輯上的一組操做,要麼都執行,要麼都不執行。
事務最經典也常常被拿出來講例子就是轉帳了。
假如小明要給小紅轉帳1000元,這個轉帳會涉及到兩個關鍵操做就是:將小明的餘額減小1000元,將小紅的餘額增長1000元。萬一在這兩個操做之間忽然出現錯誤好比銀行系統崩潰,致使小明餘額減小而小紅的餘額沒有增長,這樣就不對了。事務就是保證這兩個關鍵操做要麼都成功,要麼都要失敗。
關係性數據庫須要遵循ACID規則,具體內容以下:
髒讀(Drity Read):某個事務已更新一份數據,另外一個事務在此時讀取了同一份數據,因爲某些緣由,前一個RollBack了操做,則後一個事務所讀取的數據就會是不正確的。
不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中數據不一致,這多是兩次查詢過程當中間插入了一個事務更新的原有的數據。
幻讀(Phantom Read):在一個事務的兩次查詢中數據筆數不一致,例若有一個事務查詢了幾列(Row)數據,而另外一個事務卻在此時插入了新的幾列數據,先前的事務在接下來的查詢中,就會發現有幾列數據是它先前所沒有的。
爲了達到事務的四大特性,數據庫定義了4種不一樣的事務隔離級別,由低到高依次爲Read uncommitted、Read committed、Repeatable read、Serializable,這四個級別能夠逐個解決髒讀、不可重複讀、幻讀這幾類問題。
SQL 標準定義了四個隔離級別:
這裏須要注意的是:Mysql 默認採用的 REPEATABLE_READ隔離級別 Oracle 默認採用的 READ_COMMITTED隔離級別
事務隔離機制的實現基於鎖機制和併發調度。其中併發調度使用的是MVVC(多版本併發控制),經過保存修改的舊版本信息來支持併發一致性讀和回滾等特性。
由於隔離級別越低,事務請求的鎖越少,因此大部分數據庫系統的隔離級別都是READ-COMMITTED(讀取提交內容):,可是你要知道的是InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀)並不會有任何性能損失。
InnoDB 存儲引擎在 分佈式事務 的狀況下通常會用到SERIALIZABLE(可串行化)隔離級別。
當數據庫有併發事務的時候,可能會產生數據的不一致,這時候須要一些機制來保證訪問的次序,鎖機制就是這樣的一個機制。
就像酒店的房間,若是你們隨意進出,就會出現多人搶奪同一個房間的狀況,而在房間上裝上鎖,申請到鑰匙的人才能夠入住而且將房間鎖起來,其餘人只有等他使用完畢才能夠再次使用。
在Read Uncommitted級別下,讀取數據不須要加共享鎖,這樣就不會跟被修改的數據上的排他鎖衝突
在Read Committed級別下,讀操做須要加共享鎖,可是在語句執行完之後釋放共享鎖;
在Repeatable Read級別下,讀操做須要加共享鎖,可是在事務提交以前並不釋放共享鎖,也就是必須等待事務執行完畢之後才釋放共享鎖。
SERIALIZABLE 是限制性最強的隔離級別,由於該級別鎖定整個範圍的鍵,並一直持有鎖,直到事務完成。
在關係型數據庫中,能夠按照鎖的粒度把數據庫鎖分爲行級鎖(INNODB引擎)、表級鎖(MYISAM引擎)和頁級鎖(BDB引擎 )。
行級鎖,表級鎖和頁級鎖對比
行級鎖是Mysql中鎖定粒度最細的一種鎖,表示只針對當前操做的行進行加鎖。行級鎖能大大減小數據庫操做的衝突。其加鎖粒度最小,但加鎖的開銷也最大。行級鎖分爲共享鎖 和 排他鎖。
特色:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操做的整張表加鎖,它實現簡單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MYISAM與INNODB都支持表級鎖定。表級鎖定分爲表共享讀鎖(共享鎖)與表獨佔寫鎖(排他鎖)。
特色:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發出鎖衝突的機率最高,併發度最低。
頁級鎖是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但衝突多,行級衝突少,但速度慢。因此取了折衷的頁級,一次鎖定相鄰的一組記錄。
特色:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常
從鎖的類別上來說,有共享鎖和排他鎖。
共享鎖: 又叫作讀鎖。 當用戶要進行數據的讀取時,對數據加上共享鎖。共享鎖能夠同時加上多個。
排他鎖: 又叫作寫鎖。 當用戶要進行數據的寫入時,對數據加上排他鎖。排他鎖只能夠加一個,他和其餘的排他鎖,共享鎖都相斥。
用上面的例子來講就是用戶的行爲有兩種,一種是來看房,多個用戶一塊兒看房是能夠接受的。 一種是真正的入住一晚,在這期間,不管是想入住的仍是想看房的都不能夠。
鎖的粒度取決於具體的存儲引擎,InnoDB實現了行級鎖,頁級鎖,表級鎖。
他們的加鎖開銷從大到小,併發能力也是從大到小。
答:InnoDB是基於索引來完成行鎖
例: select * from tab_with_index where id = 1 for update;
for update 能夠根據條件來完成行鎖鎖定,而且 id 是有索引鍵的列,若是 id 不是索引鍵那麼InnoDB將完成表鎖,併發將無從談起
死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方的資源,從而致使惡性循環的現象。
常見的解決死鎖的方法
若是業務處理很差能夠用分佈式事務鎖或者使用樂觀鎖
數據庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和統一性以及數據庫的統一性。樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。
悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操做。在查詢完數據的時候就把事務鎖起來,直到提交事務。實現方式:使用數據庫中的鎖機制
樂觀鎖:假設不會發生併發衝突,只在提交操做時檢查是否違反數據完整性。在修改數據的時候把事務鎖起來,經過version的方式來進行鎖定。實現方式:樂通常會使用版本號機制或CAS算法實現。
從上面對兩種鎖的介紹,咱們知道兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下(多讀場景),即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。
但若是是多寫的狀況,通常會常常產生衝突,這就會致使上層應用會不斷的進行retry,這樣反卻是下降了性能,因此通常多寫的場景下用悲觀鎖就比較合適。
爲了提升複雜SQL語句的複用性和表操做的安全性,MySQL數據庫管理系統提供了視圖特性。所謂視圖,本質上是一種虛擬表,在物理上是不存在的,其內容與真實的表類似,包含一系列帶有名稱的列和行數據。可是,視圖並不在數據庫中以儲存的數據值形式存在。行和列數據來自定義視圖的查詢所引用基本表,而且在具體引用視圖時動態生成。
視圖使開發者只關心感興趣的某些特定數據和所負責的特定任務,只能看到視圖中所定義的數據,而不是視圖所引用表中的數據,從而提升了數據庫中數據的安全性。
視圖的特色以下:
視圖的操做包括建立視圖,查看視圖,刪除視圖和修改視圖。
視圖根本用途:簡化sql查詢,提升開發效率。若是說還有另一個用途那就是兼容老的表結構。
下面是視圖的常見使用場景:
性能。數據庫必須把視圖的查詢轉化成對基本表的查詢,若是這個視圖是由一個複雜的多表查詢所定義,那麼,即便是視圖的一個簡單查詢,數據庫也把它變成一個複雜的結合體,須要花費必定的時間。
修改限制。當用戶試圖修改視圖的某些行時,數據庫必須把它轉化爲對基本表的某些行的修改。事實上,當從視圖中插入或者刪除時,狀況也是這樣。對於簡單視圖來講,這是很方便的,可是,對於比較複雜的視圖,多是不可修改的
這些視圖有以下特徵:1.有UNIQUE等集合操做符的視圖。2.有GROUP BY子句的視圖。3.有諸如AVG\SUM\MAX等聚合函數的視圖。 4.使用DISTINCT關鍵字的視圖。5.鏈接表的視圖(其中有些例外)
遊標是系統爲用戶開設的一個數據緩衝區,存放SQL語句的執行結果,每一個遊標區都有一個名字。用戶能夠經過遊標逐一獲取記錄並賦給主變量,交由主語言進一步處理。
存儲過程是一個預編譯的SQL語句,優勢是容許模塊化的設計,就是說只須要建立一次,之後在該程序中就能夠調用屢次。若是某次操做須要執行屢次SQL,使用存儲過程比單純SQL語句執行要快。
優勢
缺點
觸發器是用戶定義在關係表上的一類由事件驅動的特殊的存儲過程。觸發器是指一段代碼,當觸發某個事件時,自動執行這些代碼。
注意不要濫用,不然會形成數據庫及應用程序的維護困難。
你們須要牢記以上基礎知識點,重點是理解數據類型CHAR和VARCHAR的差別,表存儲引擎InnoDB和MyISAM的區別。
在MySQL數據庫中有以下六種觸發器:
數據定義語言DDL(Data Ddefinition Language)CREATE,DROP,ALTER
主要爲以上操做 即對邏輯結構等有操做的,其中包括表結構,視圖和索引。
數據查詢語言DQL(Data Query Language)SELECT
這個較爲好理解 即查詢操做,以select關鍵字。各類簡單查詢,鏈接查詢等 都屬於DQL。
數據操縱語言DML(Data Manipulation Language)INSERT,UPDATE,DELETE
主要爲以上操做 即對數據進行操做的,對應上面所說的查詢操做 DQL與DML共同構建了多數初級程序員經常使用的增刪改查操做。而查詢是較爲特殊的一種 被劃分到DQL中。
數據控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK
主要爲以上操做 即對數據庫安全性完整性等有操做的,能夠簡單的理解爲權限控制等。
i交叉鏈接(CROSS JOIN)
SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)#沒有任何關聯條件,結果是笛卡爾積,結果集會很大,沒有意義,不多使用內鏈接(INNER JOIN)SELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id多表中同時符合某種條件的數據記錄的集合,INNER JOIN能夠縮寫爲JOIN
內鏈接分爲三類
外鏈接(LEFT JOIN/RIGHT JOIN)
聯合查詢(UNION與UNION ALL)
SELECT * FROM A UNION SELECT * FROM B UNION ...
全鏈接(FULL JOIN)
能夠使用LEFT JOIN 和UNION和RIGHT JOIN聯合使用
SELECT * FROM A LEFT JOIN B ON A.id=B.id UNIONSELECT * FROM A RIGHT JOIN B ON A.id=B.id
錶鏈接面試題
有2張表,1張R、1張S,R表有ABC三列,S表有CD兩列,表中各有三條記錄。
#交叉鏈接(笛卡爾積): select r.*,s.* from r,s
內鏈接結果:
select r.*,s.* from r inner join s on r.c=s.c
左鏈接結果:
select r.*,s.* from r left join s on r.c=s.c
右鏈接結果:
select r.*,s.* from r right join s on r.c=s.c
全錶鏈接的結果(MySql不支持,Oracle支持):
select r.*,s.* from r full join s on r.c=s.c
條件:一條SQL語句的查詢結果作爲另外一條查詢語句的條件或查詢結果
嵌套:多條SQL語句嵌套使用,內部的SQL查詢語句稱爲子查詢。
子查詢是單行單列的狀況:結果集是一個值,父查詢使用:=、 <、 > 等運算符
-- 查詢工資最高的員工是誰? select * from employee where salary=(select max(salary) from employee);
子查詢是多行單列的狀況:結果集相似於一個數組,父查詢使用:in 運算符
-- 查詢工資最高的員工是誰? select * from employee where salary=(select max(salary) from employee);
子查詢是多行多列的狀況:結果集相似於一張虛擬表,不能用於where條件,用於select子句中作爲子表
-- 1) 查詢出2011年之後入職的員工信息 -- 2) 查詢全部的部門信息,與上面的虛擬表中的信息比對,找出全部部門ID相等的員工。 select * from dept d, (select * from employee where join_date > '2011-1-1') e where e.dept_id = d.id; -- 使用錶鏈接: select d.*, e.* from dept d inner join employee e on d.id = e.dept_id where e.join_date > '2011-1-1'
mysql中的in語句是把外表和內表做hash 鏈接,而exists語句是對外表做loop循環,每次loop循環再對內表進行查詢。一直你們都認爲exists比in語句的效率要高,這種說法實際上是不許確的。這個是要區分環境的。
char的特色
varchar的特色
總之,結合性能角度(char更快)和節省磁盤空間角度(varchar更小),具體狀況還需具體來設計數據庫纔是穩當的作法。
varchar(50)中50的涵義
最多存放50個字符,varchar(50)和(200)存儲hello所佔空間同樣,但後者在排序時會消耗更多內存,由於order by col採用fixed_length計算col長度(memory引擎也同樣)。在早期 MySQL 版本中, 50 表明字節數,如今表明字符數。
int(20)中20的涵義
是指顯示字符的長度。20表示最大顯示寬度爲20,但仍佔4字節存儲,存儲範圍不變;
不影響內部存儲,只是影響帶 zerofill 定義的 int 時,前面補多少個 0,易於報表展現
mysql爲何這麼設計
對大多數應用沒有意義,只是規定一些工具用來顯示字符的個數;int(1)和int(20)存儲和計算均同樣;
mysql中int(10)和char(10)以及varchar(10)的區別
三者都表示刪除,可是三者有一些差異:
所以,在再也不須要一張表的時候,用drop;在想刪除部分數據行時候,用delete;在保留表而刪除全部數據的時候用truncate。
如何定位及優化SQL語句的性能問題?建立的索引有沒有被使用到?或者說怎麼才能夠知道這條語句運行很慢的緣由?
對於低性能的SQL語句的定位,最重要也是最有效的方法就是使用執行計劃,MySQL提供了explain命令來查看語句的執行計劃。 咱們知道,無論是哪一種數據庫,或者是哪一種數據庫引擎,在對一條SQL語句進行執行的過程當中都會作不少相關的優化,對於查詢語句,最重要的優化方式就是使用索引。 而執行計劃,就是顯示數據庫引擎對於SQL語句的執行的詳細狀況,其中包含了是否使用索引,使用什麼索引,使用的索引的相關信息等。
執行計劃包含的信息 id 有一組數字組成。表示一個查詢中各個子查詢的執行順序;
select_type 每一個子查詢的查詢類型,一些常見的查詢類型。
table 查詢的數據表,當從衍生表中查數據時會顯示 x 表示對應的執行計劃id partitions 表分區、表建立的時候能夠指定經過那個列進行表分區。 舉個例子:
create table tmp ( id int unsigned not null AUTO_INCREMENT, name varchar(255), PRIMARY KEY (id) ) engine = innodb partition by key (id) partitions 5;
type(很是重要,能夠看到有沒有走索引) 訪問類型
possible_keys 可能使用的索引,注意不必定會使用。查詢涉及到的字段上若存在索引,則該索引將被列出來。當該列爲 NULL時就要考慮當前的SQL是否須要優化了。
key 顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示爲NULL。
TIPS:查詢中若使用了覆蓋索引(覆蓋索引:索引的數據覆蓋了須要查詢的全部數據),則該索引僅出如今key列表中
key_length 索引長度
ref 表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
rows 返回估算的結果集數目,並非一個準確的值。
extra 的信息很是豐富,常見的有:
【推薦】SQL性能優化的目標:至少要達到 range 級別,要求是ref級別,若是能夠是consts最好。
說明:
1) consts 單表中最多隻有一個匹配行(主鍵或者惟一索引),在優化階段便可讀取到數據。 2) ref 指的是使用普通的索引(normal index)。 3) range 對索引進行範圍檢索。 反例:explain表的結果,type=index,索引物理文件全掃描,速度很是慢,這個index級別比較range還低,與全表掃描是小巫見大巫。
超大的分頁通常從兩個方向上來解決.
select * from table where age > 20 limit 1000000,10
這種查詢其實也是有能夠優化的餘地的. 這條語句須要load1000000數據而後基本上所有丟棄,只取10條固然比較慢. 當時咱們能夠修改成select * from table where id in (select id from table where age > 20 limit 1000000,10).
這樣雖然也load了一百萬的數據,可是因爲索引覆蓋,要查詢的全部字段都在索引中,因此速度會很快. 同時若是ID連續的好,咱們還能夠select * from table where id > 1000000 limit 10,
效率也是不錯的,優化的可能性有許多種,可是核心思想都同樣,就是減小load的數據.解決超大分頁,其實主要是靠緩存,可預測性的提早查到內容,緩存至redis等k-V數據庫中,直接返回便可.
在阿里巴巴《Java開發手冊》中,對超大分頁的解決辦法是相似於上面提到的第一種.
【推薦】利用延遲關聯或者子查詢優化超多分頁場景。 說明:MySQL並非跳過offset行,而是取offset+N行,而後返回放棄前offset行,返回N行,那當offset特別大的時候,效率就很是的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫。 正例:先快速定位須要獲取的id段,而後再關聯: SELECT a.* FROM 表1 a, (select id from 表1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
LIMIT 子句能夠被用於強制 SELECT 語句返回指定的記錄數。LIMIT 接受一個或兩個數字參數。參數必須是一個整數常量。若是給定兩個參數,第一個參數指定第一個返回記錄行的偏移量,第二個參數指定返回記錄行的最大數目。初始記錄行的偏移量是 0(而不是 1)
mysql> SELECT * FROM table LIMIT 5,10; // 檢索記錄行 6-15
爲了檢索從某一個偏移量到記錄集的結束全部的記錄行,能夠指定第二個參數爲 -1:
mysql> SELECT * FROM table LIMIT 95,-1; // 檢索記錄行 96-last.
若是隻給定一個參數,它表示返回最大的記錄行數目:
mysql> SELECT * FROM table LIMIT 5; //檢索前 5 個記錄行
換句話說,LIMIT n 等價於 LIMIT 0,n。
用於記錄執行時間超過某個臨界值的SQL日誌,用於快速定位慢查詢,爲咱們的優化作參考。
開啓慢查詢日誌
配置項:slow_query_log
能夠使用show variables like ‘slov_query_log’查看是否開啓,若是狀態值爲OFF,能夠使用set GLOBAL slow_query_log = on來開啓,它會在datadir下產生一個xxx-slow.log的文件。
設置臨界時間
配置項:long_query_time
查看:show VARIABLES like 'long_query_time',單位秒
設置:set long_query_time=0.5
實操時應該從長時間設置到短的時間,即將最慢的SQL優化掉
查看日誌,一旦SQL超過了咱們設置的臨界時間就會被記錄到xxx-slow.log中
在業務系統中,除了使用主鍵進行的查詢,其餘的我都會在測試庫上測試其耗時,慢查詢的統計主要由運維在作,會按期將業務中的慢查詢反饋給咱們。
慢查詢的優化首先要搞明白慢的緣由是什麼? 是查詢條件沒有命中索引?是load了不須要的數據列?仍是數據量太大?
因此優化也是針對這三個方向來的,
主鍵是數據庫確保數據行在整張表惟一性的保障,即便業務上本張表沒有主鍵,也建議添加一個自增加的ID列做爲主鍵。設定了主鍵以後,在後續的刪改查的時候可能更加快速以及確保操做數據範圍安全。
推薦使用自增ID,不要使用UUID。
由於在InnoDB存儲引擎中,主鍵索引是做爲聚簇索引存在的,也就是說,主鍵索引的B+樹葉子節點上存儲了主鍵索引以及所有的數據(按照順序),若是主鍵索引是自增ID,那麼只須要不斷向後排列便可,若是是UUID,因爲到來的ID與原來的大小不肯定,會形成很是多的數據插入,數據移動,而後致使產生不少的內存碎片,進而形成插入性能的降低。
總之,在數據量大一些的狀況下,用自增主鍵性能會好一些。
關於主鍵是聚簇索引,若是沒有主鍵,InnoDB會選擇一個惟一鍵來做爲聚簇索引,若是沒有惟一鍵,會生成一個隱式的主鍵。
null值會佔用更多的字節,且會在程序中形成不少與預期不符的狀況。
密碼散列,鹽,用戶身份證號等固定長度的字符串應該使用char而不是varchar來存儲,這樣能夠節省空間且提升檢索效率。
解題方法
對於此類考題,先說明如何定位低效SQL語句,而後根據SQL語句可能低效的緣由作排查,先從索引着手,若是索引沒有問題,考慮以上幾個方面,數據訪問的問題,長難查詢句的問題仍是一些特定類型優化的問題,逐一回答。
2.應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null -- 能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢: select id from t where num=
4.應儘可能避免在 where 子句中使用or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20 -- 能夠這樣查詢: select id from t where num=10 union all select id from t where num=2
5.in 和 not in 也要慎用,不然會致使全表掃描,如:
select id from t where num in(1,2,3) -- 對於連續的數值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
7.若是在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:
select id from t where num=@num -- 能夠改成強制查詢使用索引: select id from t with(index(索引名)) where num=@num
8.應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100 -- 應改成: select id from t where num=100*2
9.應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)=’abc’ -- name以abc開頭的id應改成: select id from t where name like ‘abc%’
一個好的數據庫設計方案對於數據庫的性能每每會起到事半功倍的效果。
須要考慮數據冗餘、查詢和更新的速度、字段的數據類型是否合理等多方面的內容。
將字段不少的表分解成多個表
對於字段較多的表,若是有些字段的使用頻率很低,能夠將這些字段分離出來造成新表。
由於當一個表的數據量很大時,會因爲使用頻率低的字段的存在而變慢。
增長中間表
對於須要常常聯合查詢的表,能夠創建中間表以提升查詢效率。
經過創建中間表,將須要經過聯合查詢的數據插入到中間表中,而後將原來的聯合查詢改成對中間表的查詢。
增長冗餘字段
設計數據表時應儘可能遵循範式理論的規約,儘量的減小冗餘字段,讓數據庫設計看起來精緻、優雅。可是,合理的加入冗餘字段能夠提升查詢速度。
表的規範化程度越高,表和表之間的關係越多,須要鏈接查詢的狀況也就越多,性能也就越差。
注意:冗餘字段的值在一個表中修改了,就要想辦法在其餘表中更新,不然就會致使數據不一致的問題。
當 cpu 飆升到 500%時,先用操做系統命令 top 命令觀察是否是 mysqld 佔用致使的,若是不是,找出佔用高的進程,並進行相關處理。
若是是 mysqld 形成的, show processlist,看看裏面跑的 session 狀況,是否是有消耗資源的 sql 在運行。找出消耗高的 sql,看看執行計劃是否準確, index 是否缺失,或者實在是數據量太大形成。
通常來講,確定要 kill 掉這些線程(同時觀察 cpu 使用率是否降低),等進行相應的調整(好比說加索引、改 sql、改內存參數)以後,再從新跑這些 SQL。
也有多是每一個 sql 消耗資源並很少,可是忽然之間,有大量的 session 連進來致使 cpu 飆升,這種狀況就須要跟應用一塊兒來分析爲什麼鏈接數會激增,再作出相應的調整,好比說限制鏈接數等
當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯降低,一些常見的優化措施以下:
垂直分區
根據數據庫裏面數據表的相關性進行拆分。 例如,用戶表中既有用戶的登陸信息又有用戶的基本信息,能夠將用戶表拆分紅兩個單獨的表,甚至放到單獨的庫作分庫。
簡單來講垂直拆分是指數據表列的拆分,把一張列比較多的表拆分爲多張表。 以下圖所示,這樣來講你們應該就更容易理解了。
垂直拆分的優勢: 能夠使得行數據變小,在查詢時減小讀取的Block數,減小I/O次數。此外,垂直分區能夠簡化表的結構,易於維護。
垂直拆分的缺點: 主鍵會出現冗餘,須要管理冗餘列,並會引發Join操做,能夠經過在應用層進行Join來解決。此外,垂直分區會讓事務變得更加複雜;
垂直分表
把主鍵和一些列放在一個表,而後把主鍵和另外的列放在另外一個表中
適用場景
缺點
水平分區
保持數據表結構不變,經過某種策略存儲數據分片。這樣每一片數據分散到不一樣的表或者庫中,達到了分佈式的目的。 水平拆分能夠支撐很是大的數據量。
水平拆分是指數據錶行的拆分,表的行數超過200萬行時,就會變慢,這時能夠把一張的表的數據拆成多張表來存放。舉個例子:咱們能夠將用戶信息表拆分紅多個用戶信息表,這樣就能夠避免單一表數據量過大對性能形成影響。
水品拆分能夠支持很是大的數據量。須要注意的一點是:分表僅僅是解決了單一表數據過大的問題,但因爲表的數據仍是在同一臺機器上,其實對於提高MySQL併發能力沒有什麼意義,因此 水平拆分最好分庫 。
水平拆分可以 支持很是大的數據量存儲,應用端改造也少,但 分片事務難以解決 ,跨界點Join性能較差,邏輯複雜。
《Java工程師修煉之道》的做者推薦 儘可能不要對數據進行分片,由於拆分會帶來邏輯、部署、運維的各類複雜度 ,通常的數據表在優化得當的狀況下支撐千萬如下的數據量是沒有太大問題的。若是實在要分片,儘可能選擇客戶端分片架構,這樣能夠減小一次和中間件的網絡I/O。
水平分表
表很大,分割後能夠下降在查詢時須要讀的數據和索引的頁數,同時也下降了索引的層數,提升查詢次數
適用場景
水平切分的缺點
下面補充一下數據庫分片的兩種常見方案:
客戶端代理: 分片邏輯在應用端,封裝在jar包中,經過修改或者封裝JDBC層來實現。 噹噹網的 Sharding-JDBC 、阿里的TDDL是兩種比較經常使用的實現。
中間件代理: 在應用和數據中間加了一個代理層。分片邏輯統一維護在中間件服務中。 咱們如今談的 Mycat 、360的Atlas、網易的DDB等等都是這種架構的實現。
UUID 使用UUID做主鍵是最簡單的方案,可是缺點也是很是明顯的。因爲UUID很是的長,除佔用大量存儲空間外,最主要的問題是在索引上,在創建索引和基於索引進行查詢時都存在性能問題。 Twitter的分佈式自增ID算法Snowflake 在分佈式系統中,須要生成全局UID的場合仍是比較多的,twitter的snowflake解決了這種需求,實現也仍是很簡單的,除去配置信息,核心代碼就是毫秒級時間41位 機器ID 10位 毫秒內序列12位。
跨分片的排序分頁
般來說,分頁時須要按照指定字段進行排序。當排序字段就是分片字段的時候,咱們經過分片規則能夠比較容易定位到指定的分片,而當排序字段非分片字段的時候,狀況就會變得比較複雜了。爲了最終結果的準確性,咱們須要在不一樣的分片節點中將數據進行排序並返回,並將不一樣分片返回的結果集進行彙總和再次排序,最後再返回給用戶。以下圖所示:
主從複製:將主數據庫中的DDL和DML操做經過二進制日誌(BINLOG)傳輸到從數據庫上,而後將這些日誌從新執行(重作);從而使得從數據庫的數據與主數據庫保持一致。
基本原理流程,3個線程以及之間的關聯
主:binlog線程——記錄下全部改變了數據庫數據的語句,放進master上的binlog中;
從:io線程——在使用start slave 以後,負責從master上拉取 binlog 內容,放進本身的relay log中;
從:sql執行線程——執行relay log中的語句;
複製過程
Binary log:主數據庫的二進制日誌
Relay log:從服務器的中繼日誌
第一步:master在每一個事務更新數據完成以前,將該操做記錄串行地寫入到binlog文件中。
第二步:salve開啓一個I/O Thread,該線程在master打開一個普通鏈接,主要工做是binlog dump process。若是讀取的進度已經跟上了master,就進入睡眠狀態並等待master產生新的事件。I/O線程最終的目的是將這些事件寫入到中繼日誌中。
第三步:SQL Thread會讀取中繼日誌,並順序執行該日誌中的SQL事件,從而與主數據庫中的數據保持一致。
讀寫分離是依賴於主從複製,而主從複製又是爲讀寫分離服務的。由於主從複製要求slave不能寫只能讀(若是對slave執行寫操做,那麼show slave status將會呈現Slave_SQL_Running=NO,此時你須要按照前面提到的手動同步一下slave)。
優勢:直接實現讀寫分離和負載均衡,不用修改代碼,master和slave用同樣的賬號,mysql官方不建議實際生產中使用
缺點:下降性能, 不支持事務
若是採用了mybatis, 能夠將讀寫分離放在ORM層,好比mybatis能夠經過mybatis plugin攔截sql語句,全部的insert/update/delete都訪問master庫,全部的select 都訪問salve庫,這樣對於dao層都是透明。 plugin實現時能夠經過註解或者分析語句是讀寫方法來選定主從庫。不過這樣依然有一個問題, 也就是不支持事務, 因此咱們還須要重寫一下DataSourceTransactionManager, 將read-only的事務扔進讀庫, 其他的有讀有寫的扔進寫庫。
缺點:類內部方法經過this.xx()方式相互調用時,aop不會進行攔截,需進行特殊處理。
視庫的大小來定,通常來講 100G 內的庫,能夠考慮使用 mysqldump 來作,由於 mysqldump更加輕巧靈活,備份時間選在業務低峯期,能夠天天進行都進行全量備份(mysqldump 備份出來的文件比較小,壓縮以後更小)。
100G 以上的庫,能夠考慮用 xtranbackup 來作,備份速度明顯要比 mysqldump 要快。通常是選擇一週一個全備,其他天天進行增量備份,備份時間爲業務低峯期。
物理備份恢復快,邏輯備份恢復慢
這裏跟機器,尤爲是硬盤的速率有關係,如下列舉幾個僅供參考
20G的2分鐘(mysqldump)
80G的30分鐘(mysqldump)
111G的30分鐘(mysqldump)
288G的3小時(xtra)
3T的4小時(xtra)
邏輯導入時間通常是備份時間的5倍以上
首先在恢復以前就應該作足準備工做,避免恢復的時候出錯。好比說備份以後的有效性檢查、權限檢查、空間檢查等。若是萬一報錯,再根據報錯的提示來進行相應的調整。
mysqldump 屬於邏輯備份。加入–single-transaction 選項能夠進行一致性備份。後臺進程會先設置 session 的事務隔離級別爲 RR(SET SESSION TRANSACTION ISOLATION LEVELREPEATABLE READ),以後顯式開啓一個事務(START TRANSACTION /!40100 WITH CONSISTENTSNAPSHOT /),這樣就保證了該事務裏讀到的數據都是事務事務時候的快照。以後再把表的數據讀取出來。若是加上–master-data=1 的話,在剛開始的時候還會加一個數據庫的讀鎖(FLUSH TABLES WITH READ LOCK),等開啓事務後,再記錄下數據庫此時 binlog 的位置(showmaster status),立刻解鎖,再讀取表的數據。等全部的數據都已經導完,就能夠結束事務
xtrabackup 屬於物理備份,直接拷貝表空間文件,同時不斷掃描產生的 redo 日誌並保存下來。最後完成 innodb 的備份後,會作一個 flush engine logs 的操做(老版本在有 bug,在5.6 上不作此操做會丟數據),確保全部的 redo log 都已經落盤(涉及到事務的兩階段提交
概念,由於 xtrabackup 並不拷貝 binlog,因此必須保證全部的 redo log 都落盤,不然可能會丟最後一組提交事務的數據)。這個時間點就是 innodb 完成備份的時間點,數據文件雖然不是一致性的,可是有這段時間的 redo 就可讓數據文件達到一致性(恢復的時候作的事
情)。而後還須要 flush tables with read lock,把 myisam 等其餘引擎的表給備份出來,備份完後解鎖。這樣就作到了完美的熱備。
使用 myisamchk 來修復,具體步驟:
使用repair table 或者 OPTIMIZE table命令來修復,REPAIR TABLE table_name 修復表 OPTIMIZE TABLE table_name 優化表 REPAIR TABLE 用於修復被破壞的表。 OPTIMIZE TABLE 用於回收閒置的數據庫空間,當表上的數據行被刪除時,所佔據的磁盤空間並無當即被回收,使用了OPTIMIZE TABLE命令後這些空間將被回收,而且對磁盤上的數據行進行重排(注意:是磁盤上,而非數據庫)
做者:ThinkWon,遵循CC 4.0 BY-SA版權協議
連接:https://blog.csdn.net/ThinkWo...