官方文檔https://www.mysql.com/cn/products/community/
小郭之前已經分享過幾篇MySql的部分概念學習筆記,本篇作為小郭學習Mysql的匯總篇,把之前的文章內容進行了匯總,并增加了許多MySql新的知識點。 同時小郭后續也會持續學習和補充更新本篇文章內容,有興趣的朋友可以點贊收藏+關注呀!
本篇默認讀者已有SQL的入門知識儲備
MySQL 是當下最流行的關系型數據庫管理系統,在 WEB 應用方面 MySQL 可以說是最流行的技術。選擇 MySQL 數據庫已是既成事實,絕大多數使用 Linux 操作系統的互聯網網站都在使用 MySQL 作為其后端的數據庫存儲方式,從大型的 BAT 門戶到電商平臺、分類門戶等無一例外。
原因可能有以下幾點:
接下來小郭將把MySql的常見知識點進行總結(部分知識點轉載于網絡)。
1)客戶端
本層所提供的服務并不是MySQL所獨有的技術。它們都是服務于C/S程序或者是這些程序所需要的,例如連接處理,身份驗證,安全性等等。
2)核心服務層
本層是MySQL的核心部分,也叫做 SQL Layer。
在 MySQL數據庫系統處理底層數據之前的所有工作都是在這一層完成的,包括權限判斷, sql解析,行計劃優化, query cache 的處理以及所有內置的函數(如日期,時間,數學運算,加密)等等。各個存儲引擎提供的功能都集中在這一層,如存儲過程,觸發器,視圖等。
3)存儲引擎
本層也叫做StorEngine Layer ,是底層數據存取操作實現的核心,由多種存儲引擎共同組成。
它們負責存儲和獲取所有存儲在MySQL中的數據。就像Linux眾多的文件系統 一樣,每個存儲引擎都有自己的優點和缺陷。核心服務層通過存儲引擎API來與它們交互的。這些API隱藏了各個存儲引擎不同的地方。對于服務層盡可能的透明。API包含了很多底層的操作,如開始一個事物,或者取出有特定主鍵的行。存儲引擎不能解析SQL,互相之間也不能通信。僅僅是簡單的響應核心服務層的請求。
以下是官網的體系結構圖:
1)客戶端連接器(Connectors)
這一層也可以認為是網絡鏈接層。
連接器為不同的客戶端程序提供與MySQL服務器的連接。 使得我們能夠連接和執行來自另一種語言或環境的MySQL語句,包括ODBC,Java(JDBC),Perl,Python,PHP,Ruby和本機C MySQL實例。
連接器實例:
2)系統管理和控制工具
系統管理和控制工具,比如MySQL安裝包bin目錄下提供的服務管理和工具(mysqld、mysql_safe、mysql.server等),還有備份恢復工具等等。
3)連接池
連接池主要負責連接管理、授權認證、安全等等。每個客戶端連接都對應著服務器上的一個線程。服務器上維護了一個線程池,避免為每個連接都創建銷毀一個線程。當客戶端連接到MySQL服務器時,服務器對其進行認證??梢酝ㄟ^用戶名與密碼認證,也可以通過SSL證書進行認證。登錄認證后,服務器還會驗證客戶端是否有執行某個查詢的操作權限。
由于每次建立連接需要消耗很多時間,連接池的作用就是將這些連接緩存下來,下次可以直接用已經建立好的連接,提升服務器性能。 連接池可以大大提高Java應用程序的性能,同時減少總體資源使用量。
4)SQL 接口
SQL Interface(SQL接口組件),接受用戶的SQL命令,并且返回用戶需要查詢的結果。比如select from就是調用SQL Interface
5)解析器
SQL命令傳遞到解析器的時候會被解析器驗證和解析。解析器是由Lex和YACC實現的,是一個很長的腳本。
在 MySQL中我們習慣將所有 Client 端發送給 Server 端的命令都稱為 query ,在 MySQL Server 里面,連接線程接收到客戶端的一個 Query 后,會直接將該 query 傳遞給專門負責將各種 Query 進行分類然后轉發給各個對應的處理模塊。
主要功能:
6)查詢優化器
SQL語句在查詢之前會使用查詢優化器對查詢進行優化。就是優化客戶端請求的 query(sql語句) ,根據客戶端請求的 query 語句,和數據庫中的一些統計信息,在一系列算法的基礎上進行分析,得出一個最優的策略,告訴后面的程序如何取得這個 query 語句的結果
7)緩存
8.0版本之前支持查詢緩存,8.0之后不支持了
主要功能是將客戶端提交 給MySQL 的 Select 類 query 請求的返回結果集 cache 到內存中,與該 query 的一個 hash 值 做一個對應。該 Query 所取數據的基表發生任何數據的變化之后, MySQL 會自動使該 query 的Cache 失效。在讀寫比例非常高的應用系統中, Query Cache 對性能的提高是非常顯著的。當然它對內存的消耗也是非常大的。
如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據。這個緩存機制是由一系列小緩存組成的。比如表緩存,記錄緩存,key緩存,權限緩存等
8)可插拔存儲引擎
存儲引擎是負責與與數據庫文件打交道。存儲引擎接口模塊可以說是 MySQL 數據庫中最有特色的一點了。目前各種數據庫產品中,基本上只有 MySQL 可以實現其底層數據存儲引擎的插件式管理。這個模塊實際上只是 一個抽象類,但正是因為它成功地將各種數據處理高度抽象化,才成就了今天 MySQL 可插拔存儲引擎的特色。
注意:存儲引擎是基于表的,而不是數據庫。
在MySQL中,存儲引擎是以插件的形式運行的。支持的引擎有十幾種之多,但我們實戰常用到的,大概只有InnoDB、MyISAM 、Memory、Archive 了,下面是這幾種引擎的區別:
MySQL 數據庫的官方網站為http://www.mysql.com,其發布的 MySQL 版本采用雙授權政策,和大多數開源產品的路線一樣,MySQL 數據庫也有社區版和企業版之分,且這兩個版本又各自分了四個版本依次發布,這四個版本分別為:Alpha 版、Beta 版、RC 版和 GA 版本。
生產環境應該選擇GA版本!
本文基于window64位操作系統+mysql5.7.43版本來進行陳述。
MySQL 安裝包獲取地址為:官方下載鏈接
國內下載地址為:國內下載鏈接
挑選對應的 MySQL Community Server 版本(社區版本,免費)及對應的操作系統。
安裝包下載后解壓到指定目錄(小郭這里是到D盤根目錄):
接下來我們需要配置下 MySQL 的配置文件
打開剛剛解壓的文件夾 D:\mysql-5.7.43-winx64\ ,在該文件夾下創建 my.ini 配置文件,編輯 my.ini 配置以下基本信息:
[client]
# 設置mysql客戶端默認字符集
default-character-set=utf8
[mysqld]
# 設置3306端口
port=3306
# 設置mysql的安裝目錄
basedir=D:\\mysql-5.7.43-winx64
# 設置 mysql數據庫的數據的存放目錄,MySQL 8+ 不需要以下配置,系統自己生成即可,否則有可能報錯
# datadir=D:\\mysql-5.7.43-winx64\\sqldata
# 允許最大連接數
max_connections=20
# 服務端使用的字符集默認為8比特編碼的latin1字符集
character-set-server=utf8
# 創建新表時將使用的默認存儲引擎
default-storage-engine=INNODB
接下來我們來啟動下 MySQL 數據庫:
以管理員身份打開 cmd 命令行工具,切換到bin目錄:
cd D:\mysql-5.7.43-winx64
初始化數據庫:
mysqld --initialize --console
執行完成后,會輸出 root 用戶的初始默認密碼:
A temporary password is generated for root@localhost: y?6lLRUr1aSh
上述密碼要記住,后續登錄要用到!
在bin目錄下,繼續執行下面命令(需管理員權限),安裝mysql服務到系統中
mysqld install
輸出如下日志,代表mysql服務安裝成功:
Service successfully installed.
繼續,啟動輸入以下命令即可:
net start mysql
當 MySQL 服務已經運行時, 我們可以通過用市面上的mysql客戶端進行連接和編寫sql, 也可以用MySQL 自帶的客戶端工具登錄到 MySQL 數據庫中, 下面演示自帶的客戶端工具的使用。
首先打開命令提示符, 輸入以下格式的命名:
//-h : 指定客戶端所要登錄的 MySQL 主機名, 登錄本機(localhost 或 127.0.0.1)該參數可以省略;
//-u : 登錄的用戶名;
//-p : 告訴服務器將會使用一個密碼來登錄, 如果所要登錄的用戶名密碼為空, 可以忽略此選項。
mysql -h 主機名 -u 用戶名 -p
示例:
mysql -u root -p
然后輸入安裝時生成的密碼即可完成登錄。
通過默認的密碼登錄上mysql后,發現總是提示:"You must reset your password using ALTER USER statement before executing this statement."
大意就是:在執行此語句之前,必須使用 ALTER USER 語句重置密碼。因第一次安裝,給的是隨機密碼,登陸成功后需要第一時間改成自己的密碼。
修改密碼有兩種方式:
# 登錄mysqlmysql -uroot -p# alter方式修改ALTER USER 'root'@'localhost' IDENTIFIED BY '123456'; //記得修改自己的賬戶
flush privileges; //修改成功后刷新權限
# set方式修改
set password for root@localhost = password('123456'); //記得修改自己的賬戶
flush privileges; //修改成功后刷新權限
更新密碼后再次執行MySQL相關語句操作即正常回顯。
接下來就可以進行正常的庫表操作了。
前面的安裝步驟特別繁瑣,也可以采用如今的容器化技術docker ,簡單幾個命令就可以安裝完畢。
通過docker快速安裝mysql
命令不區分大小寫
mysql -u username -p
實例:
mysql -u root -p
//服務器版本信息
SELECT VERSION( )
//當前數據庫名 (或者返回空)
SELECT DATABASE( )
//當前用戶名
SELECT USER( )
//服務器狀態
SHOW STATUS
//服務器配置變量
SHOW VARIABLES
//查看版本支持的存儲引擎
show engines;
1)創建數據庫
語法
CREATE DATABASE 數據庫名;
2)查詢數據庫
語法
SHOW DATABASES;
實例:
3)刪除數據庫
drop database <數據庫名>;
實例:
4)使用數據庫
use DATABASE;
實例:
語法
CREATE TABLE table_name (column_name column_type);
或者順便指定索引
CREATE TABLE table_name (
column1_name data_type,
column2_name data_type,
...,
INDEX index_name (column1 [ASC|DESC], column2 [ASC|DESC], ...)
);
實例:
CREATE TABLE IF NOT EXISTS `User`(
`user_id` INT UNSIGNED AUTO_INCREMENT,
`user_name` VARCHAR(100) NOT NULL,
`user_sex` VARCHAR(10) NOT NULL,
`insert_date` DATE,
PRIMARY KEY ( `user_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
語法
DROP TABLE table_name ;
實例:
DROP TABLE User;
在進行重要的結構修改時,建議先備份數據,并在生產環境中謹慎操作,因為修改時會影響到數據庫的性能和運行時間。
1)添加新字段
ALTER TABLE table_name
ADD column_name data_type;
2)修改字段類型
ALTER TABLE table_name
MODIFY column_name new_data_type;
3)修改字段名稱
ALTER TABLE table_name
CHANGE old_column_name new_column_name data_type;
4)刪除字段
ALTER TABLE table_name
DROP column_name;
5)添加主鍵約束
ALTER TABLE table_name
ADD PRIMARY KEY (column_name);
6)添加外鍵約束
ALTER TABLE table_name
ADD FOREIGN KEY (column_name) REFERENCES referenced_table(ref_column_name);
7)添加普通索引
ALTER TABLE table_name
ADD INDEX index_name (column1 [ASC|DESC], column2 [ASC|DESC], ...);
或者
CREATE INDEX index_name
ON table_name (column1 [ASC|DESC], column2 [ASC|DESC], ...);
8)添加唯一索引
ALTER TABLE table_name
ADD UNIQUE INDEX index_name (column1 [ASC|DESC], column2 [ASC|DESC], ...);
或者
CREATE UNIQUE INDEX index_name
ON table_name (column1 [ASC|DESC], column2 [ASC|DESC], ...);
9)刪除索引
ALTER TABLE table_name
DROP INDEX index_name;
或者
DROP INDEX index_name ON table_name;
10)重命名表
ALTER TABLE old_table_name
RENAME TO new_table_name;
11)修改表的存儲引擎
ALTER TABLE table_name ENGINE = new_storage_engine;
如果我們需要完全的復制MySQL的數據表,包括表的結構,索引,默認值等。 如果僅僅使用CREATE TABLE ... SELECT 命令,是無法實現的。下面介紹如何完全復制一張表
原始表是User,復制一個新的表叫User_2
1)獲取數據表User的建表語句。
命令:SHOW CREATE TABLE User;
2)修改SQL語句的數據表名為User_2,并執行SQL語句。
CREATE TABLE `user_2` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(100) NOT NULL,
`user_sex` varchar(10) NOT NULL,
`insert_date` date DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
此時表user_2的結構和原始表user完全一致,但是沒有數據。
3)使用INSERT INTO... SELECT 語句拷貝表中的數據
命令:
INSERT INTO User_2(user_id,user_name,user_sex,insert_date) select user_id,user_name,user_sex,insert_date from user;
通過上面三個步驟,就會完整的復制表的內容,包括表結構及表數據。
語法
INSERT INTO table_name ( field1, field2,...fieldN )
VALUES
( value1, value2,...valueN );
實例:
INSERT INTO User(user_name,user_sex,insert_date) VALUES('admin','男','2023-08-29');
語法
SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[LIMIT N][ OFFSET M]
實例:
SELECT user_name,user_sex FROM User WHERE user_name='admin' LIMIT 1;
語法
UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]
實例:
UPDATE User SET user_sex='女' where user_name='admin';
語法
DELETE FROM table_name [WHERE Clause]
實例:
DELETE FROM User WHERE user_name='admin';
語法
SELECT field1, field2,...fieldN
FROM table_name
WHERE field1 LIKE condition1 [AND [OR]] filed2 = 'somevalue'
LIKE 通常與 % 一同使用,類似于一個元字符的搜索
實例:
SELECT *FROM User WHERE user_name LIKE '%ad%';
語法
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT]
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];
UNION ALL: 返回所有結果集,包含重復數據。
UNION: 返回所有結果集,不包含重復數據。
語法
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
ORDER BY field1 [ASC [DESC][默認 ASC]], [field2...] [ASC [DESC][默認 ASC]]
實例:
//按插入時間升序
SELECT * from User ORDER BY insert_date ASC;
語法
SELECT column_name, function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
實例:
//按用戶名分組
SELECT user_name,count(1) from User GROUP BY user_name
我們可以在SELECT, UPDATE 和 DELETE 語句中使用 Mysql 的 JOIN 來聯合多表查詢。
語法:
SELECT a.x,b.y from a inner join b on a.id = b.a_id
MySQL中使用 REGEXP 操作符來進行正則表達式匹配。
實例:
//查找user_name字段中以'st'為開頭的所有數據:
SELECT user_name FROM User WHERE user_name REGEXP '^ad';
在 MySQL 中只有使用了 Innodb 數據庫引擎的數據庫或表才支持事務。
語法:
MYSQL 事務處理主要有兩種方法:
1、用 BEGIN, ROLLBACK, COMMIT來實現
BEGIN 開始一個事務
ROLLBACK 事務回滾
COMMIT 事務確認
2、直接用 SET 來改變 MySQL 的自動提交模式:
SET AUTOCOMMIT=0 禁止自動提交
SET AUTOCOMMIT=1 開啟自動提交
實例:
可以使用 SELECT ... INTO OUTFILE 語句導出數據
實例:
select *from user into outfile 'D:\mysql-5.7.43-winx64\outfile';
直接執行上述導出會報錯:
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
看字面意思是和--secure-file-pri這個變量有關系,我們看下這個變量的設置是什么:
show variables like '%secure%';
可以看到這個值是NULL, 查閱資料得知
secure-file-pri這個變量被用于限制數據導入的導出操作,諸如執行LOAD DATA以及SELECT ... INTO OUTFILE操作以及LOAD_FILE()函數。
這個secure_file_priv可以設置的值有三個可選項:
- If empty, the variable has no effect. This is not a secure setting. 【如果此配置項值為空,則表示沒有安全設置】
- 如果設置為目錄名,則服務器將限制導入和導出操作,使其僅適用于該目錄中的文件。該目錄必須存在;服務器將不會創建它。
- 如果設置為NULL,則服務器將禁用導入和導出操作。從MySQL 5.7.6開始允許使用此值
根據上述的NULL值,可以看到是不允許導出(入)到文件。
解決辦法就是在配置文件中,設置secure-file-priv為某個路徑即可,如下:
注意配置前一定要加一行[mysqld]
最后要想配置生效,需要重啟mysql服務,注意是重啟服務!不是退出重新登錄!
配置生效后,重新執行前面的導出命令即可導出數據
1)使用mysql命令導入
語法:
mysql -u用戶名 -p密碼 < 要導入的數據庫數據(data.sql)
以上命令將將備份的整個數據庫 data.sql 導入。
2)使用source命令導入
source 命令導入數據庫需要先登錄到數庫終端
導入步驟:
mysql> create database gyd; # 創建數據庫
mysql> use gyd; # 使用已創建的數據庫
mysql> set names utf8; # 設置編碼
mysql> source /home/gyd/gyd.sql # 導入備份數據庫
3)使用 LOAD DATA 導入數據
以下實例中將從當前目錄中讀取文件 outfile.txt ,將該文件中的數據插入到當前數據庫的 user 表中。
mysql> LOAD DATA LOCAL INFILE 'outfile.txt' INTO TABLE user;
LOAD DATA 默認情況下是按照數據文件中列的順序插入數據的,如果數據文件中的列與插入表中的列不一致,則需要指定列的順序。
如,在數據文件中的列順序是 user_sex,user_name, insert_date,但在插入表的列順序為user_name, user_sex, insert_date,則數據導入語法如下:
mysql> LOAD DATA LOCAL INFILE 'outfile.txt'
-> INTO TABLE user (user_name, user_sex, insert_date);
4)使用 mysqlimport 導入數據
$ mysqlimport -u root -p --local user outfile.txt
password *****
mysqlimport有很多可選項,有興趣的可以自行查資料啦!
FLUSH QUERY CACHE : 清理查詢緩存內存碎片
RESET QUERY CACHE : 從查詢緩存中移出所有查詢
FLUSH TABLES : 關閉所有打開的表,同時該操作將會清空查詢緩存中的內容。
語法:
SHOW STATUS LIKE 'value';
value參數的幾個統計參數如下 :
Connections : 連接 MySQL 服務器的次數
Uptime : MySQL 服務器的上線時間
Slow_queries : 慢查詢次數
Com_Select : 查詢操作的次數
Com_insert : 插入操作的次數
Com_update : 更新操作的次數
Com_delete : 刪除操作的次數
mysql還有很多命令,小郭會慢慢的補充進來。
MySQL 支持多種類型,大致可以分為三類:數值、日期/時間和字符串(字符)類型。
記錄mysql常用的統計寫法,來源于網絡,原文鏈接:https://blog.csdn.net/fwj_ntu/article/details/86680053
1)按天統計
format參數的取值為’%y%m%d’,可以按天輸出統計結果。
SELECT DATE_FORMAT(insertTime,'%y年%m月%d日') as d,count(1)
FROM table
GROUP BY DATE_FORMAT(insertTime,'%y%m%d')
ORDER BY d asc;
2)按自然周統計
format()函數的format參數取值為’%y%u’時,可實現按年、年中的周來統計結果。如果在where條件中限制是某一年的周期,可以直接將format參數的值配置為’%u’,否則一定要用’%y%u’,不然會把不同年的第n周合并到一起而出現錯亂。
SELECT DATE_FORMAT(insertTime,'%y年%u周') as w,min(insertTime) as st,count(1)
FROM table
GROUP BY DATE_FORMAT(insertTime,'%y%u')
ORDER BY w asc;
3)按月統計
format()函數的format參數值為’%y%m’時,可實現按月份輸出聚合結果。
SELECT DATE_FORMAT(insertTime,'%y年%m月') as m,count(1)
FROM table
GROUP BY DATE_FORMAT(insertTime,'%y%m')
ORDER BY m asc
4)按季度統計
date_format()函數沒有直接按照季節輸出結果的功能,但這對于數據分析師并不是什么難事,自己利用月度聚合結果去加工以下即可:
SELECT FLOOR((DATE_FORMAT(insertTime,'%m')-1)/3)+1 as q,min(insertTime) as st,count(*)
FROM table
WHERE DATE_FORMAT(insertTime,'%Y') = 2023
GROUP BY FLOOR((DATE_FORMAT(insertTime,'%m')-1)/3)+1
ORDER BY q asc;
5)按年份統計
date_format()函數的format參數值為’%Y’或’%y’時可實現按年份輸出統計結果。
SELECT DATE_FORMAT(insertTime,'%Y') as y,count(1)
FROM table
GROUP BY DATE_FORMAT(insertTime,'%Y')
ORDER BY y asc;
原文鏈接: https://cloud.tencent.com/developer/article/1981543
下面是Select的執行流程圖:
整個查詢執行過程,總的來說分為 6 個步驟 :
客戶端向 MySQL 服務器發送一條查詢請求,與連接池交互:連接池認證相關處理。
建立與 MySQL 的連接,這就是由連接器Connectors來完成的。連接器Connectors負責跟客戶端建立連接、獲取權限、維持和管理連接。
連接命令為: mysql -h localhost -P 3306 -u user -p password
驗證通過后,連接器會到權限表里面查出登錄用戶擁有的權限,之后這個連接里面的權限判斷邏輯,都將依賴于此時讀到的權限,一個用戶成功建立連接后,即使管理員對這個用戶的權限做了修改,也不會影響已經存在連接的權限,修改完后,只有再新建的連接才會使用新的權限設置。
連接完成后,如果沒有后續的動作,這個連接就處于空閑狀態,可以在mysql服務端控制臺執行 show processlist 命令中看到已建立的客戶端連接。
客戶端系統的連接,可以通過在客戶端控制臺執行’netstat -natp|grep 端口‘來查看, 端口就是在MYSQL服務端查到的連接信息里的端口:
客戶端如果太長時間沒動靜,連接器就會自動將它斷開;這個時間是由參數 wait_timeout 控制的,默認值是8小時。如果在連接被斷開之后,客戶端再次發送請求的話,就會收到一個錯誤提醒:Lost connection to MySQL server during query。
服務器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲在緩存中的結果,否則進入下一階段
在解析一個查詢語句前,如果查詢緩存是打開的,那么 MySQL 會檢查這個查詢語句是否命中查詢緩存中的數據。如果當前查詢恰好命中查詢緩存,在檢查一次用戶權限后直接返回緩存中的結果。這種情況下,查詢不會被解析,也不會生成執行計劃,更不會執行。MySQL將緩存存放在一個引用表 (不要理解成table,可以認為是類似于 HashMap 的數據結構),通過一個哈希值索引,這個哈希值通過查詢本身、當前要查詢的數據庫、客戶端協議版本號等一些可能影響結果的信息計算得來。所以兩個查詢在任何字符上的不同 (例如 : 空格、注釋),都會導致緩存不會命中
如果查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、MySQL庫中的系統表,其查詢結果都不會被緩存。比如函數 NOW() 或者 CURRENT_DATE() 會因為不同的查詢時間,返回不同的查詢結果,再比如包含 CURRENT_USER 或者 CONNECION_ID() 的查詢語句會因為不同的用戶而返回不同的結果,將這樣的查詢結果緩存起來沒有任何的意義
MySQL 查詢緩存系統會跟蹤查詢中涉及的每個表,如果這些表 (數據或結構) 發生變化,那么和這張表相關的所有緩存數據都將失效。正因為如此,在任何的寫操作時,MySQL必須將對應表的所有緩存都設置為失效。如果查詢緩存非常大或者碎片很多,這個操作就可能帶來很大的系統消耗,甚至導致系統僵死一會兒,而且查詢緩存對系統的額外消耗也不僅僅在寫操作,讀操作也不例外 :
基于此,并不是什么情況下查詢緩存都會提高系統性能,緩存和失效都會帶來額外消耗,特別是寫密集型應用,只有當緩存帶來的資源節約大于其本身消耗的資源時,才會給系統帶來性能提升。可以嘗試打開查詢緩存,并在數據庫設計上做一些優化 :
//先查詢緩存
mysql> SELECT SQL_CACHE COUNT(*) FROM a;
//跳過緩存直接查詢實時表
mysql> SELECT SQL_NO_CACHE COUNT(*) FROM a;
查看開啟緩存的情況,可以知道query_cache_size的設置是否合理
如果查詢緩存未命中,就要開始執行語句了。首先,MySQL 需要對 SQL 語句進行SQL解析(詞法語法)、預處理。
1)詞法分析
SQL語句是由多個字符串和空格組成的,MySQL 需要識別出里面的字符串分別是什么,代表什么。
MySQL 從輸入的"select"這個關鍵字識別出來,這是一個查詢語句。它也要把字符串“user_info”識別成“表名 user_info”,
把字符串“id ”識別成“列 id ”
2)語法分析
根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這SQL語句是否滿足 MySQL 語法。
如果提交的SQL語句不對,就會收到 You have an error in your SQL syntax 的錯誤提醒,比如下面這個語句 from 寫成了 form。
mysql> select * form user_info where id = 1;
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'form user_info where id = 1' at line 1
一般語法錯誤會提示第一個出現錯誤的位置,所以要關注的是緊接 use near 的內容。
經過前面的步驟生成的語法樹被認為是合法的了,并且由優化器將其轉化成查詢計劃。
多數情況下,一條查詢可以有很多種執行方式,最后都返回相應的結果,優化器的作用就是找到這其中最好的執行計劃.
MySQL使用基于成本的優化器,它嘗試預測一個查詢使用某種執行計劃時的成本,并選擇其中成本最小的一個。在 MySQL 可以通過查詢當前會話的 last_query_cost 的值來得到其計算當前查詢的成本
mysql> SELECT * FROM p_product_fee WHERE total_price BETWEEN 580000 AND 680000;
mysql> SHOW STATUS LIKE 'last_query_cost'; # 顯示要做多少頁的隨機查詢才能得到最后一查詢結果,這個結果是根據一些列的統計信息計算得來的,這些統計信息包括 : 每張表或者索引的頁面個數、索引的基數、索引和數據行的長度、索引的分布情況等等
有非常多的原因會導致 MySQL 選擇錯誤的執行計劃,比如統計信息不準確、不會考慮不受其控制的操作成本(用戶自定義函數、存儲過程)、MySQL認為的最優跟我們想的不一樣 (我們希望執行時間盡可能短,但 MySQL 值選擇它認為成本小的,但成本小并不意味著執行時間短) 等等
MySQL的查詢優化器是一個非常復雜的部件,它使用了非常多的優化策略來生成一個最優的執行計劃 :
比如你執行下面這樣的語句,這個語句是執行兩個表的 join:
mysql> SELECT * FROM order_master JOIN order_detail USING (order_id) WHERE order_master.pay_status = 0 AND order_detail.detail_id = 1558963262141624521;
既可以先從表 order_master 里面取出 pay_status = 0 的記錄的 order_id 值,再根據 order_id 值關聯到表 order_detail,再判斷 order_detail 里面 detail_id 的值是否等于 1558963262141624521。
也可以先從表 order_detail 里面取出 detail_id = 1558963262141624521 的記錄的 order_id 值,再根據 order_id 值關聯到 order_master,再判斷 order_master 里面 pay_status 的值是否等于 0。
這兩種執行方法的邏輯結果是一樣的,但是執行的效率會有不同,而優化器的作用就是決定選擇使用哪一個方案。優化器階段完成后,這個語句的執行方案就確定下來了,然后進入執行器階段。
在完成解析和優化階段以后,MySQL會生成對應的執行計劃,查詢執行引擎根據執行計劃給出的指令逐步執行得出結果。整個執行過程的大部分操作均是通過調用存儲引擎實現的接口來完成,這些接口被稱為 handler API。查詢過程中的每一張表由一個 handler 實例表示。實際上,MySQL在查詢優化階段就為每一張表創建了一個 handler實例,優化器可以根據這些實例的接口來獲取表的相關信息,包括表的所有列名、索引統計信息等。存儲引擎接口提供了非常豐富的功能,但其底層僅有幾十個接口,這些接口像搭積木一樣完成了一次查詢的大部分操作
開始執行SQL語句:mysql> select * from user_info where id = 1;
1)判斷是否有查詢權限有就繼續執行沒有就返回權限錯誤。
例如判斷當前連接對這個表 user_info 有沒有執行查詢的權限,如果沒有,就會返回沒有權限的錯誤。錯誤如下(如果命中查詢緩存,會在查詢緩存返回結果的時候,做權限驗證。查詢也會在優化器之前調用 precheck 驗證權限)。
ERROR 1142 (42000): SELECT command denied to user 'appusser'@'localhost' for table 'user_info'
2)執行器根據表的引擎定義去掉用引擎接口
如果有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。
對于沒有有索引的表使用全表掃描API: 比如我們這個例子中的表 user_info 中,id 字段沒有索引,那么執行器的執行流程是這樣的:
A.調用 InnoDB 引擎接口取這個表的第一行,判斷 id 值是不是 1,如果不是則跳過,如果是則將這行存在結果集中;
B.調用引擎接口取下一行,重復相同的判斷邏輯,直到取到這個表的最后一行。
C.執行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結果集返回給客戶端。
全表掃描接口:
//初始化全表掃描
virtual int rnd_init (bool scan);
//從表中讀取下一行
virtual int rnd_next (byte* buf);
對于有索引的表,使用索引相關接口:
A.第一次調用讀取索引第一條內容接口(ha_index_first)。
B.之后循環取滿足索引條件的下一行接口(ha_index_next)。
通過索引訪問table內容:
//使用索引前調用該方法
int ha_foo::index_init(uint keynr, bool sorted)
//使用索引后調用該方法
int ha_foo::index_end(uint keynr, bool sorted)
//讀取索引第一條內容
int ha_index_first(uchar * buf);
//讀取索引下一條內容
int ha_index_next(uchar * buf);
//讀取索引前一條內容
int ha_index_prev(uchar * buf);
//讀取索引最后一條內容
int ha_index_last(uchar * buf);
//給定一個key基于索引讀取內容
int index_read(uchar * buf, const uchar * key, uint key_len, enum ha_rkey_function find_flag)
數據庫的慢查詢日志中有 rows_examined 字段,表示這個語句執行過程中掃描了多少行。這個值就是在執行器每次調用引擎獲取數據行的時候累加的。在有些場景下,執行器調用一次,在引擎內部則掃描了多行,因此引擎掃描行數跟 rows_examined 并不是完全相同的。
查詢執行的最后一個階段就是將結果返回給客戶端。即使查詢不到數據,MySQL 仍然會返回這個查詢的相關信息,比如該查詢影響到的行數以及執行時間等。如果查詢緩存被打開且這個查詢可以被緩存,MySQL也會將結果存放到緩存中。結果集返回客戶端是一個增量且逐步返回的過程。有可能 MySQL 在生成第一條結果時,就開始向客戶端逐步返回結果集。這樣服務端就無須存儲太多結果而消耗過多內存,也可以讓客戶端第一時間獲得返回結果。需要注意的是,結果集中的每一行都會以一個滿足客戶端/服務器通信協議的數據包發送,再通過 TCP協議 進行傳輸,在傳輸過程中,可能對 MySQL 的數據包進行緩存然后批量發送
查詢語句的執行流程,更新語句也會同樣的走一遍。sql= update T set c=c+1 where id=2
大概的執行步驟是如下:
1)客戶端向 MySQL 服務器發送一條更新請求
2)清除表查詢緩存,跟這個有關的查詢緩存會失效。這就是一般不建議使用查詢緩存的原因。
3)分析器進行 SQL解析(詞法和語法分析),分析這是一條更新語句和。
4)優化器生成對應的執行計劃,優化器決定使用ID這個索引;
5)執行器負責更新,找到這一行,然后進行更新:
a.取數據行: 執行器先找引擎取 ID=2 這一行: ID 是主鍵,引擎直接用樹搜索找到這一行。如果 ID=2 這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然后再返回。)
b.更新數據: 執行器拿到引擎給的行數據,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。
c.更新內存: 引擎將這行新數據更新到內存中,
d.更新 redo log :同時將這個更新操作記錄到 redo log 里面,此時 redo log 處于 prepare 狀態。然后告知執行器執行完成了,隨時可以提交事務。
e.寫入binlog:執行器生成這個操作的 binlog,并把 binlog 寫入磁盤。
f.提交事務: 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。
與查詢流程不同的是,更新流程涉及兩個重要日志模塊:redo log(重做日志)和 binlog(歸檔日志)。redo log是InnoDB存儲引擎層的日志,binlog是MySQL Server層記錄的日志, 兩者都是記錄了某些操作的日志(不是所有)自然有些重復(但兩者記錄的格式不同)。
redo log在數據庫重啟恢復的時候被使用,因為其屬于物理日志的特性,恢復速度遠快于邏輯日志。而binlog和undo log屬于的邏輯日志。
物理日志和邏輯日志的區別:
索引知識的水很深,小郭專門寫了一篇文章總結: 索引概念詳解
一、MySQL
優點:
體積小、速度快、總體擁有成本低,開源;
支持多種操作系統;
是開源數據庫,提供的接口支持多種語言連接操作 ;
MySQL的核心程序采用完全的多線程編程。線程是輕量級的進程,它可以靈活地為用戶提供服務,而不過多的系統資源。用多線程和C語言實現的mysql能很容易充分利用CPU;
MySql有一個非常靈活而且安全的權限和口令系統。當客戶與MySql服務器連接時,他們之間所有的口令傳送被加密,而且MySql支持主機認證;
支持ODBC for Windows, 支持所有的ODBC 2.5函數和其他許多函數, 可以用Access連接MySql服務器, 使得應用被擴展;
支持大型的數據庫, 可以方便地支持上千萬條記錄的數據庫。作為一個開放源代碼的數據庫,可以針對不同的應用進行相應的修改;
擁有一個非??焖俣曳€定的基于線程的內存分配系統,可以持續使用面不必擔心其穩定性;
MySQL同時提供高度多樣性,能夠提供很多不同的使用者介面,包括命令行客戶端操作,網頁瀏覽器,以及各式各樣的程序語言介面,例如C+,Perl,Java,PHP,以及Python。你可以使用事先包裝好的客戶端,或者干脆自己寫一個合適的應用程序。MySQL可用于Unix,Windows,以及OS/2等平臺,因此它可以用在個人電腦或者是服務器上。
缺點:
不支持熱備份;
MySQL最大的缺點是其安全系統,主要是復雜而非標準,另外只有到調用mysqladmin來重讀用戶權限時才發生改變;
沒有一種存儲過程(Stored Procedure)語言,這是對習慣于企業級數據庫的程序員的最大限制;
MySQL的價格隨平臺和安裝方式變化。Linux的MySQL如果由用戶自己或系統管理員而不是第三方安裝則是免費的,第三方案則必須付許可費。Unix或linux 自行安裝 免費 、Unix或Linux 第三方安裝 收費。
二、SQL Server
優點:
易用性、適合分布式組織的可伸縮性、用于決策支持的數據倉庫功能、與許多其他服務器軟件緊密關聯的集成性、良好的性價比等;
為數據管理與分析帶來了靈活性,允許單位在快速變化的環境中從容響應,從而獲得競爭優勢。從數據管理和分析角度看,將原始數據轉化為商業智能和充分利用Web帶來的機會非常重要。作為一個完備的數據庫和數據分析包,SQLServer為快速開發新一代企業級商業應用程序、為企業贏得核心競爭優勢打開了勝利之門。作為重要的基準測試可伸縮性和速度獎的記錄保持者,SQLServer是一個具備完全Web支持的數據庫產品,提供了對可擴展標記語言 (XML)的核心支持以及在Internet上和防火墻外進行查詢的能力;
缺點:
開放性 :SQL Server 只能windows上運行沒有絲毫開放性操作系統系統穩定對數據庫十分重要Windows9X系列產品偏重于桌面應用NT server只適合小型企業而且windows平臺靠性安全性和伸縮性非常有限象unix樣久經考驗尤其處理大數據庫。
伸縮性并行性 :SQL server 并行實施和共存模型并成熟難處理日益增多用戶數和數據卷伸縮性有限。
安全性:沒有獲得任何安全證書。
性能 :SQL Server 多用戶時性能佳 。
客戶端支持及應用模式: 客戶端支持及應用模式。只支持C/S模式,SQL Server C/S結構只支持windows客戶用ADO、DAO、OLEDB、ODBC連接。
使用風險:SQL server 完全重寫代碼經歷了長期測試斷延遲許多功能需要時間來證明并十分兼容。
三、Oracle
優點:
開放性:Oracle 能所有主流平臺上運行(包括 windows)完全支持所有工業標準采用完全開放策略使客戶選擇適合解決方案對開發商全力支持。
可伸縮性,并行性:oracle 并行服務器通過使組結點共享同簇工作來擴展windownt能力提供高用性和高伸縮性簇解決方案windowsNT能滿足需要用戶把數據庫移UNIXOracle并行服務器對各種UNIX平臺集群機制都有著相當高集成度。
安全性:獲得最高認證級別的ISO標準認證。
性能:Oracle 性能高 保持開放平臺下TPC-D和TPC-C世界記錄。
客戶端支持及應用模式:Oracle 多層次網絡計算支持多種工業標準用ODBC、JDBC、OCI等網絡客戶連接 。
使用風險:Oracle 長時間開發經驗完全向下兼容得廣泛應用地風險低。
缺點:
對硬件的要求很高;
價格比較昂貴;
管理維護麻煩一些;
操作比較復雜,需要技術含量較高。
以上是小編今天對三大數據庫的觀點,歡迎大家留言討論
最后針對于java程序員價值上萬架構視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發等架構技術資料),希望能幫助到且找到一個好的工作,也節省大家在網上搜索資料的時間來學習,也可以關注我一下以后會有更多干貨分享。要資料請私信回復:獲取免費資料??!