1. 程式人生 > 實用技巧 >MySQL筆記彙總

MySQL筆記彙總

目錄

1 MySQL背景介紹

1.1 關於MySQL

官方文件:https://dev.mysql.com/doc/refman/8.0/en/

MySQL是Oracle公司開發、釋出和支援的最流行的開源SQL資料庫管理系統。

【主要特點】

  • 開源
  • 使用BTree索引
  • 支援多執行緒,對多核CPU效能可以達到更好的發揮
  • 用C和C++編寫

1.2 MySQL8.0新特性

  • 資料字典

    詳情參考:https://cloud.tencent.com/developer/article/1123363

    資料字典存放MySQL元資訊:表結構、資料庫名或表名、欄位的資料型別、檢視、索引、表字段資訊、儲存過程、觸發器等。

    新版本改進:

    • 將所有原先存放於資料字典檔案中的資訊,全部存放到資料庫系統表中(檔案-->表)【提升查詢資料速度】
    • 對INFORMATION_SCHEM,mysql,sys系統庫中的儲存引擎做了改進,原先使用MyISAM儲存引擎的資料字典表都改為使用InnoDB儲存引擎存放。
  • 更換新的身份認證外掛caching_sha2_password【預設使用】,但由於與客戶端相容性不太好,大多數使用者回退到了mysql_native_password版本

  • Innodb增強:

    • 自增列【消除了以往重啟例項自增列不連續的問題】

    • 可禁用死鎖檢測

      一個新的動態變數,innodb_deadlock_detect,可用於禁用死鎖檢測。在高併發性系統上,當多個執行緒等待同一鎖時,死鎖檢測會導致減速。有時,禁用死鎖檢測並依賴於innodb_lock_wait_timeout在發生死鎖時設定事務回滾。

2 CentOS 7.6 安裝MySQL

2.1 環境準備

首先centos7 已經不支援mysql(大概是因為收費),所以內部集成了mariadb,而安裝mysql的話會和mariadb的檔案衝突,所以需要先解除安裝掉mariadb

  • 解除安裝mariadb
rpm -qa | grep mariadb

rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64

centos7 內部集成了mariadb,而安裝mysql的話會和mariadb的檔案衝突,所以需要先解除安裝掉mariadb。

【如果之前安裝了mysql需要先解除安裝】

yum remove mysql*

刪除安裝目錄

whereis  mysql

rm -rf /usr/share/mysql
  • 安裝MySQL

    • 獲取yum源(MySQL官網)

    • 安裝yum源

      rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
      
    • 檢視各版本啟動狀況

      yum repolist all | grep mysql
      

      預設開啟最新版8.0

    • [調整命令】禁用8.0,開啟5.7

      yum-config-manager --disable mysql80-community
      yum-config-manager --enable mysql57-community
      

      命令在yum-utils 包裡,安裝既可以解決無法找到yum-config-manager命令:

      yum -y install yum-utils
      
    • 安裝mysqll

      yum -y install mysql-community-server
      

2.2 配置MySQL遠端連線

  • 檢視mysql版本

    mysql -V
    
  • 啟動mysql&&設定開機自啟

    systemctl start mysqld
    systemctl enable mysqld
    
  • 檢視預設生成密碼

    grep 'temporary password' /var/log/mysqld.log
    
  • 登入修改密碼

    mysql -uroot -p
    
  • 修改密碼

    ALTER USER 'root'@'localhost' IDENTIFIED BY 'NoPassword564925080!';
    
    mysql5.7之後預設安裝了密碼安全檢查外掛(validate_password),預設密碼檢查策略要求密碼必須包含:大小寫字母、數字和特殊符號,並且長度不能少於8位。否則會提示ERROR 1819 (HY000): Your password does not satisfy the current policy requirements錯誤.
    
  • 授權遠端登入使用者

    預設的密碼加密方式是:caching_sha2_password,而現在很多客戶端工具還不支援這種加密認證方式,連線測試的時候就會報錯:client does not support authentication protocol requested by server; consider upgrading MySQL client

    CREATE USER 'noneplus'@'%' IDENTIFIED BY 'Noneplus564925080!';
    
    GRANT ALL ON *.* TO 'noneplus'@'%';
    
    //修改認證方式為mysql_native_password
    ALTER USER 'noneplus'@'%' IDENTIFIED WITH mysql_native_password BY 'Noneplus564925080!';
    
    flush privileges;
    
  • 開放伺服器3306埠

  • 遠端連線

3 MySQL基礎知識儲備

3.1 常用命令

登入

mysql -u root -p

資料庫相關命令

show databases;

create database database_name;

use database_name;

drop database_name;

資料庫表相關命令

【資料庫和資料庫表相關命令都屬於DDL資料定義語言】

show tables;     [先切換到指定資料庫]

//建立表
CREATE TABLE `user_info` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '使用者id',
  `username` varchar(10) DEFAULT NULL COMMENT '使用者姓名',
  `password` varchar(20) DEFAULT NULL COMMENT '使用者密碼',
  `age` int(5) DEFAULT NULL COMMENT '年齡',
  `email` varchar(20) DEFAULT NULL COMMENT '郵箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='使用者資訊表'


//查看錶定義
show create table user_info \G;

drop table user_info;

//刪除表字段
alter table user_info column age;

//修改表
Alter table user_info modify username varchar(15);

//增加表的欄位
alter  table user_info add column gender int(1);

//欄位改名
alter table user_info change age age1 int(3);

//modify,add,change都可以進行排序

Alter table user_info modify username varchar(15) first;  //放在最前面

alter  table user_info add column gender int(1) after age;//欄位至於age之後

//修改表名

alter table user_info rename person_info;

增刪改查命令

【增刪改查屬於DML資料操作語言】

插入

INSERT INTO user_info(username,password,age,email) VALUES('hq','123456789',22,'[email protected]')

更新

UPDATE user_info SET username='hq',age=23,email='[email protected]' WHERE id=5

刪除

DELETE FROM user_info WHERE id=6

查詢

SELECT * FROM user_info WHERE id = 6
SELECT * FROM user_info WHERE id = 6 and age<30

排序【預設升序】

SELECT * FROM user_info ORDER BY ID DESC LIMIT 10   //查詢最後十條資料
SELECT * FROM user_info ORDER BY ID DESC LIMIT 10,20   //查詢最後20條資料的前10條

統計資料總條數

SELECT COUNT(1) FROM user_info;

統計最大值,最小值,求和

SELECT MAX(age),MIN(age) ,SUM(age) FROM user_info;

表連線查詢

select ename,deptname from emp,dept where emp.deptno=dept.deptno;

3.2 常用資料型別

數值型別

整數型別 位元組 最小值 最大值
tinyint 1 有符號-128 無符號0 有符號127 無符號255
smallint 2 有符號-32768 無符號0 有符號32767 無符號65535
mediumint 3 有符號-800w 無符號0 有符號800w 無符號167w
int,integer 4 有符號-21億 無符號0 有符號21億 無符號42億
bigint 8 有符號-92w兆 無符號0 有符號92w兆 無符號184w兆

int(5)指定顯式寬度【不顯式指定預設int(11)】,當數值寬度小於五位的時候,預設填滿。zerofill指用0填充。

unsigned表示是否帶符號

浮點數型別 位元組 最小值 最大值
float 4
double 8
定點數型別 位元組 最小值 最大值
decimal(M,D) M+2 有符號-128 無符號0 有符號127 無符號255

表示一共顯示M位數字,包括整數位和小數位,其中D位代表小數點有幾位

decimal不指定精度預設整數位為10,小數位為0.

日期時間型別

型別 位元組 最小值 最大值
date 4 1000-01-01 9999-12-31
datetime 8 1000-01-01 00:00:00 9999-12-31 23:59:59
timestamp 4 1970010108001 2038年的某個時刻

記錄系統當前時間可用timestamp,支援不同地方的時區差異

TIMESTAMP 儲存的時間範圍 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07

字串型別

型別 描述
char 0-255位元組
varchar 0-65535位元組
tinyblob 0-255位元組
blob 0-65535位元組
mediumblob 0-16772150位元組
longblob 0-4294967295位元組
tinytext 0-255位元組
text 0-65535位元組
mediumtext 0-16772150位元組
longtext 0-4294967295位元組
varbinary(M) 0-M位元組
binary(M) 0-M位元組

3.3 運算子

算術運算子

算術運算子 說明
+ 加法運算
- 減法運算
* 乘法運算
/ 除法運算,返回商
% 求餘運算,返回餘數

比較運算子

比較運算子 說明
= 等於
< 小於
<= 小於等於
> 大於
>= 大於等於
<=> 安全的等於,不會返回 UNKNOWN
<> 或!= 不等於
IS NULL 或 ISNULL 判斷一個值是否為 NULL
IS NOT NULL 判斷一個值是否不為 NULL
LEAST 當有兩個或多個引數時,返回最小值
GREATEST 當有兩個或多個引數時,返回最大值
BETWEEN AND 判斷一個值是否落在兩個值之間
IN 判斷一個值是IN列表中的任意一個值
NOT IN 判斷一個值不是IN列表中的任意一個值
LIKE 萬用字元匹配
REGEXP 正則表示式匹配

邏輯運算子

邏輯運算子 說明
NOT 或者 ! 邏輯非
AND 或者 && 邏輯與
OR 或者 || 邏輯或
XOR 邏輯異或【相同為0,不同為1】

位運算子

位運算子 說明
| 按位或
& 按位與
^ 按位異或
<< 按位左移
>> 按位右移
~ 按位取反,反轉所有位元

運算子優先順序

優先順序由低到高排列 運算子
1 =(賦值運算)、:=
2 II、OR
3 XOR
4 &&、AND
5 NOT
6 BETWEEN、CASE、WHEN、THEN、ELSE
7 =(比較運算)、<=>、>=、>、<=、<、<>、!=、 IS、LIKE、REGEXP、IN
8 |
9 &
10 <<、>>
11 -(減號)、+
12 *、/、%
13 ^
14 -(負號)、〜(位反轉)
15 !

4 開發規範

4.1 設計規範

正規化

  • 第一正規化:無重複的列

  • 第二正規化:屬性完全依賴於主鍵

  • 第三正規化:屬性不能傳遞依賴其他非主屬性

正規化的作用是避免資料冗餘(資料重複)。

正規化的問題

按照正規化設計出來的表在資料冗餘的問題雖然得到解決,但是會生成許多表,導致了表數量的複雜性,其二,查詢資料的時候,多表查詢的時間遠遠高於單表查詢的時間。

反正規化

正規化的目的是減小資料冗餘,而反正規化指的是在一定程度上允許資料冗餘,目的是加快資料操作。

對比

正規化與反正規化是一場時間和空間的較量,滿足正規化節省空間,滿足反正規化加快操作速度。

在滿足正規化設計資料庫的前提條件下,再根據具體的業務需求完成反正規化的設計。

4.2 命名規範

小寫+下劃線,不能使用保留關鍵字【!!!】

【MySQL物件名預設規定大小寫敏感,且在生產環境中MySQL通常執行在Linux系統下,Linux系統本身也是大小寫敏感的。】

https://dev.mysql.com/doc/mysqld-version-reference/en/keywords-8-0.html建議在設計資料表之後逐一排查有沒有使用關鍵字。】

4.3 欄位規範

原則:

  • 儘可能選擇儲存空間最小的欄位【栗子:IP轉化為整型儲存】、
  • 非負型資料優先使用無符號儲存

1,char VS varchar

​ char 定長 浪費空間 查詢速度快

​ varchar 變長 節省空間 查詢速度較慢

出於儲存空間的考慮,優先選擇varchar

2,避免使用text,blob,如果一定要使用,單獨出擴充套件表(通常這類資料會考慮使用NoSQL來儲存)

​ 【MySQL記憶體臨時表不支援text,blob這樣的大資料型別,只能使用磁碟臨時表完成,並且會導致二次查詢】

3,同財務相關的最好使用定點數decimal

4,日期型別選擇

  • DATETIME:記錄年月日時分秒,表示的時間範圍最大
  • 如果記錄的日期要讓不同時區的人使用,使用TIMESTAMP

5 B+樹索引

5.1 什麼是索引?

索引是一種資料結構,具體表現在查詢演算法上。

5.2 索引目的

提高查詢效率

【類比字典和借書】

如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然後從下往下找到y字母,再找到剩下的sql。如果沒有索引,那麼你可能需要把所有單詞看一遍才能找到你想要的。

去圖書館借書也是一樣,如果你要借某一本書,一定是先找到對應的分類科目,再找到對應的編號,這是生活中活生生的例子,通用索引,可以加快查詢速度,快速定位。

5.3 B樹

結構特徵:每個節點可包含多個子節點,葉子節點位於同一層(每個節點儲存索引和資料)

使用用法:B樹為磁碟預讀設計,其特徵相對於二叉樹降低了高度,減少IO次數(樹的高度等於IO次數)

5.3 B+樹

結構特徵:只在葉子節點儲存資料,且葉子節點有序排列,通過鏈指標相連(只有葉子節點儲存資料,其他節點都只儲存索引,單次IO能載入更多節點)

使用用法:B樹解決了磁碟IO問題,而B+樹通過資料結構優化和區間訪問加快了元素的查詢效率

5.4 原理分析

索引儲存位置

索引本身也很大,所以儲存在磁碟中,需要載入到記憶體中執行。

故:索引結構優劣標準:磁碟I/O次數

區域性性原理和磁碟預讀

區域性性原理:當一個數據被用到,其附近的資料很可能會馬上用到

磁碟預讀:由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要儘量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入主存。

B樹如何利用磁碟預讀功能

B樹的節點大小和磁碟的IO大小是進行過匹配的,一次IO可以讀取一整個節點的大小。這樣就能有效減少IO次數。

【如果節點大小和B樹大小不對齊,那麼同一頁節點可能需要兩次IO讀取】

綜上所述,B樹解決的核心問題是IO次數的問題

為什麼B+樹比B樹更適合作為索引結構

B樹解決了磁碟IO的問題但沒有解決元素遍歷複雜的問題。

B+樹的葉子節點用鏈指標相連,極大提高區間訪問速度。【比如查詢50到100的記錄,查出50後,順著指標遍歷即可】

B+樹的葉子結點可以存哪些東西

可能是整行資料,也可能是主鍵的值。

前者被稱為聚簇索引,後者稱為非聚簇索引。

聚簇索引更快!!!

為什麼???聚簇索引已經查到整行資料了,而非聚簇索引還可能根據主鍵值再進行查詢一次。

例外:覆蓋索引——資料直接從索引中取得。

6,SQL優化

SQL優化背景

開發專案上線初期,由於業務資料量相對較少,一些SQL的執行效率對程式執行效率的影響不太明顯,而開發和運維人員也無法判斷SQL對程式的執行效率有多大,故很少針對SQL進行專門的優化,而隨著時間的積累,業務資料量的增多,SQL的執行效率對程式的執行效率的影響逐漸增大,此時對SQL的優化就很有必要。

  • SQL優化發生在業務量達到一定規模的時候
  • 目的是優化SQL的執行效率

6.1 優化範圍

  • 硬體資源
  • 作業系統引數,資料庫引數配置
  • SQL語句,索引優化

6.2 SQL優化

  • 資料庫設計優化【規範,前期設計】
  • SQL語句優化
  • 索引優化
  • 讀寫分離,分庫分表

6.3 慢查詢語句

慢查詢:10s無返回結果,定義為慢查詢

SHOW STATUS LIKE "slow_queries";
SHOW VARIABLES LIKE "long_query_time";//可以顯示當前慢查詢時間

set long_query_time=1 ;//可以修改慢查詢時間

6.4 常用優化方法

  • 避免全表掃描(考慮在 where 及 order by 涉及的列上建立索引)

  • 儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描

    select id from t where num is null    
    可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:    
    select id from t where num=0    
    
  • 應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描

  • 應儘量避免在 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=20    
    
  • 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    
    
  • 應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描

    select id from t where num/2=100    
    應改為:    
    select id from t where num=100*2    
    
  • 應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描

    select id from t where substring(name,1,3)='abc'--name以abc開頭的id    
    應改為:    
    select id from t where name like 'abc%'    
    
  • 很多時候用 exists 代替 in 是一個好的選擇

    select num from a where num in(select num from b)    
    用下面的語句替換:    
    select num from a where exists(select 1 from b where num=a.num)    
    
  • 索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率(5)

  • 儘量使用數字型欄位,若只含數值資訊的欄位儘量不要設計為字元型,這會降低查詢和連線的效能,並會增加儲存開銷

  • 儘可能的使用 varchar 代替 char ,因為首先變長欄位儲存空間小,可以節省儲存空間

  • 任何地方都不要使用 select * from t ,用具體的欄位列表代替“*”,不要返回用不到的任何欄位

  • 儘量避免使用遊標,因為遊標的效率較差,如果遊標操作的資料超過1萬行,那麼就應該考慮改寫

7 事務和鎖

7.1 事務

7.1.1 事務存在的原因

事務存在的目的:保證使用者對資料操作對資料是安全的。(比如說銀行卡餘額)

7.1.2 事務的特性——ACID

原子性:一個事務要麼全部執行,要麼不執行

一致性:事務開始和結束時,資料保持一致

隔離性:事務之間互不影響

永續性:事務操作的結果具有永續性

7.1.3 關於髒讀,不可重複讀,幻讀

  • 髒讀

    事務A讀取了事務B中尚未提交的資料。如果事務B回滾,則A讀取使用了錯誤的資料。

    【一個事物在讀的時候,禁止讀取未提交的事務】

  • 不可重複讀

    不可重複讀是指在一個事務範圍內多次查詢卻返回了不同的資料值,這是由於存在查詢間隔,被另一個事務修改並提交了。

    【一個事物在讀的時候,禁止任何事務寫】

  • 幻讀

    在事務A多次讀取過程中,事務B對資料進行了新增操作,導致事務A多次讀取的資料不一致。

    【一個事物加上表級鎖,禁止任何操作的併發】

小結:

髒讀是讀取了尚未提交的資料,不可重複讀是讀取了不停更新的資料(修改),幻讀是指讀取了不停更新的資料(新增)。

7.1.4 關於事務隔離級別

目的:避免髒讀,不可重複讀,幻讀

讀未提交:一個事務可以讀到另一個事務尚未提交的資料。也就是髒讀,避免髒讀的方式:

讀提交:一個事務要等另一個事務提交後才能讀取資料。但會導致一個事務中相同查詢出現不同的結果。也就是不可重複讀。避免不可重複讀的方式:

重複讀(RR,MySQL預設級別):就是在開始讀取資料時,不允許修改操作。但會導致由於允許insert操作導致的事務結果出現不同。也就是幻讀,避免幻讀的方式::

序列化:序列化使事務序列順序執行,但會大大降低併發效能。

7.2 鎖機制

7.2.1 併發控制 控制的是什麼?

併發問題:某個時間點兩次或兩次以上同一請求的結果不一致。

當程式的使用者超過兩個人時,就有機率產生併發問題。當程式的使用者變多,產生併發問題的概率就會隨之上升。

總的來說,併發控制就是控制資料的一致性

7.2.2 共享鎖和排他鎖

Innodb實現了兩種型別的行鎖:共享鎖,排他鎖。

共享鎖:所有使用者都可讀取當前記錄,但不可修改當前記錄

select * from table lock in share mode

排它鎖(悲觀鎖):當前使用者可進行增刪改查,其他使用者無法進行任何操作(MySQL的增刪改操作預設加了排他鎖,查無任何鎖)


【為什麼在Innodb中使用索引?】

Innodb行鎖並不是鎖記錄而是鎖索引,優先鎖主鍵索引,其次鎖非主鍵索引(比如唯一索引),如果沒有索引,就需要通過全表掃描來找到當前記錄,就相當於表鎖了。(這也是為什麼需要進行索引優化的原因)

意向共享鎖和意向排他鎖

Innodb雖然使用行鎖,但並沒有廢棄表鎖。

行鎖和表鎖】

MyISAM儲存引擎使用的是表鎖,而Innodb增加了行鎖。並不意味著Innodb徹底拋棄了表鎖。

關於行鎖,較小的粒度導致其高併發,但也因較小的粒度導致加鎖慢,開銷大,會出現死鎖情況。

關於表鎖,較大的粒度在高併發上的表現很弱,但同時粒度較大,加鎖塊,開銷小,不會出現死鎖情況。

沒有完美的技術,只有合適的解決方案。在高併發場景下使用行鎖而忍受一些問題本質上是一種權衡。

意向鎖的背景衝突】

意向鎖的出現本質上是解決行鎖和表鎖矛盾的問題。

事務A獲得了表中某一行的共享鎖,事務B申請了表的寫許可權,這時候就會產生矛盾。

關於意向鎖】

首先,意向鎖是一種表鎖。

意向共享鎖:事務獲得表中的某一行的共享鎖前,需要先獲得整張表的意向共享鎖。

意向排他鎖:事務獲得表中的某一行的排他鎖前,需要先獲得整張表的意向排他鎖。

意向鎖的加鎖過程是自動完成的。

【意向鎖的共享問題】

意向鎖是表鎖,它的互斥性是針對表級別的事務,比如一個事務要獲取一張表的寫許可權。所以意向鎖對於表級別的事務是互斥的。但是對於行級別的事務是共享的,也就是說,一個意向鎖可以被多個行級別的事務所持有。

7.3 死鎖

關於死鎖抖音上有一個非常好玩的小視訊:

面試官問:解釋一下死鎖,解釋明白了就發offer

應聘者答:先發offer,發了offer再解釋

死鎖本質上就是持有鎖和釋放鎖的問題,就像這個視訊裡描述的,面試官在聽到死鎖的解釋後,才會釋放offer這個鎖,而應聘者是得到offer後才會釋放死鎖解釋這個鎖。offer和對死鎖的解釋就可以類比兩個鎖。

死鎖的狀態就是互相等待。

7.4 樂觀鎖與悲觀鎖

樂觀鎖和悲觀鎖並不是鎖的具體實現,而是併發控制的兩種策略,或者說是抽象。

樂觀鎖(適合多讀場景)

  • 樂觀鎖本質上是沒有鎖的。
  • 執行流程,先讀取資料,然後在更新前檢查在讀取至更新這段時間資料是否被修改
    • 未修改:直接更新資料
    • 已修改:重新讀取,再次提交更新(或者放棄操作)

為什麼樂觀鎖適合多讀場景?

樂觀鎖是一種更新前的檢查機制,相對於悲觀鎖來說在多讀場景下可以減少鎖的效能開銷,對於多寫場景,樂觀鎖會一直進入已修改,重新讀取,再次提交的迴圈,反而帶來更多的資源消耗。

悲觀鎖(適合多寫場景)

  • 讀取資料的時候上鎖(其他使用者就無法讀取),直到本次資料更新完成才會釋放鎖。在多寫場景下,能保證較高的資料一致性。

【總的來說,樂觀鎖回滾重試,悲觀鎖阻塞事務】