MySQL NotesNotes:BASIC Knowledge數據類型以及屬性修飾符:Maintenance Command:表修改MYSQL查詢單表查詢查詢順序多表查詢和子查詢MySQL視圖DDL & DML修改密碼:MySQL 系統參數設定:存儲引擎存儲引擎所使用的數據文件:各類存儲引擎特性InnoDB:索引類型:MyISAM:ARCHIVE:CSV:BLACKHOLE:MEMORY:MRG_MYISAM:NDB:三方存儲引擎:InnoDB與MyISAM比較存儲引擎的選擇需求MySQL用戶管理:帳號管理命令權限管理命令管理權限MySQL鎖數據庫訪問權限MySQL查詢緩存MySQL事務MySQL日誌查詢日誌慢查詢日誌錯誤日誌二進制日誌:中繼日誌事務日誌備份與恢復備份實例Xtrabackup: 支持對innodb 增量熱備份的工具使用SELECT和LOAD語句進行備份和還原MySQL複製主從複製MySQL GTIDGTID參數設定GTID主從配置多源複製 Multi-Source Replication:MySQL Proxy安裝mysql-proxy SysV腳本admin.lua文件補充範式html
對於Mysql而言,任何的字符是必需要加引號的,數值型必定不能加引號前端
數值型 tinyinit, smallint,mediumint,int,brgint dicimal float, doublenode
字符型 char, varchar, tinytext, text, mediumtext, longtext binary, varbinary, tinyblob,blob,mediumblob,longblobmysql
字符型經常使用的屬性修飾符: 1. NULL:容許非空 2. NOT NULL:非空約束 3. DEFAULT 'string': 僅能用於char和varchar,不適用於text. 4. CHARACTER SET:字符集 使用SHOW VARIABLES LIKE '%char%'
使用SHOW CHARACTER SET
查看字符集 5. COLLATION: 排序規則 SHOW COLLATION
查看排序規則 note:Binary strings are ordered by each bytes valuesgit
數值型經常使用的屬性修飾符:github
1 . AUTO_INCREMENT:自動增加web
1. 非空且惟一,至少是惟一鍵,要支持索引 2. 必須爲整數,使用UNSIGNED修飾符 3. 必須是integer data type: INT 4. 若爲主鍵則爲非空,若指定爲惟一鍵,則要指定INT爲非空
2 . UNSIGNED: 無符號 3 . NULL/NOT NULL 4 . DEFAULT int_num面試
浮點型經常使用的屬性修飾符:正則表達式
note: g=global,f=floatredis
BOOLEAN:
實際上就是類型tinyint(1), 1:爲真 0:爲假
Datatime Data Types:
Date: 三個字節 #1000-01-01 to 9999-12-31 TIME: 三個字節 #-838:59:59 to 838:59:59 DATETIME: 八個字節 #1000-01-01 00:00:00 to 9999-12-31 23:59:59 YEAR(2): 00 to 99 #一個字節 YEAR(4): 1901 to 2155 #一個字節 TIMESTAMP: 四個字節 #1970-01-01 00:00:00 to 2038-01-18 22:14:07 修飾符: 1 . NULL/NOT NULL 2 . DEFAULT
ENUM和SET:枚舉和集合 ENUM: 最多能夠列舉65535 strings SET: 最多支持64個字符,一般爲單個字符。 note: SET能夠從中選擇多個,ENUM只能選擇一個 SET一般存儲索引 eg: a,b,c,d 1001則表示a,d
表空間: MyISAM表,每表有三個文件,都位於數據庫目錄中:
InnoDB表,有兩種存儲方式 默認方式: 每張表有一個獨立文件和一個共享的文件 tb_name.frm:表結構的定義,位於數據庫目錄中 ibdata#:共享的表空間文件,默認位於數據目錄(datadir指向目錄)中 獨立的表空間文件:每張表有一個表結構文件和一個特有的表空間文件 tb_name.frm: 表結構 tb_name.ibd: 表空間
show global variables like 'innodb%'; innodb_file_per_table | OFF #默認關閉
MYSQL語句:數據庫不指定則繼承服務器的,表的不指定則繼承數據庫的,數據的不指定則繼承表的 查看warning
show warning;
查看排序規則
SHOW COLLATION;
查看存儲引擎
SHOW ENGINES;
建立數據庫表文件etc 建立數據庫
mysql Syntax: CREATE {DATABASE|SCHEMA}[IF NOT EXSITS] db_name [DEFAULT] [CHARACTER SET ''][DEFAULT][COLLATE=''] eg. CREATE DATABASE IF NOT EXISTS mydb
直接查詢出建立表時所使用的命令,以後能夠直接使用指令添加表
show create table students; | students | CREATE TABLE `students` ( `StuID` int(10) unsigned NOT NULL AUTO_INCREMENT, `Name` varchar(50) NOT NULL, `Age` tinyint(3) unsigned NOT NULL, `Gender` enum('F','M') NOT NULL, `ClassID` tinyint(3) unsigned DEFAULT NULL, `TeacherID` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`StuID`) ) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 |
刪除數據庫:
DROP {DATABASE|SCHEMA}[IF EXSITS] db_name
修改數據庫:
ALTER {DATABASE|SCHEMA} db_name [DEFAULT] [CHARACTER SET=''] [COLLATE='']
數據庫名稱修改比較複雜
備份庫中個表,刪除原庫,還原庫
表建立方式1:
Syntax: CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [partition_options]
create table t1 (ID INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name CHAR(30));
Note: temporary表會在內存中建立 create_defination: 字段的定義、字段名、類型和類型修飾符 鍵、約束或索引 鍵都是索引,可是索引未必都是鍵
eg. CREATE TABLE t3 (Name VarChar(50) NOT NULL, Age TINYINT UNSIGNED NOT NULL,PRIMARY KEY(Name,Age));
table_option
ENGINE [=] engine_name AUTO_INCREMENT [=] value #指定auto-increment 開始數字大小 [DEFAULT] CHARACTER SET [=] charset_name #設定默認名 [DEFAULT] COLLATE [=] collation_name #排序規則 COMMENT [=] 'string' #註釋 DELAY_KEY_WRITE [=] {0 | 1} #頻繁修改的表,能夠提升性能。防止頻繁寫入的場景 INDEX DIRECTORY [=] #索引目錄位置 ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} #表格式
表建立方式2 根據替他表來複制數據,並不複製表結構
mysql Syntax: CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [partition_options] select_statement
表建立方式3 根據其餘表來建立空表,複製表結構
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name { LIKE old_tbl_name | (LIKE old_tbl_name) }
ALTER TABLE tbl_name [alter_specification [, alter_specification] ...] [partition_options]
修改字段定義:
插入新字段
ADD [COLUMN] col_name column_definition [FIRST | AFTER col_name ]
eg. alter table t5 add Age TINYINT UNSIGNED NOT NULL; alter table t6 add Gender ENUM('M','F') NOT NULL DEFAULT 'M'
添加多個字段使用
ADD [COLUMN] (col_name column_definition,...) note:同CREATE表
刪除字段:
DROP [COLUMN] col_name
修改字段:
修改字段名稱
CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name] eg. alter table t6 change Name StuName CHAR(30) NOT NULL;
修改字段類型以及屬性
MODIFY [COLUMN] col_name column_definition [FIRST|AFTER col_name] eg. alter table t6 modify Gender ENUM('M','F') NOT NULL after Age;
修改約束及索引
查看索引
SHOW INDEXES FROM [Table]
添加索引
ADD {INDEX|KEY} [index_name] [index_type] (index_col_name,...) [index_option] ... eg. alter table t6 add index (StuName)
刪除索引
DROP {INDEX|KEY} index_name eg. alter table t6 drop index StuName;
建立約束:
ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...) [index_option] ...
ADD [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type] (index_col_name,...) [index_option] ...
啓動/取消鍵:
DISABLE KEYS ENABLE KEYS
修改表名
RENAME [TO|AS] new_tb1_name
修改表選項:
table_option [[,] table_option] ... (see CREATE TABLE options) note:與create相同 eg. ALTER TABLE t6 engine MYISAM;
指定排序標準的字段
ORDER BY col_name [, col_name] ...
轉換字符集以及排序規則
CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]
Example:
建立表
create table Lab (ID smallint not null primary key, Host varchar(30) not null,IP varchar(40) not null, Password varchar(10) not null, Format varchar(50) not null);
調整表結構
alter table Lab add Username varchar(20) not null after Host;
添加表結構
insert table Lab (ID,Host,Username,IP,Password,Format)value(1,centos.example.com,root,192.168.48.130,nsadm,ssh root@192.168.48.130);
新增字段:
alter table users add Class tinyint not null auto_increment after Name;
將ID字段名稱修改成TID:
alter table users change ID TID not null;
將Age字段放置最後:
alter table users modify ID smallint not null after Class;
查詢執行路徑:
查詢執行路徑 客戶端/服務器 SQL | ——————>查詢緩存 | ——————> 解析器 | —————>預處理器 | | | |—————————————————>| | | 查詢優化器 | | | | |—————————————————————————>查詢執行引擎 | | API調用 | 存儲引擎 | 數據
MySQL的查詢操做:
單表查詢:簡單查詢 多表查詢:鏈接查詢 聯合查詢 選擇和投影: 投影:挑選要顯示的字段 選擇:挑選要顯示的行
SELECT values_to_display FROM table_name WHERE expression GROUP BY how_to_group Having expression ORDER BY how_to_sort LIMIT row_count
投影法: SELECT 字段1,字段2,...FROM tb_name; SELECT * FROM tb_name; 選擇: SELECT 字段1,... FROM tb_name WHERE 子句; 布爾條件表達式
布爾條件表達式操做符
= 等值比較 <> != 不等值比較 <=> 空值安全的比較,與空值比較 <;<=;>;>= BETWEEN AND 判斷指定字段的範圍是否在制定的範圍 IN 是否在集合中 IS NULL 是否爲空 IS NOT NULL 是否不爲空 LIKE 通配比較 %(任意長度的任意字符) _(任意單個字符) note: Like性能較差 REGEXP aka RLIKE 正則表達式匹配
eg.
CREATE TABLE students(SID INT UNSIGNED AUTO_INCREMENT NOT NULL UNIQUE KEY,Name CHAR(30) NOT NULL, Age TINYINT UNSIGNED NOT NULL, Gender ENUM('F','M') NOT NULL DEFAULT 'M', Tutor CHAR(30)); INSERT INTO students VALUES (1,"Guo Jing", 27,"M","Ke Zhen E"); INSERT INTO students VALUES (3,"Guo Polu",21,'M',"Jia Bao Yu"; INSERT INTO students VALUES (3,"Guo Polu",21,'M',"Jia Bao Yu"); INSERT INTO students VALUES (4,'Xue Baochai',19,'F','Jia Baoyu'); INSERT INTO students VALUES (5,'Xia yu he',37,'F','Bababa'); INSERT INTO students VALUES (6,'Wu Yong',51,'M','Lin Dai Yu');
選出年齡在25到40之間的學生:
select * from students where age between 25 and 40;
查看符合列表中條件的項
select Name,Age From students where age in (25,26,27,28,29)
使用通配或者正則表達式匹配比較
select * from students where Name like 'X%'; select * from students where Name rlike 'X.*';
查看是否爲空
select name,tutor From students where tutor is NULL;
組合條件測試:
NOT aka! 非運算符 AND aka && 與運算 OR aka || 或運算 XOR 不包含比較
select * from students where Age > 25 and Gender = 'M';
按序輸出:
ORDER BY [字段] ASC: 升序 DESC: 降序
select * from students where Age > 25 and Gender = 'M' order by Name;
聚合函數
SUM(): 求和
select SUM(Age) from students;
AVG(): 求平均值
select AVG(Age) from students;
MAX(): 求最大值
select MAX(Age) from students;
MIN(): 求最小值
select MIN(Age) from students;
COUNT(): 統計個數
select COUNT(Age) from students where Age > 25;
分組:
經過必定的規則將一個數據集劃分爲若干個小的區域,而後針對若干個小區域進行數據處理 通常配合集合函數來運用 GROUP by how_to_group
select SUM(Age),Gender from students GROUP BY Gender;
對分組的條件過濾: Having 只返回有用的行: LIMIT 一個數爲顯示的行數 兩個數字爲偏移第一個數字行,顯示第二個數字
Select語句的執行流程
from clause -> where clause -> group by -> having clause -> order by -> select -> limit
Select語句的緩存選項:
聯結查詢:事先將兩張或者多張表join, 根據join的結果進行查詢 cross join: 交叉聯結,容易出現重複行 天然聯結:等值聯結,把相同的字段進行等值聯結 外聯結: 左外聯結,只保留出如今左外聯結元以前(左邊)的關係中的元組(以左表爲基準)
left_tb LEFT JOIN right_tb ON [condition]
eg. select s.Name,c.Class from students AS s LEFT JOIN classes AS c ON s.ClassID = c.ClassID
右外聯結:只保留出如今右外聯結元算以後(右邊)的關係中的元組(以右表爲準)
left_tb RIGHT JOIN right_tb ON [condition]
還有全外聯結 別名: AS 表別名和字段別名
子查詢:在查詢中嵌套的額查詢 用於WHERE中的子查詢 1 . 用於比較表達式中的子查詢,子查詢的返回值只能有一個 2 . 用於EXISTS中的子查詢,判斷存在與否 3 . 用於IN中的子查詢 判斷存在於指定列表中 用於FROM中子查詢:
select alias.col,... FROM (SELECT clause) AS alias WHERE condition
查詢案例
1. 顯示前5位同窗姓名、課程以及成績: 使用天然聯結中的等值聯結:
select Name,Course,Score From students AS s, courses AS c,coc,scores AS ss where s.ClassID = coc.ClassID AND coc.CourseID = c.CourseID AND s.StuID <=5 AND s.StuID=ss.StuID AND ss.CourseID=coc.CourseID;
2. 求前8位同窗每位同窗本身兩門課的平均成績,並按降序排列,使用ORDER BY 進行降序排列
select Name,AVG(Score) From students AS s, coc, courses as c, scores AS ss where s.ClassID=coc.ClassID AND coc.CourseID = c.CourseID AND s.StuID <=8 AND s.StuID=ss.StuID AND coc.CourseID=ss.CourseID GROUP by Name ORDER BY AVG(Score) DESC;
3. 顯示每門課程課程名以及學習了這門課程的同窗個數
select Course, count(Name) from courses as c, students as s, coc where coc.CourseID=c.CourseID AND coc.ClassID=s.ClassID Group By Course;
4. 查找年齡大於平均年齡的全部同窗姓名 定義AVG(Age)的由來,這種嵌套查詢稱爲子查詢
select Name From students Where Age > (select AVG(Age) From students);
5 . 查找沒有開課班級的同窗姓名添加新學生和班級
INSERT INTO students VALUES(26,'TOM',33,'F',8,11),(27,'Jerry',26,'M',9,2); insert into classes values(9,'Liangshan',22);
此處使用左聯結,是的沒有課程的班級置NULL
select Name From students where ClassID IN (select classes.ClassID from classes LEFT JOIN coc ON classes.ClassID=coc.ClassID where coc.ClassID is NULL);
MySQL聯合查詢:
使用UNION進行聯合:
SELECT clause UNION SELECT clause eg. SELECT Name,Age FROM teachers UNION SELECT Name,Age FROM students;
使用EXPLAIN關鍵字來查詢SQL語句的執行過程
explain select Name,Age FROM students WHERE Age > 25;
查看錶的索引
SHOW INDEXES FROM students;
添加表索引
ALTER TABLE students ADD INDEX(Age);
存儲下來的SELECT語句 查看建立視圖的幫助: HELP CREATE VIEW
eg. CREATE VIEW stu AS select StuID,Name,Age,Gender FROM students;
查看錶的狀態
SHOW TABLE status;
查看視圖建立信息
SHOW CREATE VIEW stu;
DDL: Data Defination
DML: Data Manipulation
INSERT
第一種:
INSERT INTO tb_name [(col1,col2,...)] {value|values} (val1,val2,...)[,(val21,val22,...)]
第二種:
INSERT INTO tb_name SET col1=val1 col2=val2,...;
第三種:
INSERT INTO tb_name SELECT clause;
insert into t1 (Name) VALUES('tom'),('jerry');
查看最後一次插入的auto_increment 值
SELECT LAST_INSERT_ID();
重置表自動增加參數
TRUNCATE tb_name
REPLACE: 語法格式與INSERT相同,在必要時能夠完成替換操做。除了在新插入的數據和表中主鍵定義或惟一索引定義的數據相同時,會替換老的數據,老的行
UPDATE:
Syntax: UPDATE [LOW_PRIORITY] [IGNORE] table_reference SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ... [WHERE where_condition] [ORDER BY ...] [LIMIT row_count] eg. update students set Name='Hugh' where Name='Jerry';
Note: UPDATE一般狀況下,必需要使用WHERE子句,或者使用LIMIT限制要修改的行數
--safe-update 防止update錯內容
DELETE:
Syntax: DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [WHERE where_condition] [ORDER BY ...] [LIMIT row_count]
Note: DELETE同UPDATE,刪除時要作限定,若是不限定則清空整個表
MySQL的功能從邏輯上面試在二層實現的,
Query Cache;只有select語句會被緩存下來 Parser; 解釋器,分析器 Optimizer; 優化器 插件式存儲引擎是在三層實現
UPDATE USER SET PASSWORD= PASSWORD('nsadm') where user='root'; FLUSH privileges; mysqladmin -uUserName -hHost password 'new_password' -p mysqladmin -uUserName -hHost -p flush-privileges
MYSQL SQL_MODE:SQL模式
note: 默認使用空模式 SHOW GLOBAL VARIABLES LIKE 'sql_mode'; 設定服務器變量的值:僅用於支持動態的變量 支持修改的服務器變量: 動態變量:能夠在MySQL運行時修改 靜態變量:於配置文件中配置,並於重啓後方能生效 服務器變量從生效範圍需求,分兩類: 全局變量:服務器級別,修改以後僅對新創建的會話有效,對當前會話無效 會話變量:會話級別,僅對當前會話有效 會話創建時,從全局繼承各變量;
查看服務器變量
SHOW (GLOBAL|SESSION) VARIABLES [LIKE '']; Note:默認爲SESSION
SELECT @@(global|session).sql_mode;
SELECT @@global.sql_mode;
SELECT * from information_schema.GLOBAL_VARIABLES WHERE VARIABLE_NAME='sql_mode; 保存於information_schema的表中
使用SET關鍵字修改變量
前提:修改全局變量,默認僅管理員有權限
SET (GLOBAL|SESSION) VARIABLE_NAME='VALUE'; Note:缺省爲session變量 eg. SET SESSION sql_mode='STRICT_ALL_TABL
Note:不論SESSION仍是GLOBAL的VARIABLES,在mysql服務重啓以後都會失效。那麼久意味着,若是想要其持久生效。則須要修改mysql的配置文件相應段落中[mysqld]
關於MySQL中字符的大小寫:
存儲引擎是表級別的, 因此存儲引擎也稱爲「表類型」
查看存儲引擎
show engines
修改表的存儲引擎
ALTER TABLE classes ENGINE 'InnoDB';
修改表的存儲引擎,會帶來大量的I/O,生產環境中不建議使用
數據庫導入
mysql -uroot -p < mydb.sql
查看錶具體信息
show table status like "classes" \G;
Name: classes Engine: InnoDB Version: 10 Row_format: Compact Rows: 8 Avg_row_length: 2048 Data_length: 16384 Max_data_length: 0 Index_length: 0 Data_free: 8388608 Auto_increment: 9 Create_time: 2016-02-18 17:02:07 Update_time: NULL Check_time: NULL Collation: utf8_general_ci Checksum: NULL
Name:表名 Engine:存儲引擎 Version:版本 Row_format:行格式 {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} 每種格式存儲開銷各不一樣 Rows:表中的行數 MYISAM表是精確的 InnoDB表的行數是一個估計值 Avg_row_length:平均每行所包含的字節數 Data_length:表中數據整體大小,單位是字節 Rows * Avg_row_length Max_data_length:表的最大佔用長度,單位爲字節 Index_length:索引大小 Data_free: 對MyISAM表,標示已經分配但還沒有使用的空間,其中包含此前刪除行以後騰出來的空間 Auto_increment: 下一個自動增加的值 Create_time: 表的建立時間 Update_time: 表數據的最後修改時間 Check_time: 使用CHECK TABLE或myisamchk最近一次檢測表的時間 Collation: 排序規則 Checksum:若是啓用則爲表的校驗和 Create_options:建立表時指定使用的其餘選項: Comment:表的註釋
MyISAM: 每一個表都會在數據庫目錄下存儲三個文件: tb_name.frm 表結構 tb_name.MYD 數據 tb_name.MYI 索引 InnoDB: 兩種格式:
1 . innodb_file_per_table = OFF, 即便用共享表空間
每張表有一個獨有的表格式定義:tb_name.frm 還有一個默認位於數據木下共享的表空間文件:ibdata# 2 . innodb_file_per_table = on, 即便用獨立表空間 每一個表在數據庫目錄下存儲兩個文件 tb_name.frm #表空間 tb_name.ibd #數據文件 表空間: table space, 由InnoDB管理的特有格式數據文件,內部能夠同時存儲數據和索引。支持聚簇索引
查看有關ENGINE的變量
SHOW VARIABLES LIKE '%engine%';
+---------------------------+--------+ | Variable_name | Value | +---------------------------+--------+ | default_storage_engine | InnoDB | | engine_condition_pushdown | ON | | storage_engine | InnoDB | +---------------------------+--------+
默認存儲引擎爲InnoDB,能夠經過修改default_storage _engine服務變量實現
支持事務:事務日誌 ib_logfile0和ib_logfile1順序存儲的log file能夠在mydata的目錄下找到 他們的大小是固定的,而且是磁盤上一段連續的存儲空間。
支持外鍵
MVCC
聚簇索引: 聚簇索引以外的其餘索引,一般稱爲輔助索引。將索引和數據排列到一塊兒。只要能找到索引就能連續的查詢到行。 聚簇索引只能有一個; 輔助索引能夠有多個;輔助索引能夠查找到聚簇索引,而後經過聚簇索引能夠查找到數據存放位置。 聚簇索引一般使用主鍵來實現,由於主鍵不容許被重複
支持自適應的hash索引
支持熱備份
行級鎖:間隙鎖
適用於多讀少寫的場景。較小的表,可以容忍崩潰後數據難以恢復的狀況 全文索引** 壓縮**:一般只能用來作數據查詢,不能進行修改。提升性能,一般使用來作數據倉庫
空間索引
表級鎖:
延遲更新索引鍵:Delay Key Right 不支持事物,外鍵和行級鎖 崩潰後難以修復
僅支持INSERT和SELECT,支持很好的壓縮功能。適用於存儲純文本的日誌信息,或其餘按時間序列實現的數據採集類的應用。 不支持事物,不能很好的支持索引
將數據存儲爲CSV格式,(浮點數存儲於文本會丟失精度). 不支持索引,僅僅適用於數據交換的場景。 數據以都好分割
沒有存儲機制,任何發往此引擎的數據都會被丟棄 在級聯複製時有用,做爲中轉服務器。能夠記錄二進制日誌
數據保存至內存中,即內存表。 經常使用於保存數據的中間數據,作臨時表。不是保存最終數據的 支持hash索引,使用表級鎖,不支持BLOB和TEXT數據類型
是MYISAM的變種,可以將多個MyISAM表合併成一個虛表
是MySQL CLUSTER中專用的存儲引擎,實現分佈式,高可用的MySQL集羣。可是並無人用。。
OLTP類: 在線事物處理 XtraDB: 加強的InnoDB,由Percona提供; 編譯安裝MySQL時,下載XtraDB的源碼替換MySQL存儲引擎中的InnoDB源碼 PBXT: 對固態磁盤有優化,支持事務,支持MVCC。MariaDB自帶此引擎 TokuDB: 使用Fractal Trees索引,性能很好。適用於存儲大數據,擁有很好的壓縮比。一樣被引入到新版本的mariaDB.
開源社區存儲引擎
Aria: 前身是Maria,能夠理解爲加強版的MyISAM存儲引擎(支持崩潰後安全恢復,支持數據緩存) Groona: 全文索引引擎,Mroonga是基於Groona的二次開發 OQGraph: 由Open Query研發,支持圖式結構的存儲引擎 SphinxSE: 爲Sphinx全文搜索服務器提供了SQL接口 Spider: 能數據切分紅不一樣分片,比較搞笑透明地實現了分片(shared),並支持在分片上支持進行查詢
列式存儲引擎
按列存儲,對按列查詢的場景很是有效。Web應用中,經常使用的恰巧就是列式查詢。
一般來講InnoDB在不斷完善後,各類性能都比較優秀。建議使用InnoDB,在多讀少寫的場景中儘可能使用Read Commited的隔離級別。
用戶帳號:username@hostname, password
建立用戶:
CREATE USER username@hostname [ IDENTIFIED BY [PASSWORD] 'password' ]
eg.僅容許用戶從192.168.48.100至192.168.48.199的用戶登陸。使用通配
testuser@'192.168.48.1__'
修改用戶名:
RENAME USER old_user TO new_user
建立用戶testuser並將其密碼設置爲testpass
create user testuser@'192.168.%.1__' IDENTIFIED BY 'testpass'; FLUSH PRIVILEGES;
修改用戶密碼
SET PASSWORD FOR 'bob'@'%.example.org' = PASSWORD('cleartext password')
/usr/bin/mysqladmin -u root password 'nsadm'
DROP USER
RENAME USER
SET PASSWORD
查看用戶可以使用的權限
SHOW GRANTS FOR username@'hostname';
忘記MySQL密碼
mysql-safe --skip-grant-tables --skip-networking --datadir=''
MySQL的權限類別:
CREATE TEMPORARY TABLES CREATE USER FILE #讀寫文件的權限 LOCK TABLES
執行操做時加的鎖模式 讀鎖:也叫共享鎖 寫鎖:獨佔鎖,排它鎖 鎖粒度: 表鎖:table lock 鎖定了整張表 行鎖:row lock 鎖定了須要的行 粒度越小,開銷越大,可是併發性越好; 粒度越大,開銷越小,可是併發性越差; 鎖的實現位置: MySQL鎖:可使用顯式鎖 存儲引擎鎖:自動進行的(隱式鎖) 顯示鎖: LOCK TABLES
UNLOCK TABLES
手動施加鎖,好比在備份時使用
LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ... lock_type: READ [LOCAL] | [LOW_PRIORITY] WRITE eg. lock tables classes READ; 釋放全部鎖 unlock tables;
InnoDB存儲引擎也支持另一種顯式鎖(行級鎖): 只鎖定部分行,只是在一個語句中加鎖,若是語句執行完畢,則鎖會自動釋放
SELECT ... LOCK IN SHARE MODE; SELECT ... FOR UPDATE;
PROCESS 查看各與用戶相關的線程信息 SHOW PROCESSLIST
RELOAD #FLUSH和RESET權限 REPLICATION CLIENT REPLICATION SLAVE #複製表的權限 SHOW DATABASES SHUTDOWN SUPER
庫級別和表級別:
數據操做類的權限:
與字段相關的權限:
全部權限:
權限授予
GRANT priv_type [(column_list)] [, priv_type [(column_list)]] ... ON [TABLE|FUNCTION|PROCEDURE] priv_level TO username@hostname [IDENTIFIED BY 'password'],[username@hostname [],...] [REQUIRE SSL] [WITH with_option ...] priv_level: * | *.* | db_name.* | db_name.tbl_name | tbl_name | db_name.routine_name with_option: GRANT OPTION | MAX_QUERIES_PER_HOUR count | MAX_UPDATES_PER_HOUR count | MAX_CONNECTIONS_PER_HOUR count | MAX_USER_CONNECTIONS count
eg.
GRANT select,update,insert,delete on testdb.* to 'testuser1'@'192.168.%.%' IDENTIFIED BY 'testpass';
收回權限
REVOKE
eg.
revoke create on testdb.* from 'testuser1'@'192.168.%.%';
去權限也能夠用DROP
DROP USER 'testuser'@'192.168.%.%'
與用戶受權先關的表:
若果用戶由於登陸錯誤次數過多致使帳號被鎖,則可使用flush hosts 來刷新主機緩存
用於保存MySQL查詢語句返回的完整結果。被命中時,會馬上返回結果。省去了解析,優化和執行等階段
查詢緩存未必帶來的都是好事,在硬件變得強大以後,多顆CPU之間會爭用緩存
如何檢查緩存?
MySQL保存結果於緩存中,把SELECT語句自己作hash計算,計算的結果做爲key,查詢結果作value 什麼樣的語句會被緩存? 查詢語句中有一些不肯定數據時,不會緩存。例如NOW(), CURRENT_TIME(), 通常來講,若是查詢中包含用戶自定義函數、存儲函數、用戶自定義變量、臨時表、mysql庫中的系統表,或者任何包含權限的表,通常都不會緩存 緩存會帶來額外開銷:
查詢緩存變量
SHOW GLOBAL VARIABLES LIKE 'query_cache%';
+------------------------------+----------+ | Variable_name | Value | +------------------------------+----------+ | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 16777216 | | query_cache_type | ON | | query_cache_wlock_invalidate | OFF | +------------------------------+----------+
query_cache_type: 查詢緩存類型,是否開啓緩存功能,開啓方式有三種
query_cache_size: 緩存使用的總空間,單位是字節,大小必須是1024的整數倍。MySQL啓動時,會一次分配並當即初始化指定大小的內存空間。若是修改此大小會清空命中緩存並從新初始化。
query_cache_min_res_unit: 存儲緩存的最小內存塊。(query_cache_size-Qcache_free_memory)/Qcache_queries_in_cache 獲得平均值來參考,接近理想的值
query_cache_limit: 單個緩存對象的最大值,超出時則不予緩存。手動使用SQL_NO_CACHE能夠人爲地避免嘗試緩存返回結果超出子參數限定值的語句。
query_cache_wlock_invalidate: 若是某個表被其餘用戶鏈接鎖住,是否仍然從緩存中返回結果。OFF表示返回
判斷命中率
次數命中率和字節命中率 查看判斷命令命中率
SHOW GLOBAL STATUS LIKE 'Qcache%';
+-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 16755376 | | Qcache_hits | 3 | | Qcache_inserts | 3 | | Qcache_lowmem_prunes | 0 | | Qcache_not_cached | 4 | | Qcache_queries_in_cache | 3 | | Qcache_total_blocks | 9 | +-------------------------+----------+
碎片整理
FLUSH QUERY_CACHE
清空緩存
RESET QUERY_CACHE
查看緩存命中率
SHOW GLOBAL STATUS WHERE Variable_name='Qcache_hits' OR Variable_name='Com_sele ct';
計算次數命中率,獲取以上參數值,計算方法Qcache_hits/(Com_select + Qcache_hits) 字節命中率很難做爲估算,因此計算次數命中率只能做爲參考。 命中和寫入的比率: Qcache_hits/Qcache_inserts的值,總比值能大於3:1,則說明緩存也是有效的。能達到10:1爲比較理想的狀況
緩存優化使用思路:
事務:Transaction 事務就是一組原子性的查詢語句:也即將多個查詢黨組偶一個獨立的工做單元 ACID測試:能知足ACID測試就表示其支持事務、或兼容事務
隔離級別:
與事物相關的經常使用命令:
START TRANSACTION
開啓事務
COMMIT:事物提交 ROLLBACK:事物回滾
SAVEPOINT 記錄點:
SAVEPOINT identifier ROLLBACK [WORK] TO [SAVEPOINT] identifier RELEASE SAVEPOINT identifier
若是沒有顯示啓動事物,每一個語句都會當作一個獨立的事物。其執行徹底會被自動提交
show global variables like '%commit%';
select @@session.autocommit;
set global autocommmit = 0;
查看隔離級別:
SHOW GLOBAL VARIABLES LIKE '%iso%';
SELECT @@global.tx_isolation;
SET GLOBAL tx_isolation='READ-UNCOMMITED' READ-COMMITED REPEATABLE-READ
READ UNCOMMITED 不可重讀,髒讀,幻讀 READ COMMIT: 不可重讀,幻讀 REPEATABLE READ: 可重讀,幻讀 SERIALIZABLE: 阻塞 建議:對事物要求不是特別嚴格的場景下,可使用讀提交 MVCC: 多版本併發控制 每一個事物啓動時,InnoDB爲每一個啓動的事物提供一個當下時刻的快照 爲實現此功能,InnoDB會爲每一個表提供兩隱藏字段,一個用於保存建立時間,一個用於建立失效時間。 裏面存儲的是系統版本號:(system version number)
查詢日誌:高開銷,默認關閉。 慢查詢日誌:查詢執行時長超過指定時長的查詢,即爲慢查詢 錯誤日誌:啓動,關閉,複製線程時相關信息都會記錄進日誌中 二進制日誌:記錄引發數據改變的全部操做的日誌。MySQL的複製功能就依賴此功能 中繼日誌:將主服務器二進制日誌文件保存到從服務器中所記錄的日誌文件 事務日誌:
PCI-E的固態硬盤,幾乎能夠體會接近內存的性能,帶寬接近前端總線
查看innodb的參數
SHOW GLOBAL VARIABLES LIKE 'innodb%';
SHOW GLOBAL VARIABLES LIKE '%log%'
log = {ON|OFF}:是否記錄全部語句的日誌信息於通常查詢日誌文件(general_log) log_output={TABLE|FILE|NONE} 記錄於表中,文件中以及不記錄 TABLE和FILE能夠同時出現,用逗號分隔便可 general_log:是否啓用查詢日誌,與log參數衝突 general_log_file:定義通常查詢日誌保存的文件
long_query_time: 慢查詢的時長,查過次時間的時長被稱爲慢查詢 slow_query_log='OFF|ON' 存放位置取決於log_output 參數 slow_query_log_file: 慢查詢日誌文件 log_slow_rate_limit=1
服務器啓動和關閉過程當中的信息 服務器運行過程當中的錯誤信息 時間調度器運行一個時間時產生的信息 在複製架構中的從服務器上啓動從吳福氣線程時產生的信息 log_error=‘/path/error_log_file' log_warnings='{1|0}' 是否記錄警告信息於日誌信息中
"修改相關的信息",一般也叫做複製日誌 時間點恢復 使用mysqlbinlog工具來查看二進制日誌:
查看當前正在被使用的二進制文件
SHOW MASTER STATUS;
使用寫緩衝區域完成並行操做 日誌滾動:
手動滾動日誌,重啓mysqld服務也會滾動日誌
FLUSH LOGS;
查看二進制日誌
SHOW BINARY LOGS;
手動清除日誌文件
Syntax: PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
查看二進制日誌文件內容
Syntax: SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
SHOW BINLOG EVENTS in 'mysql-bin.000002' FROM 892; mysqlbinlog --start-position=892 mysql-bin.000002
server-id: 服務器的身份標示
MySQL記錄二進制日誌的格式:
日誌文件內容格式:
查看進程的ID號
SHOW PROCESSLIST;
於二進制日誌文件相關的服務器變量:
log_bin={ON|OFF},還能夠是一個文件路徑 log_bin_trust_function_creators sql_log_bin={ON|OFF} sync_binlog:同步時間間隔,從緩衝區同步到日誌中,默認爲0,說明不靠時間來控制。 binlog_format={statement|row|mixed} note: 切勿將二進制文件與數據文件放於同一磁盤設備 max_binlog_cache_size =: 二進制日誌緩存空間大小,僅用於緩衝事務類的語句,單位是字節 max_binlog_stmt_cache_size: 非事務類和事物類共同使用的緩衝空間大小 max_binlog_size: 二進制日誌文件的滾動上限,單位是字節 臨時關閉二進制日誌文件,例如測試需求:
SET SESSION sql_log_bin=0;
從服務器從主服務器中讀取二進制日誌執行時所記錄的日誌 relay_log_purge={ON|OFF} 自動清理再也不須要的中繼日誌 relay_log_info_file: 中繼日誌的文件
備份目的
備份類型: 經過快照備份的時間點能夠保證一致 根據備份數據服務器是否在線
根據備份的數據集:
根據備份時的接口(直接備份數據文件仍是經過mysql服務器導出數據):
物理備份:直接複製(歸檔)數據文件的備份方式
物理備份跨平臺能力沒有邏輯備份好
邏輯備份:把數據從庫中提取出來,保存爲文本文件
mysqldump
根據備份時備份整個數據仍是僅備份變化的數據:
備份策略:
備份對象:
備份工具: mysqldump: 邏輯備份工具,可以對innodb的熱備,可是代價比較高,對MyISAM只能作到溫備, 對Aria也只能作溫備,備份和恢復過程比較慢。單線程工具,只能啓用一顆CPU進行單線程的備份,性能不好。 mysqldumper: 多線程的mysqldump 此兩種方式很難完成增量備份 lvm-snapshot: 接近於熱備份的工具。由於要先請求全局鎖,而後建立快照,並在快照建立完成以後釋放全局鎖。 使用cp, tar等工具進行物理備份。備份和恢復速度較快 很難實現增量備份,而且請求全局鎖要等待一段時間,在繁忙的服務器上尤爲如此。 SELECT clause INTO OUTFILE '/path/to/somefile' 使用SELECT和LOAD來實現部分備份,不備份表格式,只備份數據。也是一種邏輯備份工具,速度略快於mysqldump LOAD DATA INFILE '/path/from/somefile' Innobase: 商業備份工具, innodackup。 物理備份,速度快 Xtrabackup: 由Percona提供的開源備份工具 InnoDB熱備,增量備份; MyISAM溫備,不支持增量; mysqlhotcopy: 幾乎冷備
mysqldump: 備份前要加鎖
mysqldump [options] [db_name [tb1_name ...]]
備份單個庫:mydqldump [options] db_name 恢復時,若是目標庫不存在,須要事先手動建立
mysqldump --all-databases -uroot -p> /tmp/all.sql --all-databases: 備份全部庫 --databases db1,db2: 備份指定的多個庫 --lock-all-tables: 在備份開始前自動請求鎖定全部表(建議使用),自動請求加讀鎖。對MyISAM和InnoDB進行溫備 --single-transaction: 可以對InnoDB存儲引擎實現熱備 備份代碼經常使用三選項 --events: 備份事件調度器代碼 --routines:備份存儲過程和存儲函數 --trigger:備份觸發器 備份時滾動日誌: --flush-logs:施加鎖後,滾動日誌 複製時的同步位置標記: --master-data= [0|1|2] 0:不記錄 1:記錄爲CHANGE MASTER語句 2:記錄爲註釋的CHANGE MASTER語句
mysqldump -uroot -p --all-databases --flush-logs --master-data=2 --lock-all-tables> all.sql
使用mysqldump備份: 請求鎖: --lock-all-tables 或使用 --single--transaction進行innodb熱備份 滾動日誌: --flush-logs 選定要備份的庫: --databases 記錄二進制日誌文件及位置,--master-data= FLUSH table時會將全部表中位於緩衝區的內容所有同步到磁盤中去。 通常使用FLUSH附加鎖的方式
FLUSH TABLES WITH READ TABLE
還原日誌的案例
首先熱備份 mysqldump -uroot -p --databases mydb --single-transaction --flush-logs --master-data=2 > /tmp/mydb.sql 在備份後對數據庫進行修改,因爲master-data=2 能夠記錄當前的二進制log的position位置 mysqlbinlog --start-position=107 mysql-bin.000028 將修改內容也進行備份 mysqlbinlog --start-position=107 --stop-position=272 mysql-bin.000028 > /tmp/mydb1.sql mysql -uroot -p < mydb.sql mysql -uroot -p < mydb1.sql
也能夠是用source的方法還原數據
set session sql_log_bin=0; source /tmp/mydb.sql set session sql_log_bin=1; show master status;
恢復: 建議,關閉二進制日誌,關閉其餘用戶鏈接 備份策略:基於mysqldump 備份:mysqldump+二進制日誌文件(增量) 週日作一次徹底備份,備份的同時滾動日誌 mysqladmin flush-logs
週一至週六,備份二進制日誌便可 恢復:徹底備份+各二進制文件中至此此刻的事件 對於MySQL配置文件,以及與MySQL相關的配置文件在每次修改後都應該直接作備份
基於快照備份
步驟:
一、請求全局鎖,並滾動日誌
FLUSH TABLES WITH READ LOCK; FLUSH LOGS;
二、作二進制日誌文件及位置標記(手動進行)
mysql -e 'show master status' > /path/to/somefile
三、建立快照卷
lvcreate -L -s -n 'mysnap' -p r/path/to/some_l
四、釋放全局鎖
UNLOCK TABLES;
五、掛載快照卷並備份,快照卷只是做爲一個訪問通路來使用
cp -a 的方式
六、備份完成後,刪除快照卷
umount /mnt; lvremove /dev/mapper/mysql_data-mysql--snap
恢復:
innobackupex: 須要MySQL服務處於運行狀態,而且須要經過用戶驗證來進行查詢驗證
建立最小受權的用戶
CREATE USER 'bkpuser'@'localhost' IDENTIFIED BY 'bkpass'; REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'bkpuser'; GRANT RELOAD,LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkuser'@'localhost'; FLUSH PRIVILEGES;
使用Xtrabackup的前提是要將innodb的表分離成數據與表結構分離的狀態
innodb_file_per_table=ON
LSN: log serial number
執行backup
innobackupex --user=root --password=nsadm /mybackups/
還原數據:
通常狀況下,在備份完成後,數據尚且不能用於恢復操做,由於備份的數據中可能會包含還沒有提交的事務或已經提交但還沒有同步至數據文件中的事務。所以,此時數據文件仍處理不一致狀態。「準備」的主要做用正是經過回滾未提交的事務及同步已經提交的事務至數據文件也使得數據文件處於一致性狀態。 innobakupex命令的--apply-log選項可用於實現上述功能。以下面的命令:
innobackupex --apply-log /path/to/BACKUP-DIR
恢復操做
innobackupex --copy-back /path/to/BACKUP-DIR
增量備份:
innobackupex --incremental /backup --incremental-basedir=BASEDIR --incremental-basedir 相對增量目錄
此後若是又作修改後並無作相應的增量備份,則查看最後一次增量備份中的xtrabackup_binlog_info 文件 而後進行還原 還原準備(按序執行)及執行:
innobackupex --apply-log --redo-only BASE-DIR innobackupex --apply-log --redo-only BASE-DIR --incremental-dir=INCREMENTAL-DIR-1 innobackupex --apply-log --redo-only BASE-DIR --incremental-dir=INCREMENTAL-DIR-2
將備份文件歸檔壓縮:
innobackupex --stream=tar /backup | gzip > /backup/`date +%F_%H-%M-%S`.tar.gz
note:
從備份中恢復應該遵循步驟:
skip-networking
socket=/tmp/mysql-recovery.sock
註釋前面在my.cnf中添加的選項,並重啓
NOTE:默認狀況下InnoDB表不能經過直接複製表文件的方式在MySQL服務器之間進行移植,即使使用了innodb_file_per_table選項,而是用xtrabackup工具能夠實現此種功能,前提是innodb_file_per_table開啓
使用innobackupex導出表
innobackupex --apply-log --export /path/to/backup
使用innobackupex導入表
要在MySQL服務器上導入來自於其餘服務器的某innodb表,須要如今當前服務器上建立一個與原表結構一致的表,然後才能實現將表導入
CREATE TABLE mytable (...) ENGINE=InnoDB;
而後將此表的表空間刪除(講自動建立的表空間刪除)
ALTER TABLE mydatabase.mytable DISCARD TABLESPACE;
將導出表的服務器的mytable表的mytable.ibd和mytable.exp文件複製到當前服務器的數據目錄中,而後使用以下命令將其"導入"
ALTER TABLE mydatabase.mytable IMPORT TABLESPACE;
Xtrabackup 部分備份
建立部分備份的方式有三種:正則表達式(--include), 枚舉表文件(--tables-file)和列出要備份的數據庫(--databases)
SELECT INTO OUTFILE '' LOAD DATA INFILE '' eg. select * from students where Gender='M' into outfile '/tmp/stu.txt'; CREATE TABLE testtb LIKE students; LOAD DATA INFILE '/tmp/stu.txt' INTO TABLE testtb;
(a)使用--include 使用--include時,要求爲其指定要備份的表的完整名稱,即形如databasename.tablename,
innobackupex --include='^mageedu[.]tb1' /path/to/backup
(b)使用--tables-file 此選項的參數須要是一個文件名,此文件中每行包含一個要備份的表的完整名稱
echo -e 'mageedu.tb1\nmageedu.tb2' > /tmp/tables.txt innobackupex --tables-file=/tmp/tables.txt /path/to/backup
(c)使用--databases 此選項接受的參數爲數據名,若是要指定多個數據庫,彼此間須要以空格隔開;同時,在指定某數據庫時,也能夠只指定其中的某張表。此外,此選項也能夠接受一個文件爲參數,文件中每一行爲一個要備份的對象
innobackupex --databases="mageedu testdb" /path/to/backup
整理(preparing)部分備份 prepare部分備份的過程相似於導出表的過程,要使用--export選項進行
innobackupex --apply-log --export /pat/to/partial/backup
擴展: scale on: 向上擴展,垂直擴展 scale out:向外擴展,水平擴展 默認爲異步工做模式來完成複製,即有寫操做進來時,主服務器先寫到數據中,再寫二進制文件 SLAVE: IO thread,向主服務器請求二進制日誌中的事件 SQL thread,從中繼日誌讀取事件並在本地執行 從服務器的二進制日誌絕大多數時間內是沒有用處的,因此建議關閉。從服務器必定不能夠執行寫操做 MASTER: binlog dump,將IO thread請求的事件發送給對方(傾倒線程) 工做架構: 從服務器:有且只能有一個主服務器 MariaDB 08-10:支持多主模型,多源複製(multi-source replication) 一主多從: 寫操做只能對主服務器執行,從服務器能夠用來負責讀操做 讀寫分離:主從模型下,讓前端分發起能識別讀/寫,而且按需調度至目標主機: 環的方式對2^32取模,對範圍內有影響。 虛擬主機的方式,將分佈離散化
雙主模型(能夠分別讀寫)
必須設定雙方的自動增加屬性,以免衝突
auto_increment_increment: 步長設定爲2
auto_increment_offset 定義自動增加字段的起始值
數據會產生不一致。
A: update t1 set Salary=salary+1000 WHERE Age>=30; B: update t1 set Age=Age-3 WHERE Salary<3000;
功用:均衡讀請求,寫請求雙方一致。並不能分攤寫負載。
示例,主從複製的配置
版本:
從哪兒開始複製:
主從服務器配置過程: 主服務器:
1. 修改Server-id
2. 啓用二進制日誌
3. 建立有複製權限的帳號
grant replication slave,replication client ON *.* TO 'repluser'@'192.168.%.%' identified by 'replpass';
SHOW MASTER STATUS;
SHOW PROCESSLIST
在主服務器上查看從服務器
SHOW SLAVE HOST
從服務器: 1 . 修改Server-id 2 . 啓用中繼日誌 3 . 鏈接主服務器 4 .啓動複製線程
鏈接主服務器的命令:
Syntax: CHANGE MASTER TO option [, option] ... option: MASTER_BIND = 'interface_name' | MASTER_HOST = 'host_name' | MASTER_USER = 'user_name' | MASTER_PASSWORD = 'password' | MASTER_PORT = port_num | MASTER_CONNECT_RETRY = interval | MASTER_HEARTBEAT_PERIOD = interval | MASTER_LOG_FILE = 'master_log_name' | MASTER_LOG_POS = master_log_pos | RELAY_LOG_FILE = 'relay_log_name' | RELAY_LOG_POS = relay_log_pos | MASTER_SSL = {0|1} | MASTER_SSL_CA = 'ca_file_name' | MASTER_SSL_CAPATH = 'ca_directory_name' | MASTER_SSL_CERT = 'cert_file_name' | MASTER_SSL_KEY = 'key_file_name' | MASTER_SSL_CIPHER = 'cipher_list' | MASTER_SSL_VERIFY_SERVER_CERT = {0|1} | IGNORE_SERVER_IDS = (server_id_list)
change master to MASTER_HOST='192.168.48.135',MASTER_USER='repluser',MASTER_PASSWORD='replpass';
SHOW SLAVE STATUS; #默認不啓動 Slave_IO_Running: No Slave_SQL_Running: No
啓動SLAVE
Syntax: START SLAVE [thread_types] START SLAVE [SQL_THREAD] UNTIL MASTER_LOG_FILE = 'log_name', MASTER_LOG_POS = log_pos START SLAVE [SQL_THREAD] UNTIL RELAY_LOG_FILE = 'log_name', RELAY_LOG_POS = log_pos
啓用中途備份的思路, 先對主服務器進行備份,而後在從服務器中恢復
mysqldump -uroot -p --all-databases --flush-logs --master-data=2 --lock-all-tables> all.sql
而後在記錄主服務器的二進制日誌的開始位置
CREATE MASTER TO MASTER_HOST='192.168.48.135',MASTER_USER='repluser',MASTER_PASSWORD='replpass',MASTER_LOG_FILE='master-bin.000002',MASTER_LOG_POS=367;
MySQL簡單複製應用擴展: 一、主從服務器時間要同步(ntp): */5 * * * * /usr/bin/ntpdate 192.168.48.130 二、限制從服務器只讀,應當配置在[mysqld]中 read_only=ON note:僅能限制不具備SUPER權限用戶沒法執行寫操做 想限制全部用戶還有一種方式
FLUSH TABLES WITH READ LOCK
三、如何主從複製時的事務安全 在主服務器上配置: sync_binlog=1 (此功能默認不開啓,緣由是autocommit是開啓的狀態) 這種級別的日誌同步,會下降系統性能,可是從事務安全的角度來看是值得 的 從服務器有可能同步慢於主服務器 Possibly semi-synchronously on MySQL 5.5 (由Google免費提供給MySQL的) 主從: 從多個服務器,以均衡爲目的挑選一個響應客戶端請求的服務器:
公共緩存服務器,if 緩存命中則返回,then 使用輪詢的方式查詢各服務器,工做在旁路模式bypass. memcached: 緩存能力+API
經常使用的複製拓撲
半同步複製
須要安裝插件
Syntax: INSTALL PLUGIN plugin_name SONAME 'shared_library_name'
install plugin rpl_semi_sync_master SONAME 'semisync_master.so'; install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so';
show global variables like '%semi%'; SET GLOBAL rpl_semi_sync_master_enable=ON; SET GLOBAL rpl_semi_sync_master_timeout=1000;
rpl_semi_sync_master_enabled rpl_semi_sync_master_timeout (單位是毫秒) rpl_semi_sync_master_trace_level rpl_semi_sync_master_wait_no_slave 在從服務器上面開啓半同步的IO_Thread
stop slave IO_THREAD; start slave IO_THREAD;
note:一旦某次等待超時,會自動降級爲異步 取消semisync 插件
UNINSTALL PLUGIN rpl_semi_sync_master;
複製過濾器
master: binlog_do_db= binlog_ignore_db= slave: replicate_do_db= replicate_ignore_db= replicate_do_table=db_name.table_name replicate_ignore_table replicate_wild_do_table= 通配方式匹配 replicate_wild_ignore_table=
同時啓用時以白名單爲準,若同時在白黑名單中出現,則拒絕複製
主主複製
主服務器A上 [mysqld] server-id = 10 log-bin = mysql-bin relay-log = relay-mysql auto-increment-offset = 1 auto-increment-increment = 2
主服務器B上 [mysqld] server-id = 20 log-bin = mysql-bin relay-log = relay-mysql auto-increment-offset = 2 auto-increment-increment = 2
若此兩臺服務器均爲新創建,則個服務器指定另外一臺服務器爲本身的主服務器便可:
serverA|mysql> CHANGE MASTER TO MASTER_HOST='192.168.48.136',MASTER_USER='repluser',MASTER_PASSWORD='replpass',MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=811 serverB|mysql> CHANGE MASTER TO MASTER_HOST='192.168.48.135',MASTER_USER='repluser',MASTER_PASSWORD='replpass',MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=710
若是原來的MASTER server沒法change master to, 那麼執行
RESET SLAVE;
多主,且高可用的解決方案: MMM: Multi Master MySQL MHA: MySQL HA
SSL
基於SSL的複製,在CHANGE MASTER TO 時能夠指定:
| MASTER_SSL = {0|1} | MASTER_SSL_CA = 'ca_file_name' | MASTER_SSL_CAPATH = 'ca_directory_name' | MASTER_SSL_CERT = 'cert_file_name' | MASTER_SSL_KEY = 'key_file_name'
複製相關的文件:
master-info: 文本文件,保存了從服務器鏈接至主服務器時所須要的全部信息,通常而言一行一個值。對應於show slave status 中的值 relay-log.info: 二進制日誌和中繼日誌的座標,保存了複製位置。包括二進制日誌和中繼日誌的文件及位置。 爲了複製的安全性,應該將如下三項都設置爲1
| sync_master_info | 1 | | sync_relay_log | 1 | | sync_relay_log_info | 1 |
從服務器意外崩潰時,建議使用pt-slave-start命令來啓動slave
基於行基於語句複製: 基於語句:數據量小,易於查看,適應性強;有寫語句沒法作到精確複製,沒法對使用了觸發器、存儲過程等代碼的應用實現精確複製 基於行:可以精確完成有着觸發器、存儲過程等代碼場景中的複製。能完成幾乎全部的複製場景。沒法判斷執行 了什麼樣的SQL語句,數據量可能略大。 默認使用mixed混合型的。目前來講單純使用基於語句的幾乎不存在
從服務器落後於主服務器:
Seconds_Behind_Master: 0 從服務器落後於主服務器的時長 評估主從服務器表中的數據是否一致:
pt-table-checksum
若是數據不一致,解決辦法在從服務器上 一、從新備份並在從服務器導入數據; 二、pt-table-sync 來手動同步服務器狀態 爲了提升複製時的數據安全性,在主服務器上的設定: sync_binlog = 1 innodb_flush_log_at_trx_commit=1 此參數的值設定爲1,性能降低會較嚴重,通常設定爲2等。此時主服務器崩潰依然有可能致使從服務器沒法獲取到所有的二進制日誌事件 若是master意外崩潰致使二進制日誌中的某時間損壞,能夠在從服務器使用以下參數忽略: sql_slave_skip_counter = 0 第三方複製解決方案:Tungsten, Galera (高性能多主DB)
編譯安裝mariadb環境
yum groupinstall -y Development Tools yum install -y ncurses-devel openssl-devel openssl yum install cmake
MySQL 5.6以後才引入,使得其賦值功能的配置、監控及管理變得更加易於實現,且更加健壯 GTID: unique identifiers comprising the Server UUID and a transaction number. UUID和事務號的合併 UUID: 128bits Utilities for simplifying Replication https://lunchpad.net/mysql-utilities
mysqlreplicate
mysqlrplcheck
Provides simple verification of development and fast fault resolution
mysqlrplshow
Discovers and displays the replication topology on-demand
mysqlfailover
Enable automatic or manual failover to a slave in the event of an outage to the master
mysqlrpladmin
perform a switchover to a specific slave
多線程模型中 SQL thread應當以數據庫爲單位。最多一個數據庫只能啓動一個線程 I/O thread 應用在一從多主模型中
binlog-format:二進制日誌的格式,有row、statement和mixed幾種類型; 須要注意的是:當設置隔離級別爲READ-COMMITED必須設置二進制日誌格式爲ROW,如今MySQL官方認爲STATEMENT這個已經再也不適合繼續使用;但mixed類型在默認的事務隔離級別下,可能會致使主從數據不一致; log-slave-updates、gtid-mode、enforce-gtid-consistency、report-port和report-host:用於啓動GTID及知足附屬的其它需求 master-info-repository和relay-log-info-repository:啓用此兩項,可用於實如今崩潰時保證二進制及從服務器安全的功能; sync-master-info:啓用之可確保無信息丟失; slave-paralles-workers:設定從服務器的SQL線程數;0表示關閉多線程複製功能; binlog-checksum、master-verify-checksum和slave-sql-verify-checksum:啓用複製有關的全部校驗功能; binlog-rows-query-log-events:啓用之可用於在二進制日誌記錄事件相關的信息,可下降故障排除的複雜度; log-bin:啓用二進制日誌,這是保證複製功能的基本前提; server-id:同一個複製拓撲中的全部服務器的id號必須唯一; report-host: The host name or IP address of the slave to be reported to the master during slave registration. This value appears in the output of SHOW SLAVE HOSTS on the master server.
report-port: The TCP/IP port number for connecting to the slave, to be reported to the master during slave registration.
master-info-repository: The setting of this variable determines whether the slave logs master status and connection information to a FILE (master.info), or to a TABLE (mysql.slave_master_info)
relay-log-info-repository: This option causes the server to log its relay log info to a file or a table.
log_slave_updates: Whether updates received by a slave server from a master server should be logged to the slave's own binary log. Binary logging must be enabled on the slave for this variable to have any effect.
enforce_gtid_consistency:
一、配置master節點(MySQL5.6)
[mysqld] binlog-format=ROW log-bin=master-bin log-slave-updates=true gtid-mode=on enforce-gtid-consistency=true master-info-repository=TABLE relay-log-info-repository=TABLE sync-master-info=1 slave-parallel-workers=2 binlog-checksum=CRC32 master-verify-checksum=1 slave-sql-verify-checksum=1 binlog-rows-query-log_events=1 server-id=1 report-port=3306 port=3306 datadir=/Mysql_data/data socket=/tmp/mysql.sock report-host=master.example.com
配置slave節點
[mysqld] binlog-format=ROW log-slave-updates=true gtid-mode=on enforce-gtid-consistency=true master-info-repository=TABLE relay-log-info-repository=TABLE sync-master-info=1 slave-parallel-workers=2 binlog-checksum=CRC32 master-verify-checksum=1 slave-sql-verify-checksum=1 binlog-rows-query-log_events=1 server-id=11 report-port=3306 port=3306 log-bin=mysql-bin.log datadir=/Mysql_data/data socket=/tmp/mysql.sock report-host=slave.example.com
對於mariadb gtid-mode 和 enforce-gtid-consistency 這兩項不須要 多線程應改成slave-parallel-threads
二、建立複製用戶
GRANT REPLICATION SLAVE,REPLCIATION CLIENT ON *.* TO 'repluser'@'192.168.%.%' IDENTIFIED BY 'repluser'
三、爲備節點提供初始數據
鎖定主表,備份主節點上的數據,將其還原至從節點:若是沒有啓用GTID,在備份是須要master上使用show master status
命令查看二進制日誌文件名稱以及事件位置,以便後面啓動slave節點時使用
change master to MASTER_HOST='192.168.48.135',MASTER_USER='repluser',MASTER_PASSWORD='replpass',MASTER_USE_GTID=current_pos;
note: MASTER_USE_GTID=current_pos note: skip-slave-start #複製進程不隨mysql啓動 note: 若直接使用GTID current pos同步失敗,則先使用二進制主從複製指定master-bin log的index和pos以後再進行同步便可
MariaDB-10,須要修改
一、不支持的參數
gtid-mod=on
enforce-gtid-consistency=true
二、修改的參數
slave-parallel-workers參數修改成slave-parallel-threads
一個新的參數:MASTER_USER_GTID={current_pos|slave_pos|no}
更換master的方式:
STOP SLAVE; CHANGE MASTER TO master_host='192.168.48.135',master_port=3312; START SLAVE;
CHANGE MASTER ['connection_name'] ... FLUSH RELAY LOGS ['connection_name'] MASTER_POS_WAIT(...,['connection_name']) RESET SLAVE ['connection_name'] SHOW RELAYLOG ['connection_name'] EVENTS SHOW SLAVE ['connection_name'] STATUS SHOW ALL SLAVES STATUS START SLAVE ['connection_name'....] START ALL SLAVES STOP SLAVE ['connection_name'] STOP ALL SLAVES
查看從節點信息
SHOW SLAVE HOSTs;
多源複製時,每一個源應該使用不一樣的數據庫;多源複製目前還不支持半同步複製,只能經過異步的方式完成複製
實現讀寫分離
https://dev.mysql.com/doc/mysql-proxy/en/mysql-proxy-install.html
源碼安裝
首先解決依賴關係:
libevent 1.x or higher (1.3b or later is preferred). lua 5.1.x or higher. glib2 2.6.0 or higher. pkg-config. libtool 1.5 or higher. MySQL 5.0.x or higher developer files.
安裝:
tar zxf mysql-proxy-0.8.2.tar.gz cd mysql-proxy-0.8.2 ./configure make && make check make install
默認狀況下, mysql-proxy安裝在/usr/local/sbin/mysql-proxy,而Lua示例腳本安裝在/usr/local/share目錄中。mysql proxy的各配置參數請參見官方文檔,http://dev.mysql.com/doc/refman/5.6/en/mysql-proxy-configuration.html
RPM包安裝
使用RPM包在RHEL6上安裝mysql-proxy,其會提供配置文件及服務腳本,但沒有提供讀寫分享的腳本。
mysql-proxy啓動參數
ADMIN_USER – the user for the proxy's admin interface. You can leave the default admin user. ADMIN_PASSWORD – the password for the admin user in clear text. Change the default password for better security. ADMIN_LUA_SCRIPT – the admin script in the Lua programming language. Without this script the admin interface cannot work. You can leave the default value. PROXY_USER – the system user under which the proxy will work. By default it is mysql-proxy, and it's safe to leave it as is. PROXY_OPTIONS – proxy options such as logging level, plugins, and Lua scripts to be loaded.
其中PROXY_OPTIONS用於定義mysql-proxy工做時的重要參數
PROXY_OPTIONS="--daemon --log-level=info --log-use-syslog --plugins=proxy --plugins=admin --proxy-backend-addresses=192.168.1.102:3306 --proxy-read-only-backend-addresses=192.168.1.105:3306 --proxy-lua-script=/usr/lib/mysql-proxy/lua/proxy/rw-splitting.lua"
#!/bin/bash # # mysql-proxy This script starts and stops the mysql-proxy daemon # # chkconfig: - 78 30 # processname: mysql-proxy # description: mysql-proxy is a proxy daemon for mysql # Source function library. . /etc/rc.d/init.d/functions prog="/usr/local/mysql-proxy/bin/mysql-proxy" # Source networking configuration. if [ -f /etc/sysconfig/network ]; then . /etc/sysconfig/network fi # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 # Set default mysql-proxy configuration. ADMIN_USER="admin" ADMIN_PASSWD="admin" ADMIN_LUA_SCRIPT="/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua" PROXY_OPTIONS="--daemon" PROXY_PID=/var/run/mysql-proxy.pid PROXY_USER="mysql-proxy" # Source mysql-proxy configuration. if [ -f /etc/sysconfig/mysql-proxy ]; then . /etc/sysconfig/mysql-proxy fi RETVAL=0 start() { echo -n $"Starting $prog: " daemon $prog $PROXY_OPTIONS --pid-file=$PROXY_PID --proxy-address="$PROXY_ADDRESS" --user=$PROXY_USER --admin-username="$ADMIN_USER" --admin-lua-script="$ADMIN_LUA_SCRIPT" --admin-password="$ADMIN_PASSWORD" RETVAL=$? echo if [ $RETVAL -eq 0 ]; then touch /var/lock/subsys/mysql-proxy fi } stop() { echo -n $"Stopping $prog: " killproc -p $PROXY_PID -d 3 $prog RETVAL=$? echo if [ $RETVAL -eq 0 ]; then rm -f /var/lock/subsys/mysql-proxy rm -f $PROXY_PID fi } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart) stop start ;; condrestart|try-restart) if status -p $PROXY_PIDFILE $prog >&/dev/null; then stop start fi ;; status) status -p $PROXY_PID $prog ;; *) echo "Usage: $0 {start|stop|restart|reload|status|condrestart|try-restart}" RETVAL=1 ;; esac exit $RETVAL
建立mysql-proxy用戶並提供mysql-proxy配置文件
ADMIN_USER="admin" ADMIN_PASSWORD="admin" ADMIN_ADDRESS="" ADMIN_LUA_SCRIPT="/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua" PROXY_ADDRESS="" PROXY_USER="mysql-proxy" PROXY_OPTIONS="--daemon --log-level=info --log-use-syslog --plugins=proxy --plugins=admin --proxy-backend-addresses=192.168.48.135:3306 --proxy-read-only-backend-addresses=192.168.48.136:3306 --proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua"
mysql-proxy 配置選項
mysql-proxy的配置選項大體可分爲幫助選項、管理選項、代理選項及應用程序選項幾類,下面一塊兒去介紹它們。 --help --help-admin --help-proxy --help-all ———— 以上四個選項均用於獲取幫助信息; --proxy-address=host:port ———— 代理服務監聽的地址和端口; --admin-address=host:port ———— 管理模塊監聽的地址和端口; --proxy-backend-addresses=host:port ———— 後端mysql服務器的地址和端口; --proxy-read-only-backend-addresses=host:port ———— 後端只讀mysql服務器的地址和端口; --proxy-lua-script=file_name ———— 完成mysql代理功能的Lua腳本; --daemon ———— 以守護進程模式啓動mysql-proxy; --keepalive ———— 在mysql-proxy崩潰時嘗試重啓之; --log-file=/path/to/log_file_name ———— 日誌文件名稱; --log-level=level ———— 日誌級別; --log-use-syslog ———— 基於syslog記錄日誌; --plugins=plugin,.. ———— 在mysql-proxy啓動時加載的插件; --user=user_name ———— 運行mysql-proxy進程的用戶; --defaults-file=/path/to/conf_file_name ———— 默認使用的配置文件路徑;其配置段使用[mysql-proxy]標識; --proxy-skip-profiling ———— 禁用profile; --pid-file=/path/to/pid_file_name ———— 進程文件名;
建立admin.lua並保存置/usr/local/mysql-proxy/share/doc/mysql-proxy
--[[ $%BEGINLICENSE%$ Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA $%ENDLICENSE%$ --]] function set_error(errmsg) proxy.response = { type = proxy.MYSQLD_PACKET_ERR, errmsg = errmsg or "error" } end function read_query(packet) if packet:byte() ~= proxy.COM_QUERY then set_error("[admin] we only handle text-based queries (COM_QUERY)") return proxy.PROXY_SEND_RESULT end local query = packet:sub(2) local rows = { } local fields = { } if query:lower() == "select * from backends" then fields = { { name = "backend_ndx", type = proxy.MYSQL_TYPE_LONG }, { name = "address", type = proxy.MYSQL_TYPE_STRING }, { name = "state", type = proxy.MYSQL_TYPE_STRING }, { name = "type", type = proxy.MYSQL_TYPE_STRING }, { name = "uuid", type = proxy.MYSQL_TYPE_STRING }, { name = "connected_clients", type = proxy.MYSQL_TYPE_LONG }, } for i = 1, #proxy.global.backends do local states = { "unknown", "up", "down" } local types = { "unknown", "rw", "ro" } local b = proxy.global.backends[i] rows[#rows + 1] = { i, b.dst.name, -- configured backend address states[b.state + 1], -- the C-id is pushed down starting at 0 types[b.type + 1], -- the C-id is pushed down starting at 0 b.uuid, -- the MySQL Server's UUID if it is managed b.connected_clients -- currently connected clients } end elseif query:lower() == "select * from help" then fields = { { name = "command", type = proxy.MYSQL_TYPE_STRING }, { name = "description", type = proxy.MYSQL_TYPE_STRING }, } rows[#rows + 1] = { "SELECT * FROM help", "shows this help" } rows[#rows + 1] = { "SELECT * FROM backends", "lists the backends and their state" } else set_error("use 'SELECT * FROM help' to see the supported commands") return proxy.PROXY_SEND_RESULT end proxy.response = { type = proxy.MYSQLD_PACKET_OK, resultset = { fields = fields, rows = rows } } return proxy.PROXY_SEND_RESULT end
智能管理工具:
mysql -uadmin -padmin -h192.168.48.130 --port=4041
note:端口抓包要保證工做端口工做在混雜模式
建立讀寫分離lua文件,保存爲/usr/lib/mysql-proxy/lua/proxy/rw-splitting.lua
--[[ $%BEGINLICENSE%$ Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA $%ENDLICENSE%$ --]] --- -- a flexible statement based load balancer with connection pooling -- -- * build a connection pool of min_idle_connections for each backend and maintain -- its size -- * -- -- local commands = require("proxy.commands") local tokenizer = require("proxy.tokenizer") local lb = require("proxy.balance") local auto_config = require("proxy.auto-config") --- config -- -- connection pool if not proxy.global.config.rwsplit then proxy.global.config.rwsplit = { min_idle_connections = 4, max_idle_connections = 8, is_debug = false } end --- -- read/write splitting sends all non-transactional SELECTs to the slaves -- -- is_in_transaction tracks the state of the transactions local is_in_transaction = false -- if this was a SELECT SQL_CALC_FOUND_ROWS ... stay on the same connections local is_in_select_calc_found_rows = false --- -- get a connection to a backend -- -- as long as we don't have enough connections in the pool, create new connections -- function connect_server() local is_debug = proxy.global.config.rwsplit.is_debug -- make sure that we connect to each backend at least ones to -- keep the connections to the servers alive -- -- on read_query we can switch the backends again to another backend if is_debug then print() print("[connect_server] " .. proxy.connection.client.src.name) end local rw_ndx = 0 -- init all backends for i = 1, #proxy.global.backends do local s = proxy.global.backends[i] local pool = s.pool -- we don't have a username yet, try to find a connections which is idling local cur_idle = pool.users[""].cur_idle_connections pool.min_idle_connections = proxy.global.config.rwsplit.min_idle_connections pool.max_idle_connections = proxy.global.config.rwsplit.max_idle_connections if is_debug then print(" [".. i .."].connected_clients = " .. s.connected_clients) print(" [".. i .."].pool.cur_idle = " .. cur_idle) print(" [".. i .."].pool.max_idle = " .. pool.max_idle_connections) print(" [".. i .."].pool.min_idle = " .. pool.min_idle_connections) print(" [".. i .."].type = " .. s.type) print(" [".. i .."].state = " .. s.state) end -- prefer connections to the master if s.type == proxy.BACKEND_TYPE_RW and s.state ~= proxy.BACKEND_STATE_DOWN and cur_idle < pool.min_idle_connections then proxy.connection.backend_ndx = i break elseif s.type == proxy.BACKEND_TYPE_RO and s.state ~= proxy.BACKEND_STATE_DOWN and cur_idle < pool.min_idle_connections then proxy.connection.backend_ndx = i break elseif s.type == proxy.BACKEND_TYPE_RW and s.state ~= proxy.BACKEND_STATE_DOWN and rw_ndx == 0 then rw_ndx = i end end if proxy.connection.backend_ndx == 0 then if is_debug then print(" [" .. rw_ndx .. "] taking master as default") end proxy.connection.backend_ndx = rw_ndx end -- pick a random backend -- -- we someone have to skip DOWN backends -- ok, did we got a backend ? if proxy.connection.server then if is_debug then print(" using pooled connection from: " .. proxy.connection.backend_ndx) end -- stay with it return proxy.PROXY_IGNORE_RESULT end if is_debug then print(" [" .. proxy.connection.backend_ndx .. "] idle-conns below min-idle") end -- open a new connection end --- -- put the successfully authed connection into the connection pool -- -- @param auth the context information for the auth -- -- auth.packet is the packet function read_auth_result( auth ) if is_debug then print("[read_auth_result] " .. proxy.connection.client.src.name) end if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then -- auth was fine, disconnect from the server proxy.connection.backend_ndx = 0 elseif auth.packet:byte() == proxy.MYSQLD_PACKET_EOF then -- we received either a -- -- * MYSQLD_PACKET_ERR and the auth failed or -- * MYSQLD_PACKET_EOF which means a OLD PASSWORD (4.0) was sent print("(read_auth_result) ... not ok yet"); elseif auth.packet:byte() == proxy.MYSQLD_PACKET_ERR then -- auth failed end end --- -- read/write splitting function read_query( packet ) local is_debug = proxy.global.config.rwsplit.is_debug local cmd = commands.parse(packet) local c = proxy.connection.client local r = auto_config.handle(cmd) if r then return r end local tokens local norm_query -- looks like we have to forward this statement to a backend if is_debug then print("[read_query] " .. proxy.connection.client.src.name) print(" current backend = " .. proxy.connection.backend_ndx) print(" client default db = " .. c.default_db) print(" client username = " .. c.username) if cmd.type == proxy.COM_QUERY then print(" query = " .. cmd.query) end end if cmd.type == proxy.COM_QUIT then -- don't send COM_QUIT to the backend. We manage the connection -- in all aspects. proxy.response = { type = proxy.MYSQLD_PACKET_OK, } if is_debug then print(" (QUIT) current backend = " .. proxy.connection.backend_ndx) end return proxy.PROXY_SEND_RESULT end -- COM_BINLOG_DUMP packet can't be balanced -- -- so we must send it always to the master if cmd.type == proxy.COM_BINLOG_DUMP then -- if we don't have a backend selected, let's pick the master -- if proxy.connection.backend_ndx == 0 then proxy.connection.backend_ndx = lb.idle_failsafe_rw() end return end proxy.queries:append(1, packet, { resultset_is_needed = true }) -- read/write splitting -- -- send all non-transactional SELECTs to a slave if not is_in_transaction and cmd.type == proxy.COM_QUERY then tokens = tokens or assert(tokenizer.tokenize(cmd.query)) local stmt = tokenizer.first_stmt_token(tokens) if stmt.token_name == "TK_SQL_SELECT" then is_in_select_calc_found_rows = false local is_insert_id = false for i = 1, #tokens do local token = tokens[i] -- SQL_CALC_FOUND_ROWS + FOUND_ROWS() have to be executed -- on the same connection -- print("token: " .. token.token_name) -- print(" val: " .. token.text) if not is_in_select_calc_found_rows and token.token_name == "TK_SQL_SQL_CALC_FOUND_ROWS" then is_in_select_calc_found_rows = true elseif not is_insert_id and token.token_name == "TK_LITERAL" then local utext = token.text:upper() if utext == "LAST_INSERT_ID" or utext == "@@INSERT_ID" then is_insert_id = true end end -- we found the two special token, we can't find more if is_insert_id and is_in_select_calc_found_rows then break end end -- if we ask for the last-insert-id we have to ask it on the original -- connection if not is_insert_id then local backend_ndx = lb.idle_ro() if backend_ndx > 0 then proxy.connection.backend_ndx = backend_ndx end else print(" found a SELECT LAST_INSERT_ID(), staying on the same backend") end end end -- no backend selected yet, pick a master if proxy.connection.backend_ndx == 0 then -- we don't have a backend right now -- -- let's pick a master as a good default -- proxy.connection.backend_ndx = lb.idle_failsafe_rw() end -- by now we should have a backend -- -- in case the master is down, we have to close the client connections -- otherwise we can go on if proxy.connection.backend_ndx == 0 then return proxy.PROXY_SEND_QUERY end local s = proxy.connection.server -- if client and server db don't match, adjust the server-side -- -- skip it if we send a INIT_DB anyway if cmd.type ~= proxy.COM_INIT_DB and c.default_db and c.default_db ~= s.default_db then print(" server default db: " .. s.default_db) print(" client default db: " .. c.default_db) print(" syncronizing") proxy.queries:prepend(2, string.char(proxy.COM_INIT_DB) .. c.default_db, { resultset_is_needed = true }) end -- send to master if is_debug then if proxy.connection.backend_ndx > 0 then local b = proxy.global.backends[proxy.connection.backend_ndx] print(" sending to backend : " .. b.dst.name); print(" is_slave : " .. tostring(b.type == proxy.BACKEND_TYPE_RO)); print(" server default db: " .. s.default_db) print(" server username : " .. s.username) end print(" in_trans : " .. tostring(is_in_transaction)) print(" in_calc_found : " .. tostring(is_in_select_calc_found_rows)) print(" COM_QUERY : " .. tostring(cmd.type == proxy.COM_QUERY)) end return proxy.PROXY_SEND_QUERY end --- -- as long as we are in a transaction keep the connection -- otherwise release it so another client can use it function read_query_result( inj ) local is_debug = proxy.global.config.rwsplit.is_debug local res = assert(inj.resultset) local flags = res.flags if inj.id ~= 1 then -- ignore the result of the USE <default_db> -- the DB might not exist on the backend, what do do ? -- if inj.id == 2 then -- the injected INIT_DB failed as the slave doesn't have this DB -- or doesn't have permissions to read from it if res.query_status == proxy.MYSQLD_PACKET_ERR then proxy.queries:reset() proxy.response = { type = proxy.MYSQLD_PACKET_ERR, errmsg = "can't change DB ".. proxy.connection.client.default_db .. " to on slave " .. proxy.global.backends[proxy.connection.backend_ndx].dst.name } return proxy.PROXY_SEND_RESULT end end return proxy.PROXY_IGNORE_RESULT end is_in_transaction = flags.in_trans local have_last_insert_id = (res.insert_id and (res.insert_id > 0)) if not is_in_transaction and not is_in_select_calc_found_rows and not have_last_insert_id then -- release the backend proxy.connection.backend_ndx = 0 elseif is_debug then print("(read_query_result) staying on the same backend") print(" in_trans : " .. tostring(is_in_transaction)) print(" in_calc_found : " .. tostring(is_in_select_calc_found_rows)) print(" have_insert_id : " .. tostring(have_last_insert_id)) end end --- -- close the connections if we have enough connections in the pool -- -- @return nil - close connection -- IGNORE_RESULT - store connection in the pool function disconnect_client() local is_debug = proxy.global.config.rwsplit.is_debug if is_debug then print("[disconnect_client] " .. proxy.connection.client.src.name) end -- make sure we are disconnection from the connection -- to move the connection into the pool proxy.connection.backend_ndx = 0 end
Apple使用PgSQL MySQL並不區分下劃線和破折線
RDMBS設計範式: 設計關係數據庫時,聽從不一樣的規範要求,設計出合理的關係型數據庫,這些不一樣的規範要求被稱爲不一樣的範式,各類範式呈遞次規範,越高的範式數據庫冗餘越小。
目前關係數據庫有六種範式:第一範式(1NF)、第二範式(2NF)、第三範式(3NF)、巴德斯科範式(BCNF)、第四範式(4NF)和第五範式(5NF,又稱完美範式)。知足最低要求的範式是第一範式(1NF)。在第一範式的基礎上進一步知足更多規範要求的稱爲第二範式(2NF),其他範式以次類推。通常說來,數據庫只需知足第三範式(3NF)就好了。
(1) 第一範式(1NF)
所謂第一範式(1NF)是指在關係模型中,對域添加的一個規範要求,全部的域都應該是原子性的,即數據庫表的每一列都是不可分割的原子數據項,而不能是集合,數組,記錄等非原子數據項。即實體中的某個屬性有多個值時,必須拆分爲不一樣的屬性。在符合第一範式(1NF)表中的每一個域值只能是實體的一個屬性或一個屬性的一部分。簡而言之,第一範式就是無重複的域。
說明:在任何一個關係數據庫中,第一範式(1NF)是對關係模式的設計基本要求,通常設計中都必須知足第一範式(1NF)。不過有些關係模型中突破了1NF的限制,這種稱爲非1NF的關係模型。換句話說,是否必須知足1NF的最低要求,主要依賴於所使用的關係模型。
(2) 第二範式(2NF)
第二範式(2NF)是在第一範式(1NF)的基礎上創建起來的,即知足第二範式(2NF)必須先知足第一範式(1NF)。第二範式(2NF)要求數據庫表中的每一個實例或記錄必須能夠被惟一地區分。選取一個能區分每一個實體的屬性或屬性組,做爲實體的惟一標識。
第二範式(2NF)要求實體的屬性徹底依賴於主關鍵字。所謂徹底依賴是指不能存在僅依賴主關鍵字一部分的屬性,若是存在,那麼這個屬性和主關鍵字的這一部分應該分離出來造成一個新的實體,新實體與原實體之間是一對多的關係。爲實現區分一般須要爲表加上一個列,以存儲各個實例的惟一標識。簡而言之,第二範式就是在第一範式的基礎上屬性徹底依賴於主鍵。
(3) 第三範式(3NF)
第三範式(3NF)是第二範式(2NF)的一個子集,即知足第三範式(3NF)必須知足第二範式(2NF)。簡而言之,第三範式(3NF)要求一個關係中不包含已在其它關係已包含的非主關鍵字信息。簡而言之,第三範式就是屬性不依賴於其它非主屬性,也就是在知足2NF的基礎上,任何非主屬性不得傳遞依賴於主屬性。