關係型數據庫:javascript
Oracle、DB二、Microsoft SQL Server、Microsoft Access、MySQLphp
非關係型數據庫:html
NoSql、Cloudant、MongoDb、redis、HBasejava
兩種數據庫之間的區別:python
關係型數據庫mysql
關係型數據庫的特性
redis
一、關係型數據庫,是指採用了關係模型來組織數據的數據庫;算法
二、關係型數據庫的最大特色就是事務的一致性;sql
三、簡單來講,關係模型指的就是二維表格模型,而一個關係型數據庫就是由二維表及其之間的聯繫所組成的一個數據組織。數據庫
關係型數據庫的優勢
一、容易理解:二維表結構是很是貼近邏輯世界一個概念,關係模型相對網狀、層次等其餘模型來講更容易理解;
二、使用方便:通用的SQL語言使得操做關係型數據庫很是方便;
三、易於維護:豐富的完整性(實體完整性、參照完整性和用戶定義的完整性)大大減低了數據冗餘和數據不一致的機率;
四、支持SQL,可用於複雜的查詢。
關係型數據庫的缺點
一、爲了維護一致性所付出的巨大代價就是其讀寫性能比較差;
二、固定的表結構;
三、高併發讀寫需求;
四、海量數據的高效率讀寫;
非關係型數據庫
非關係型數據庫的特性
一、使用鍵值對存儲數據;
二、分佈式;
三、通常不支持ACID特性;
四、非關係型數據庫嚴格上不是一種數據庫,應該是一種數據結構化存儲方法的集合。
非關係型數據庫的優勢
一、無需通過sql層的解析,讀寫性能很高;
二、基於鍵值對,數據沒有耦合性,容易擴展;
三、存儲數據的格式:nosql的存儲格式是key,value形式、文檔形式、圖片形式等等,文檔形式、圖片形式等等,而關係型數據庫則只支持基礎類型。
非關係型數據庫的缺點
一、不提供sql支持,學習和使用成本較高;
二、無事務處理,附加功能bi和報表等支持也很差;
MySQL存儲引擎簡介
MySQL支持數個存儲引擎做爲對不一樣表的類型的處理器。MySQL存儲引擎包括處理事務安全表的引擎和處理非事務安全表的引擎:
MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜索能力。MyISAM在全部MySQL配置裏被支持,它是默認的存儲引擎,除非你配置MySQL默認使用另一個引擎。
MEMORY存儲引擎提供「內存中」表。MERGE存儲引擎容許集合將被處理一樣的MyISAM表做爲一個單獨的表。就像MyISAM同樣,MEMORY和MERGE存儲引擎處理非事務表,這兩個引擎也都被默認包含在MySQL中。
注:MEMORY存儲引擎正式地被肯定爲HEAP引擎。
InnoDB和BDB存儲引擎提供事務安全表。BDB被包含在爲支持它的操做系統發佈的MySQL-Max二進制分發版裏。InnoDB也默認被包括在所 有MySQL 5.1二進制分發版裏,你能夠按照喜愛經過配置MySQL來容許或禁止任一引擎。
EXAMPLE存儲引擎是一個「存根」引擎,它不作什麼。你能夠用這個引擎建立表,但沒有數據被存儲於其中或從其中檢索。這個引擎的目的是服務,在 MySQL源代碼中的一個例子,它演示說明如何開始編寫新存儲引擎。一樣,它的主要興趣是對開發者。
NDB Cluster是被MySQL Cluster用來實現分割到多臺計算機上的表的存儲引擎。它在MySQL-Max 5.1二進制分發版裏提供。這個存儲引擎當前只被Linux, Solaris, 和Mac OS X 支持。在將來的MySQL分發版中,咱們想要添加其它平臺對這個引擎的支持,包括Windows。
ARCHIVE存儲引擎被用來無索引地,很是小地覆蓋存儲的大量數據。
CSV存儲引擎把數據以逗號分隔的格式存儲在文本文件中。
BLACKHOLE存儲引擎接受但不存儲數據,而且檢索老是返回一個空集。
FEDERATED存儲引擎把數據存在遠程數據庫中。在MySQL 5.1中,它只和MySQL一塊兒工做,使用MySQL C Client API。在將來的分發版中,咱們想要讓它使用其它驅動器或客戶端鏈接方法鏈接到另外的數據源。
如何選擇最適合你的存儲引擎呢?
MyISAM:默認的MySQL插件式存儲引擎,它是在Web、數據倉儲和其餘應用環境下最常使用的存儲引擎之一。注意,經過更改STORAGE_ENGINE配置變量,可以方便地更改MySQL服務器的默認存儲引擎。
InnoDB:用於事務處理應用程序,具備衆多特性,包括ACID事務支持。(提供行級鎖)
BDB:可替代InnoDB的事務引擎,支持COMMIT、ROLLBACK和其餘事務特性。
Memory:將全部數據保存在RAM中,在須要快速查找引用和其餘相似數據的環境下,可提供極快的訪問。
Merge:容許MySQL DBA或開發人員將一系列等同的MyISAM表以邏輯方式組合在一塊兒,並做爲1個對象引用它們。對於諸如數據倉儲等VLDB環境十分適合。
Archive:爲大量不多引用的歷史、歸檔、或安全審計信息的存儲和檢索提供了完美的解決方案。
Federated:可以將多個分離的MySQL服務器連接起來,從多個物理服務器建立一個邏輯數據庫。十分適合於分佈式環境或數據集市環境。
Cluster/NDB:MySQL的簇式數據庫引擎,尤爲適合於具備高性能查找要求的應用程序,這類查找需求還要求具備最高的正常工做時間和可用性。
Other:其餘存儲引擎包括CSV(引用由逗號隔開的用做數據庫表的文件),Blackhole(用於臨時禁止對數據庫的應用程序輸入),以及Example引擎(可爲快速建立定製的插件式存儲引擎提供幫助)。
MySQL存儲引擎比較
MyISAM
MyISAM是MySQL的默認存儲引擎。MyISAM不支持事務、也不支持外鍵,但其訪問(讀)速度快,對事務完整性沒有要求。
MyISAM除了提供ISAM裏所沒有的索引和字段管理的大量功能,MyISAM還使用一種表格鎖定的機制,來優化多個併發的讀寫操做,其代價是你須要常常運行OPTIMIZE TABLE命令,來恢復被更新機制所浪費的空間。MyISAM還有一些有用的擴展,例如用來修復數據庫文件的MyISAMCHK工具和用來恢復浪費空間的MyISAMPACK工具。MYISAM強調了快速讀取操做,這可能就是爲何MySQL受到了WEB開發如此青睞的主要緣由:在WEB開發中你所進行的大量數據操做都是讀取操做。因此,大多數虛擬主機提供商和INTERNET平臺提供商只容許使用MYISAM格式。MyISAM格式的一個重要缺陷就是不能在表損壞後恢復數據。
InnoDB存儲引擎提供了具備提交、回滾和崩潰恢復能力的事務安全。可是比起MyISAM存儲引擎,InnoDB寫的處理效率差一些而且會佔用更多的磁盤空間以保留數據和索引。
MEMORY/HEAP
MEMORY(又叫HEAP)存儲引擎使用存在內存中的內容來建立表。每一個MEMORY表只實際對應一個磁盤文件。MEMORY類型的表訪問很是得快,由於它的數據是放在內存中的,而且默認使用HASH索引。可是一旦服務關閉,表中的數據就會丟失掉。 HEAP容許只駐留在內存裏的臨時表格。駐留在內存裏讓HEAP要比ISAM和MYISAM都快,可是它所管理的數據是不穩定的,並且若是在關機以前沒有進行保存,那麼全部的數據都會丟失。在數據行被刪除的時候,HEAP也不會浪費大量的空間。HEAP表格在你須要使用SELECT表達式來選擇和操控數據的時候很是有用。
MEMORY主要用於那些內容變化不頻繁的代碼表,或者做爲統計操做的中間結果表,便於高效地堆中間結果進行分析並獲得最終的統計結果。
MERGE
MERGE存儲引擎是一組MyISAM表的組合,這些MyISAM表必須結構徹底相同。MERGE表自己沒有數據,對MERGE類型的表進行查詢、更新、刪除的操做,就是對內部的MyISAM表進行的。 MERGE用於將一系列等同的MyISAM表以邏輯方式組合在一塊兒,並做爲一個對象引用它。MERGE表的優勢在於能夠突破對單個MyISAM表大小的限制,經過將不一樣的表分佈在多個磁盤上,能夠有效的改善MERGE表的訪問效率。
MyISAM與InnoDB的區別
InnoDB和MyISAM是許多人在使用MySQL時最經常使用的兩個表類型,這兩個表類型各有優劣,視具體應用而定。基本的差異爲:MyISAM類型不支持事務處理等高級處理,而InnoDB類型支持。MyISAM類型的表強調的是性能,其執行數度比InnoDB類型更快,可是不提供事務支持,而InnoDB提供事務支持已經外部鍵等高級數據庫功能。
MyISAM表還支持3中不一樣的存儲格式: 靜態表 、 動態表 、壓縮表
靜態表是默認的存儲格式,靜態表中的字段都是非變長的字段,優勢是:存儲很是迅速,容易緩存,出現故障容易恢復;缺點是:佔用的空間一般比動態表多。(注意: 在存儲時,列的寬度不足時,用空格補足,當時在訪問的時候並不會獲得這些空格)
動態表的字段是變長的,優勢是:佔用的空間相對較少,可是頻繁地更新刪除記錄會產生碎片,須要按期改善性能,而且出現故障的時候恢復相對比較困難。
壓縮表佔用磁盤空間小,每一個記錄是被單獨壓縮的,因此只有很是小的訪問開支。
InnoDB存儲方式爲兩種:使用共享表空間存儲 、使用多表空間
什麼是範式?
簡言之就是,數據庫設計對數據的存儲性能,還有開發人員對數據的操做都有莫大的關係。因此創建科學的,規範的的數據庫是須要知足一些規範的來優化數據數據存儲方式。在關係型數據庫中這些規範就能夠稱爲範式。(簡單來講,就是根據須要,來優化數據存儲方式!)
什麼是三大範式?
第一範式:當關系模式R的全部屬性都不能在分解爲更基本的數據單位時,稱R是知足第一範式的,簡記爲1NF。知足第一範式是關係模式規範化的最低要求,不然,將有不少基本操做在這樣的關係模式中實現不了。(說白了,就是關係模式R的全部屬性不能再分解了,那麼R就知足第一範式!)
特性:
一、每一列屬性都是不可再分的屬性值,確保每一列的原子性
二、兩列的屬性相近或類似或同樣,儘可能合併屬性同樣的列,確保不產生冗餘數據。
若是需求知道那個省那個市並按其分類,那麼顯然第一個表格是不容易知足需求的,也不符合第一範式。
顯然第一個表結構不但不能知足足夠多物品的要求,還會在物品少時產生冗餘。也是不符合第一範式的。
第二範式:若是關係模式R知足第一範式,而且R得全部非主屬性都徹底依賴於R的每個候選關鍵屬性,稱R知足第二範式,簡記爲2NF。(說白了,就是非主屬性都要依賴於每個關鍵屬性!)
每一行的數據只能與其中一列相關,即一行數據只作一件事。只要數據列中出現數據重複,就要把表拆分開來。
一我的同時訂幾個房間,就會出來一個訂單號多條數據,這樣子聯繫人都是重複的,就會形成數據冗餘。咱們應該把他拆開來。
這樣便實現一條數據作一件事,不摻雜複雜的關係邏輯。同時對錶數據的更新維護也更易操做。
第三範式:設R是一個知足第一範式條件的關係模式,X是R的任意屬性集,若是X非傳遞依賴於R的任意一個候選關鍵字,稱R知足第三範式,簡記爲3NF.
數據不能存在傳遞關係,即沒個屬性都跟主鍵有直接關係而不是間接關係。像:a-->b-->c 屬性之間含有這樣的關係,是不符合第三範式的。
好比Student表(學號,姓名,年齡,性別,所在院校,院校地址,院校電話)
這樣一個表結構,就存在上述關係。 學號--> 所在院校 --> (院校地址,院校電話)
這樣的表結構,咱們應該拆開來,以下。
(學號,姓名,年齡,性別,所在院校)--(所在院校,院校地址,院校電話)
什麼是事務?
事務是由一步或幾步數據庫操做序列組成邏輯執行單元,這系列操做要麼所有執行,要麼所有放棄執行。程序和事務是兩個不一樣的概念。通常而言:一段程序中可能包含多個事務。(說白了就是幾步的數據庫操做而構成的邏輯執行單元)
事務具備四個特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability)。這四個特性也簡稱ACID性。
(1)原子性:事務是應用中最小的執行單位,就如原子是天然界最小顆粒,具備不可再分的特徵同樣。事務是應用中不可再分的最小邏輯執行體。(最小了,不可再分了)
(2)一致性:事務執行的結果,必須使數據庫從一個一致性狀態,變到另外一個一致性狀態。當數據庫中只包含事務成功提交的結果時,數據庫處於一致性狀態。一致性是經過原子性來保證的。(說罷了就是白狗變成了黑狗,不能出現斑點狗!)
(3)隔離性:各個事務的執行互不干擾,任意一個事務的內部操做對其餘併發的事務,都是隔離的。也就是說:併發執行的事務之間不能看到對方的中間狀態,併發執行的事務之間不能相互影響。(說白了,就是你作你的,我作個人!)
(4)持續性:持續性也稱爲持久性,指事務一旦提交,對數據所作的任何改變,都要記錄到永久存儲器中,一般是保存進物理數據庫。(說白了就是一條道跑到黑)
MySQL如何支持事務?
MYSQL的事務處理主要有兩種方法
1.用begin,rollback,commit來實現
begin開始一個事務
rollback事務回滾
commit 事務確認
2.直接用set來改變mysql的自動提交模式
mysql默認是自動提交的,也就是你提交一個query,就直接執行!能夠經過
set autocommit = 0 禁止自動提交
set autocommit = 1 開啓自動提交
來實現事務的處理
一、顯示數據庫
1
|
SHOW DATABASES;
|
二、建立數據庫
1
2
3
4
5
|
# utf-8
CREATE DATABASE 數據庫名稱 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
# gbk
CREATE DATABASE 數據庫名稱 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;
|
三、使用數據庫
1
|
USE db_name;
|
顯示當前使用的數據庫中全部表:SHOW TABLES;
四、用戶管理
1
2
3
4
5
6
7
8
9
10
|
建立用戶
create user
'用戶名'
@
'IP地址'
identified by
'密碼'
;
刪除用戶
drop user
'用戶名'
@
'IP地址'
;
修改用戶
rename user
'用戶名'
@
'IP地址'
; to
'新用戶名'
@
'IP地址'
;;
修改密碼
set password
for
'用戶名'
@
'IP地址'
= Password(
'新密碼'
)
PS:用戶權限相關數據保存在mysql數據庫的user表中,因此也能夠直接對其進行操做(不建議)
|
五、受權管理
1
2
3
|
show grants
for
'用戶'
@
'IP地址'
-- 查看權限
grant 權限 on 數據庫.表 to
'用戶'
@
'IP地址'
-- 受權
revoke 權限 on 數據庫.表 from
'用戶'
@
'IP地址'
-- 取消權限
|
一、建立表
1
2
3
4
|
create table 表名(
列名 類型 是否能夠爲空,
列名 類型 是否能夠爲空
)ENGINE=InnoDB DEFAULT CHARSET=utf8
|
二、刪除表
1
|
drop table 表名
|
三、清空表
1
2
|
delete
from 表名
truncate table 表名
|
四、修改表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
添加列:alter table 表名 add 列名 類型
刪除列:alter table 表名 drop column 列名
修改列:
alter table 表名 modify column 列名 類型; -- 類型
alter table 表名 change 原列名 新列名 類型; -- 列名,類型
添加主鍵:
alter table 表名 add primary key(列名);
刪除主鍵:
alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;
添加外鍵:alter table 從表 add constraint 外鍵名稱(形如:FK_從表_主表) foreign key 從表(外鍵字段) references 主表(主鍵字段);
刪除外鍵:alter table 表名 drop foreign key 外鍵名稱
修改默認值:ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;
刪除默認值:ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;
|
五、基本數據類型
MySQL的數據類型大體分爲:數值、時間和字符串
一、增
1
2
3
|
insert
into
表 (列名,列名...)
values
(值,值,值...)
insert
into
表 (列名,列名...)
values
(值,值,值...),(值,值,值...)
insert
into
表 (列名,列名...)
select
(列名,列名...)
from
表
|
二、刪
1
2
|
delete
from
表
delete
from
表
where
id=1
and
name
=
'alex'
|
三、改
1
|
update
表
set
name
=
'alex'
where
id>1
|
四、查
1
2
3
|
select
*
from
表
select
*
from
表
where
id > 1
select
nid,
name
,gender
as
gg
from
表
where
id > 1
|
五、其餘
MySQL服務器的默認端口是3306。
增:建立數據表
USE database CREATE TABLE example(id INT, name VARCHAR(20), sex BOOLEAN);
刪:
ALTER TABLE 表名 DROP 屬性名; # 刪除字段 DROP TABLE 表名; # 刪除表
改:
ALTER TABLE 舊錶名 RENAME 新表名; # 修改表名 ALTER TABLE 表名 MODIFY 屬性名 數據類型; # 修改字段數據類型
查:
SELECT * FROM 表名 WHERE id=1; # 條件查詢 SELECT * FROM 表名 WHERE 字段名 BETWEEN 條件一 AND 條件二 # 範圍查詢 SELECT COUNT(*) FROM 表名; # 查詢表共有多少條記錄
觸發器:是由INSERT、UPDATE和DELETE等事件來觸發某種特定操做,知足觸發條件時,數據庫系統會執行觸發器中定義的語句,這樣能夠保證某些操做之間的一致性。
CREATE TRIGGER 觸發器名稱 BEFORE|AFTER 觸發事件 ON 表名稱 FOR EACH ROW BEGIN 執行語句 END
一、視圖
視圖只是一種邏輯對象,是一種虛擬表,它並不是物理對象,由於視圖不佔物理存儲空間,在視圖中被查詢的表稱爲視圖的基表,大多數的select語句均可以用在建立視圖中(說白了,視圖就是一種虛擬表,就像是一張電子照片)
優勢:集中用戶使用的數據,掩碼數據的複雜性,簡化權限管理以及爲向其餘應用程序輸出而從新組織數據等
二、觸發器
(1)觸發器是一個特殊的存儲過程,它是MySQL在insert、update、delete的時候自動執行的代碼塊。
(2)觸發器必須定義在特定的表上。
(3)自動執行,不能直接調用,
(說白了,觸發器其實就是一個神,他會待在本身的廟宇中,當百姓受難了,經過一些禱告儀式,如insert、update、delete,他會自動的進行降妖除魔!)
三、函數
它跟php或js中的函數幾乎同樣:須要先定義,而後調用(使用)。
只是規定,這個函數,必需要返回數據——要有返回值
四、存儲過程
存儲過程(procedure),概念相似於函數,就是把一段代碼封裝起來,當要執行這一段代碼的時候,能夠經過調用該存儲過程來實現。在封裝的語句體裏面,能夠同if/else ,case,while等控制結構,能夠進行sql編程,查看現有的存儲過程。
一、普通索引
這是最基本的索引,它沒有任何限制,好比上文中爲title字段建立的索引就是一個普通索引,MyIASM中默認的BTREE類型的索引,也是咱們大多數狀況下用到的索引。
–直接建立索引 CREATE INDEX index_name ON table(column(length)) –修改表結構的方式添加索引 ALTER TABLE table_name ADD INDEX index_name ON (column(length)) –建立表的時候同時建立索引 CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), INDEX index_name (title(length)) ) –刪除索引 DROP INDEX index_name ON table
二、 惟一索引
與普通索引相似,不一樣的就是:索引列的值必須惟一,但容許有空值(注意和主鍵不一樣)。若是是組合索引,則列值的組合必須惟一,建立方法和普通索引相似。
–建立惟一索引 CREATE UNIQUE INDEX indexName ON table(column(length)) –修改表結構 ALTER TABLE table_name ADD UNIQUE indexName ON (column(length)) –建立表的時候直接指定 CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), UNIQUE indexName (title(length)) );
三、全文索引(FULLTEXT)
MySQL從3.23.23版開始支持全文索引和全文檢索,FULLTEXT索引僅可用於 MyISAM 表;他們能夠從CHAR、VARCHAR或TEXT列中做爲CREATE TABLE語句的一部分被建立,或是隨後使用ALTER TABLE 或CREATE INDEX被添加。////對於較大的數據集,將你的資料輸入一個沒有FULLTEXT索引的表中,而後建立索引,其速度比把資料輸入現有FULLTEXT索引的速度更爲快。不過切記對於大容量的數據表,生成全文索引是一個很是消耗時間很是消耗硬盤空間的作法。
–建立表的適合添加全文索引 CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), FULLTEXT (content) ); –修改表結構添加全文索引 ALTER TABLE article ADD FULLTEXT index_content(content) –直接建立索引 CREATE FULLTEXT INDEX index_content ON article(content)
4.、單列索引、多列索引
多個單列索引與單個多列索引的查詢效果不一樣,由於執行查詢時,MySQL只能使用一個索引,會從多個索引中選擇一個限制最爲嚴格的索引。
5.、組合索引(最左前綴)
平時用的SQL查詢語句通常都有比較多的限制條件,因此爲了進一步榨取MySQL的效率,就要考慮創建組合索引。例如上表中針對title和time創建一個組合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。創建這樣的組合索引,實際上是至關於分別創建了下面兩組組合索引:
–title,time
–title
爲何沒有time這樣的組合索引呢?這是由於MySQL組合索引「最左前綴」的結果。簡單的理解就是隻從最左面的開始組合。並非只要包含這兩列的查詢都會用到該組合索引,以下面的幾個SQL所示:
–使用到上面的索引 SELECT * FROM article WHREE title='測試' AND time=1234567890; SELECT * FROM article WHREE utitle='測試'; –不使用上面的索引 SELECT * FROM article WHREE time=1234567890;
索引的最左前綴原理:
一般咱們在創建聯合索引的時候,也就是對多個字段創建索引,相信創建過索引的同窗們會發現,不管是oralce仍是mysql都會讓咱們選擇索引的順序,好比咱們想在a,b,c三個字段上創建一個聯合索引,咱們能夠選擇本身想要的優先級,a、b、c,或者是b、a、c 或者是c、a、b等順序。爲何數據庫會讓咱們選擇字段的順序呢?不都是三個字段的聯合索引麼?這裏就引出了數據庫索引的最左前綴原理。
好比:索引index1:(a,b,c)有三個字段,咱們在使用sql語句來查詢的時候,會發現不少狀況下不按照咱們想象的來走索引。
select * from table where c = '1'
這個sql語句是不會走index1索引的
select * from table where b =‘1’ and c ='2'
這個語句也不會走index1索引。
什麼語句會走index1索引呢?
答案是:
select * from table where a = '1' select * from table where a = '1' and b = ‘2’ select * from table where a = '1' and b = ‘2’ and c='3'
咱們能夠發現一個共同點,就是全部走索引index1的sql語句的查詢條件裏面都帶有a字段,那麼問題來了,index1的索引的最左邊的列字段是a,是否是查詢條件中包含a就會走索引呢?
例如:
select * from table where a = '1' and c= ‘2’
這個sql語句,按照以前的理解,包含a字段,會走索引,可是是否是全部字段都走了索引呢?
咱們來作個實驗:
我這裏有一個表:
創建了一個聯合索引,prinIdAndOrder裏面有三個字段 PARENT_ID, MENU_ORDER, MENU_NAME
接下來測試以前的語句:
EXPLAIN SELECT t.* FROM sys_menu t WHERE t.`PARENT_ID` = '0' AND t.`MENU_NAME` = '系統工具'
這一句sql就至關於以前的select * from table where a = '1' and c= ‘2’這個sql語句了,咱們來看看解釋計劃:
能夠看到走了索引prinIdAndOrder,可是旁邊的key_len=303,但道理key_len應該是大於303的,爲何呢?由於PARENT_ID字段的類型是varchar(100) NULL,因此key_len=100*3+2+1=303,可是還有MENU_NAME呢!具體的key_len的計算方法,你們能夠百度,個人表的字符集是utf-8,不一樣字符集的表的計算方式不同。這裏的解釋計劃顯示key_len只有303,說明只是走了字段PARENT_ID的索引,沒有走MENU_NAME的索引。
這也是最左前綴原理的一部分,索引index1:(a,b,c),只會走a、a,b、a,b,c 三種類型的查詢,其實這裏說的有一點問題,a,c也走,可是隻走a字段索引,不會走c字段。
另外還有一個特殊狀況說明下,select * from table where a = '1' and b > ‘2’ and c='3' 這種類型的也只會有a與b走索引,c不會走。
像select * from table where a = '1' and b > ‘2’ and c='3' 這種類型的sql語句,在a、b走完索引後,c確定是無序了,因此c就無法走索引,數據庫會以爲還不如全表掃描c字段來的快。不知道我說明白沒,感受這一塊說的始終有點牽強。
主鍵
定義:惟一標識一條記錄,不能有重複的,不容許爲空
做用:用來保證數據完整性
個數:主鍵只能有一個
ALTER TABLE 「表名」 ADD PRIMARY KEY (字段名)
外鍵
定義:表的外鍵是另外一表的主鍵, 外鍵能夠有重複的, 能夠是空值
做用:用來和其餘表創建聯繫用的
個數:一個表能夠有多個外鍵
ALTER TABLE 「表名」 ADD FOREIGN KEY (字段名) REFERENCES 「另外一張表名」( 字段名)
一、若是條件中有or,即便其中有條件帶索引也不會使用(這也是爲何儘可能少用or的緣由)
注意:要想使用or,又想讓索引生效,只能將or條件中的每一個列都加上索引
二、對於多列索引,不是使用的第一部分(第一個),則不會使用索引
三、like查詢是以%開頭
四、若是列類型是字符串,那必定要在條件中將數據使用引號引用起來,不然不使用索引
五、若是mysql估計使用全表掃描要比使用索引快,則不使用索引
其餘
一、爲何要開啓慢日誌查詢?
開啓慢查詢日誌,可讓MySQL記錄下查詢超過指定時間的語句,經過定位分析性能的瓶頸,才能更好的優化數據庫系統的性能。
二、參數說明
slow_query_log 慢查詢開啓狀態
slow_query_log_file 慢查詢日誌存放的位置(這個目錄須要MySQL的運行賬號的可寫權限,通常設置爲MySQL的數據存放目錄)
long_query_time 查詢超過多少秒才記錄
三、設置步驟
①.查看慢查詢相關參數
mysql> show variables like 'slow_query%'; +---------------------------+----------------------------------+ | Variable_name | Value | +---------------------------+----------------------------------+ | slow_query_log | OFF | | slow_query_log_file | /mysql/data/localhost-slow.log | +---------------------------+----------------------------------+ mysql> show variables like 'long_query_time'; +-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | long_query_time | 10.000000 | +-----------------+-----------+
②.設置方法
將 slow_query_log 全局變量設置爲「ON」狀態
mysql> set global slow_query_log='ON';
設置慢查詢日誌存放的位置
mysql> set global slow_query_log_file='/var/lib/mysql/test-10-226-slow.log';
查詢超過1秒就記錄
mysql> set global long_query_time=1;
通過這些操做,則慢日誌查詢也就開啓了!但這種方法只是臨時生效,mysql重啓後就會失效
因此咱們不能讓他失效,因此需進行以下操做
編輯配置文件/etc/my.cnf加入以下內容
[mysqld] slow_query_log = ON slow_query_log_file = /var/lib/mysql/test-10-226-slow.log long_query_time = 1
修改配置後重啓
mysql
systemctl restart mysqld mysql -uroot -p
使用下面命令驗證
show variables like 'slow_query%';
mysql永久開啓了漫查詢日誌功能
在命令行下mysql的數據導出有個很好用命令mysqldump,它的參數有一大把,能夠這樣查看:
mysqldump
(mysqldump命令位於mysql/bin/目錄中) //要專到mysql/bin/目錄中才能使用,直接cmd運行命令窗口使用不了,專到數據庫所在的mysql/bin/目錄中使用.
最經常使用的:
mysqldump -uroot -pmysql databasefoo table1 table2 > foo.sql
這樣就能夠將數據庫databasefoo的表table1,table2以sql形式導入foo.sql中,其中-uroot參數表示訪問數據庫的用戶名是root,若是有密碼還須要加上-p參數
C:\Users\jack> mysqldump -uroot -pmysql sva_rec date_drv > e:\date_drv.sql
mysql的數據導入也是至關便捷的,如:
mysql -uroot databasefoo < foo.sql
這樣就能夠將foo.sql的數據所有導入數據庫databasefoo
導出整個數據庫
mysqldump -u用戶名 -p密碼 數據庫名 > 導出的文件名
C:\Users\jack> mysqldump -uroot -pmysql sva_rec > e:\sva_rec.sql
導出一個表,包括表結構和數據
mysqldump -u用戶名 -p 密碼 數據庫名 表名> 導出的文件名
C:\Users\jack> mysqldump -uroot -pmysql sva_rec date_rec_drv> e:\date_rec_drv.sql
導出一個數據庫結構
C:\Users\jack> mysqldump -uroot -pmysql -d sva_rec > e:\sva_rec.sql
導出一個表,只有表結構
mysqldump -u用戶名 -p 密碼 -d數據庫名 表名> 導出的文件名
C:\Users\jack> mysqldump -uroot -pmysql -d sva_rec date_rec_drv> e:\date_rec_drv.sql
導入數據庫
經常使用source 命令
進入mysql數據庫控制檯,
如mysql -u root -p
mysql>use 數據庫
而後使用source命令,後面參數爲腳本文件(如這裏用到的.sql)
mysql>source d:wcnc_db.sql
關於數據庫的優化方案,可參考下面的連接
就長度來講:
♣ char的長度是不可變的;
♣ 而varchar的長度是可變的,也就是說,定義一個char[10]和varchar[10],若是存進去的是‘csdn’,那麼char所佔的長度依然爲10,除了字符‘csdn’外,後面跟六個空格,而varchar就立馬把長度變爲4了,取數據的時候,char類型的要用trim()去掉多餘的空格,而varchar是不須要的,儘管如此,char的存取速度仍是要比varchar要快得多,由於其長度固定,方便程序的存儲與查找;可是char也爲此付出的是空間的代價,由於其長度固定,因此不免會有多餘的空格佔位符佔據空間,可謂是以空間換取時間效率,而varchar是以空間效率爲首位的。
就存儲方式來講:
♣ char的存儲方式是,對英文字符(ASCII)佔用1個字節,對一個漢字佔用兩個字節;
♣ 而varchar的存儲方式是,對每一個英文字符佔用2個字節,漢字也佔用2個字節,二者的存儲數據都非unicode的字符數據。
https://www.cnblogs.com/xinysu/p/7860609.html
在mysql中limit能夠實現快速分頁,可是若是數據到了幾百萬時咱們的limit必須優化纔能有效的合理的實現分頁了,不然可能卡死你的服務器哦。
當一個表數據有幾百萬的數據的時候成了問題!
如 * from table limit 0,10 這個沒有問題 當 limit 200000,10 的時候數據讀取就很慢,能夠按照一下方法解決第一頁會很快
PERCONA PERFORMANCE CONFERENCE 2009上,來自雅虎的幾位工程師帶來了一篇」EfficientPagination Using MySQL」的報告
limit10000,20的意思掃描知足條件的10020行,扔掉前面的10000行,返回最後的20行,問題就在這裏。
LIMIT 451350 , 30 掃描了45萬多行,怪不得慢的都堵死了。
可是,limit 30 這樣的語句僅僅掃描30行。
那麼若是咱們以前記錄了最大ID,就能夠在這裏作文章
舉個例子
平常分頁SQL語句
select id,name,content from users order by id asc limit 100000,20
掃描100020行
若是記錄了上次的最大ID
select id,name,content from users where id>10073 order by id asc limit 20
掃描20行。
總數據有500萬左右
如下例子 當 select * from wl_tagindex where byname='f' order by id limit 300000,10 執行時間是 3.21s
優化後:
select * from ( select id from wl_tagindex where byname='f' order by id limit 300000,10 ) a left join wl_tagindex b on a.id=b.id
執行時間爲 0.11s 速度明顯提高
這裏須要說明的是 我這裏用到的字段是 byname ,id 須要把這兩個字段作複合索引,不然的話效果提高不明顯
總結
當一個數據庫表過於龐大,LIMIT offset, length中的offset值過大,則SQL查詢語句會很是緩慢,你需增長order by,而且order by字段須要創建索引。
若是使用子查詢去優化LIMIT的話,則子查詢必須是連續的,某種意義來說,子查詢不該該有where條件,where會過濾數據,使數據失去連續性。
若是你查詢的記錄比較大,而且數據傳輸量比較大,好比包含了text類型的field,則能夠經過創建子查詢。
SELECT id,title,content FROM items WHERE id IN (SELECT id FROM items ORDER BY id limit 900000, 10);
若是limit語句的offset較大,你能夠經過傳遞pk鍵值來減少offset = 0,這個主鍵最好是int類型而且auto_increment
SELECT * FROM users WHERE uid > 456891 ORDER BY uid LIMIT 0, 10;
這條語句,大意以下:
SELECT * FROM users WHERE uid >= (SELECT uid FROM users ORDER BY uid limit 895682, 1) limit 0, 10;
若是limit的offset值過大,用戶也會翻頁疲勞,你能夠設置一個offset最大的,超過了能夠另行處理,通常連續翻頁過大,用戶體驗不好,則應該提供更優的用戶體驗給用戶。
關於limit 分頁優化方法請參考下面的連接:
什麼是索引合併?
下面咱們看下mysql文檔中對索引合併的說明:
The Index Merge method is used to retrieve rows with several range scans and to merge their results into one. The merge can produce unions, intersections, or unions-of-intersections of its underlying scans. This access method merges index scans from a single table; it does not merge scans across multiple tables.
根據官方文檔中的說明,咱們能夠了解到:
一、索引合併是把幾個索引的範圍掃描合併成一個索引。
二、索引合併的時候,會對索引進行並集,交集或者先交集再並集操做,以便合併成一個索引。
三、這些須要合併的索引只能是一個表的。不能對多表進行索引合併。
怎麼肯定使用了索引合併?
在使用explain對sql語句進行操做時,若是使用了索引合併,那麼在輸出內容的type列會顯示 index_merge,key列會顯示出全部使用的索引。以下:
使用索引合併的示例
數據表結構:
mysql> show create table test\G *************************** 1. row *************************** Table: test Create Table: CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `key1_part1` int(11) NOT NULL DEFAULT '0', `key1_part2` int(11) NOT NULL DEFAULT '0', `key2_part1` int(11) NOT NULL DEFAULT '0', `key2_part2` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `key1` (`key1_part1`,`key1_part2`), KEY `key2` (`key2_part1`,`key2_part2`) ) ENGINE=MyISAM AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
數據
使用索引合併的案例
mysql> explain select * from test where (key1_part1=4 and key1_part2=4) or key2_part1=4\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: test type: index_merge possible_keys: key1,key2 key: key1,key2 key_len: 8,4 ref: NULL rows: 3 Extra: Using sort_union(key1,key2); Using where 1 row in set (0.00 sec)
未使用索引合併的案例
mysql> explain select * from test where (key1_part1=1 and key1_part2=1) or key2_part1=4\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: test type: ALL possible_keys: key1,key2 key: NULL key_len: NULL ref: NULL rows: 29 Extra: Using where 1 row in set (0.00 sec)
從上面的兩個案例你們能夠發現,相同模式的sql語句,可能有時能使用索引,有時不能使用索引。是否能使用索引,取決於mysql查詢優化器對統計數據分析後,是否定爲使用索引更快。所以,單純的討論一條sql是否能夠使用索引有點片面,還須要考慮數據。
注意事項
mysql5.6.7以前的版本遵照range優先的原則。也就是說,當一個索引的一個連續段,包含全部符合查詢要求的數據時,哪怕索引合併能提供效率,也再也不使用索引合併。舉個例子:
mysql> explain select * from test where (key1_part1=1 and key1_part2=1) and key2_part1=1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: test type: ref possible_keys: key1,key2 key: key2 key_len: 4 ref: const rows: 9 Extra: Using where 1 row in set (0.00 sec)
上面符合查詢要求的結果只有一條,而這一條記錄被索引key2所包含。
能夠看到這條sql語句使用了key2索引。可是這個並非最快的執行方式。其實,把索引key1和索引key2進行索引合併,取交集後,就發現只有一條記錄適合。應該查詢效率會更快。
tips:這條sql語句未在mysql5.6.7以後版本執行驗證,以上爲理論推導。有興趣的話,您能夠到mysql5.6.7以後版本上驗證下。
什麼是覆蓋索引?
一般開發人員會根據查詢的where條件來建立合適的索引,可是優秀的索引設計應該考慮到整個查詢。其實mysql能夠使用索引來直接獲取列的數據。若是索引的葉子節點包含了要查詢的數據,那麼就不用回表查詢了,也就是說這種索引包含(亦稱覆蓋)全部須要查詢的字段的值,咱們稱這種索引爲覆蓋索引。
注:引入數據表t_user,插入約1千萬條記錄,用做下文例子使用。
一、工欲善其事,必先利其器
explain命令是查看查詢優化器如何決定執行查詢的主要方法。要使用此命令,只須要在select關鍵字以前添加這個命令便可。當執行查詢時,它會返回信息,顯示出執行計劃中的每一部分和執行的次序,而並不是真正執行這個查詢。以下所示,是執行explain的顯示結果,其中sql語句中的\G表示將輸出按列顯示:
當發起一個被索引覆蓋的查詢時,在explain的Extra列能夠看到 Using index的標識。
二、場景查詢表中name列有值的記錄數
(1)查詢表中name列有值的記錄數
查詢語句用SQL_NO_CACHE關鍵字來禁止緩存查詢結果。此查詢耗時6.43秒
(2)執行計劃
從圖的執行計劃得知,type:ALL,表示MySQL掃描整張表,從頭至尾去找到須要的行。下面對此查詢列創建索引。
(3)爲name列創建索引
(4)從新執行的查詢sql
如圖所示,爲name列創建索引以後,從新執行查詢。此時查詢耗時3.80秒,比未加索引提升了2.63秒
(5)從新查看執行計劃
從圖的查詢計劃可知,type:index,這個跟全表掃描同樣,只是MySQL掃描表時按索引次序進行而不是行。可是看到Extra:Using index,說明MySQL正在使用覆蓋索引,它只掃描索引的數據,而不是按索引次序的每一行。它比按索引次序全表掃描的開銷少不少。
三、分頁查詢email
(1)分頁查詢email
從圖可知,分頁查詢耗時53.99
(2)分頁查詢執行計劃
如圖所示,type:All,說明MySQL進行了全表掃描。下面在password和email列上建立聯合索引。
(3)添加聯合索引
(4)從新分頁查詢
如圖所示,分頁查詢基本不耗時間。
(5)從新執行查詢計劃
從圖可知,Extra:Using index,MySQL使用了覆蓋索引進行查詢。查詢效率獲得極大的提高。
四、覆蓋索引總結
回想一下,若是查詢只須要掃描索引而無須回表,將帶來諸多好處。
(1)索引條目一般遠小於數據行大小,若是隻讀取索引,MySQL就會極大地減小數據訪問量。
(2)索引按照列值順序存儲,對於I/O密集的範圍查詢會比隨機從磁盤中讀取每一行數據的I/O要少不少。
(3)InnoDB的輔助索引(亦稱二級索引)在葉子節點中保存了行的主鍵值,若是二級索引可以覆蓋查詢,則可沒必要對主鍵索引進行二次查詢了。
覆蓋索引就是從索引中直接獲取查詢結果,要使用覆蓋索引須要注意select查詢列中包含在索引列中;where條件包含索引列或者複合索引的前導列;查詢結果的字段長度儘量少。
什麼是讀寫分離?
MySQL Proxy最強大的一項功能是實現「讀寫分離(Read/Write Splitting)」。基本的原理是讓主數據庫處理事務性查詢,而從數據庫處理SELECT查詢。數據庫複製被用來把事務性查詢致使的變動同步到集羣中的從數據庫。 固然,主服務器也能夠提供查詢服務。使用讀寫分離最大的做用無非是環境服務器壓力。能夠看下這張圖:
讀寫分離的好處
一、增長冗餘
二、增長了機器的處理能力
三、對於讀操做爲主的應用,使用讀寫分離是最好的場景,由於能夠確保寫的服務器壓力更小,而讀又能夠接受點時間上的延遲。
讀寫分離提升性能之緣由
一、物理服務器增長,負荷增長
二、主從只負責各自的寫和讀,極大程度的緩解X鎖和S鎖爭用
三、從庫可配置myisam引擎,提高查詢性能以及節約系統開銷
四、從庫同步主庫的數據和主庫直接寫仍是有區別的,經過主庫發送來的binlog恢復數據,可是,最重要區別在於主庫向從庫發送binlog是異步的,從庫恢復數據也是異步的
五、讀寫分離適用與讀遠大於寫的場景,若是隻有一臺服務器,當select不少時,update和delete會被這些select訪問中的數據堵塞,等待select結束,併發性能不高。 對於寫和讀比例相近的應用,應該部署雙主相互複製
六、能夠在從庫啓動是增長一些參數來提升其讀的性能,例如--skip-innodb、--skip-bdb、--low-priority-updates以及--delay-key-write=ALL。固然這些設置也是須要根據具體業務需求來定得,不必定能用上
七、分攤讀取。假如咱們有1主3從,不考慮上述1中提到的從庫單方面設置,假設如今1分鐘內有10條寫入,150條讀取。那麼,1主3從至關於共計40條寫入,而讀取總數沒變,所以平均下來每臺服務器承擔了10條寫入和50條讀取(主庫不承擔讀取操做)。所以,雖然寫入沒變,可是讀取大大分攤了,提升了系統性能。另外,當讀取被分攤後,又間接提升了寫入的性能。因此,整體性能提升了,說白了就是拿機器和帶寬換性能。MySQL官方文檔中有相關演算公式:官方文檔 見6.9FAQ之「MySQL複製可以什麼時候和多大程度提升系統性能」
八、MySQL複製另一大功能是增長冗餘,提升可用性,當一臺數據庫服務器宕機後能經過調整另一臺從庫來以最快的速度恢復服務,所以不能光看性能,也就是說1主1從也是能夠的。
讀寫分離示意圖
一、Redis和Memcache都是將數據存放在內存中,都是內存數據庫。不過memcache還可用於緩存其餘東西,例如圖片、視頻等等;
二、Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲;
三、虛擬內存--Redis當物理內存用完時,能夠將一些好久沒用到的value 交換到磁盤;
四、過時策略--memcache在set時就指定,例如set key1 0 0 8,即永不過時。Redis能夠經過例如expire 設定,例如expire name 10;
五、分佈式--設定memcache集羣,利用magent作一主多從;redis能夠作一主多從。均可以一主一從;
六、存儲數據安全--memcache掛掉後,數據沒了;redis能夠按期保存到磁盤(持久化);
七、災難恢復--memcache掛掉後,數據不可恢復; redis數據丟失後能夠經過aof恢復;
八、Redis支持數據的備份,即master-slave模式的數據備份;
redis下,數據庫是由一個整數索引標識,而不是由一個數據庫名稱。默認狀況下,一個客戶端鏈接到數據庫0。redis配置文件中下面的參數來控制數據庫總數:
/etc/redis/redis.conf
文件中,有個配置項 databases = 16 //默認有16個數據庫
什麼是redis?
redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。
上面的話好像很專業的樣子,這裏咱們簡單的理解爲,其實redis就是一個消息中間件,能夠做爲多個進程的消息中轉站,是比以前咱們用的manage模塊更方便自由的共享內存。
一、基本操做
以前咱們已經知道,redis是以key-value的形式存儲的,因此咱們在操做的時候。首先咱們將redis所在主機的ip和發佈端口做爲參數實例化了一個對象r,而後執行set('name','Eva_J'),這樣咱們就在內存中存儲了一個key爲name,值爲‘Eva_J’的項。咱們能夠理解爲{'name':'Eva_J'},當咱們要讀取的以後,只須要get('name'),就會獲得'Eva_J'的值。
二、鏈接池
redis-py使用connection pool來管理對一個redis server的全部鏈接,避免每次創建、釋放鏈接的開銷。默認,每一個Redis實例都會維護一個本身的鏈接池。能夠直接創建一個鏈接池,而後做爲參數Redis,這樣就能夠實現多個Redis實例共享一個鏈接池。
三、管道
redis-py默認在執行每次請求都會建立(鏈接池申請鏈接)和斷開(歸還鏈接池)一次鏈接操做,若是想要在一次請求中指定多個命令,則能夠使用pipline實現一次請求指定多個命令,而且默認狀況下一次pipline 是原子性操做。
四、發佈訂閱
發佈者:服務器
訂閱者:Dashboad和數據處理
Demo以下:
定義一個redishelper類,創建與redis鏈接,定義頻道爲fm92.4,定義發佈public及訂閱subscribe方法。
訂閱者:導入剛剛咱們寫好的類,實例化對象,調用訂閱方法,就能夠使用while True接收信息了。
發佈者:導入剛剛咱們寫好的類,實例化對象,調用發佈方法,下例發佈了一條消息‘hello’
redis主從複製
和Mysql主從複製的緣由同樣,Redis雖然讀取寫入的速度都特別快,可是也會產生讀壓力特別大的狀況。爲了分擔讀壓力,Redis支持主從複製,Redis的主從結構能夠採用一主多從或者級聯結構,Redis主從複製能夠根據是不是全量分爲全量同步和增量同步。下圖爲級聯結構。
全量同步
Redis全量複製通常發生在Slave初始化階段,這時Slave須要將Master上的全部數據都複製一份。具體步驟以下:
- 從服務器鏈接主服務器,發送SYNC命令;
- 主服務器接收到SYNC命名後,開始執行BGSAVE命令生成RDB文件並使用緩衝區記錄此後執行的全部寫命令;
- 主服務器BGSAVE執行完後,向全部從服務器發送快照文件,並在發送期間繼續記錄被執行的寫命令;
- 從服務器收到快照文件後丟棄全部舊數據,載入收到的快照;
- 主服務器快照發送完畢後開始向從服務器發送緩衝區中的寫命令;
- 從服務器完成對快照的載入,開始接收命令請求,並執行來自主服務器緩衝區的寫命令;
完成上面幾個步驟後就完成了從服務器數據初始化的全部操做,從服務器此時能夠接收來自用戶的讀請求。
增量同步
Redis增量複製是指Slave初始化後開始正常工做時主服務器發生的寫操做同步到從服務器的過程。
增量複製的過程主要是主服務器每執行一個寫命令就會向從服務器發送相同的寫命令,從服務器接收並執行收到的寫命令。
Redis主從同步策略
主從剛剛鏈接的時候,進行全量同步;全同步結束後,進行增量同步。固然,若是有須要,slave 在任什麼時候候均可以發起全量同步。redis 策略是,不管如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步。
關於其同步機制,請點擊這裏
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,當用Redis作Master-slave的高可用方案時,假如master宕機了,Redis自己(包括它的不少客戶端)都沒有實現自動進行主備切換,而Redis-sentinel自己也是一個獨立運行的進程,它能監控多個master-slave集羣,發現master宕機後能進行自動切換。
它的主要功能有如下幾點:
不時地監控redis是否按照預期良好地運行;
若是發現某個redis節點運行出現情況,可以通知另一個進程(例如它的客戶端);
可以進行自動切換。當一個master節點不可用時,可以選舉出master的多個slave(若是有超過一個slave的話)中的一個來做爲新的master,其它的slave節點會將它所追隨的master的地址改成被提高爲master的slave的新地址。
Sentinel支持集羣
很顯然,只使用單個sentinel進程來監控redis集羣是不可靠的,當sentinel進程宕掉後(sentinel自己也有單點問題,single-point-of-failure)整個集羣系統將沒法按照預期的方式運行。因此有必要將sentinel集羣,這樣有幾個好處:
即便有一些sentinel進程宕掉了,依然能夠進行redis集羣的主備切換;
若是隻有一個sentinel進程,若是這個進程運行出錯,或者是網絡堵塞,那麼將沒法實現redis集羣的主備切換(單點問題);
若是有多個sentinel,redis的客戶端能夠隨意地鏈接任意一個sentinel來得到關於redis集羣中的信息。
Sentinel版本
Sentinel當前最新的穩定版本稱爲Sentinel 2(與以前的Sentinel 1區分開來)。隨着redis2.8的安裝包一塊兒發行。安裝完Redis2.8後,能夠在redis2.8/src/裏面找到Redis-sentinel的啓動程序。
強烈建議:
若是你使用的是redis2.6(sentinel版本爲sentinel 1),你最好應該使用redis2.8版本的sentinel 2,由於sentinel 1有不少的Bug,已經被官方棄用,因此強烈建議使用redis2.8以及sentinel 2。
運行Sentinel
運行sentinel有兩種方式:
第一種
redis-sentinel /path/to/sentinel.conf
第二種
redis-server /path/to/sentinel.conf --sentinel
以上兩種方式,都必須指定一個sentinel的配置文件sentinel.conf,若是不指定,將沒法啓動sentinel。sentinel默認監聽26379端口,
因此運行前必須肯定該端口沒有被別的進程佔用。
Sentinel的配置
Redis源碼包中包含了一個sentinel.conf文件做爲sentinel的配置文件,配置文件自帶了關於各個配置項的解釋。典型的配置項以下所示:
sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 sentinel monitor resque 192.168.1.3 6380 4 sentinel down-after-milliseconds resque 10000 sentinel failover-timeout resque 180000 sentinel parallel-syncs resque 5
上面的配置項配置了兩個名字分別爲mymaster和resque的master,配置文件只須要配置master的信息就好啦,不用配置slave的信息,由於slave可以被自動檢測到(master節點會有關於slave的消息)。須要注意的是,配置文件在sentinel運行期間是會被動態修改的,例如當發生主備切換時候,配置文件中的master會被修改成另一個slave。這樣,以後sentinel若是重啓時,就能夠根據這個配置來恢復其以前所監控的redis集羣的狀態。
sentinel monitor mymaster 127.0.0.1 6379 2
這一行表明sentinel監控的master的名字叫作mymaster,地址爲127.0.0.1:6379,行尾最後的一個2表明什麼意思呢?咱們知道,網絡是不可靠的,有時候一個sentinel會由於網絡堵塞而誤覺得一個master redis已經死掉了,當sentinel集羣式,解決這個問題的方法就變得很簡單,只須要多個sentinel互相溝通來確認某個master是否真的死了,這個2表明,當集羣中有2個sentinel認爲master死了時,才能真正認爲該master已經不可用了。(sentinel集羣中各個sentinel也有互相通訊,經過gossip協議)。
除了第一行配置,咱們發現剩下的配置都有一個統一的格式:
sentinel <option_name> <master_name> <option_value>
接下來咱們根據上面格式中的option_name一個一個來解釋這些配置項:
down-after-milliseconds
sentinel會向master發送心跳PING來確認master是否存活,若是master在「必定時間範圍」內不迴應PONG 或者是回覆了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認爲這個master已經不可用了(subjectively down, 也簡稱爲SDOWN)。而這個down-after-milliseconds就是用來指定這個「必定時間範圍」的,單位是毫秒。
不過須要注意的是,這個時候sentinel並不會立刻進行failover主備切換,這個sentinel還須要參考sentinel集羣中其餘sentinel的意見,若是超過某個數量的sentinel也主觀地認爲該master死了,那麼這個master就會被客觀地(注意哦,此次不是主觀,是客觀,與剛纔的subjectively down相對,此次是objectively down,簡稱爲ODOWN)認爲已經死了。須要一塊兒作出決定的sentinel數量在上一條配置中進行配置。
parallel-syncs
在發生failover主備切換時,這個選項指定了最多能夠有多少個slave同時對新的master進行同步,這個數字越小,完成failover所需的時間就越長,可是若是這個數字越大,就意味着越多的slave由於replication而不可用。能夠經過將這個值設爲 1 來保證每次只有一個slave處於不能處理命令請求的狀態。
其餘配置項在sentinel.conf中都有很詳細的解釋。
全部的配置均可以在運行時用命令SENTINEL SET command動態修改。
Sentinel的「仲裁會」
前面咱們談到,當一個master被sentinel集羣監控時,須要爲它指定一個參數,這個參數指定了當須要判決master爲不可用,而且進行failover時,所須要的sentinel數量,本文中咱們暫時稱這個參數爲票數
不過,當failover主備切換真正被觸發後,failover並不會立刻進行,還須要sentinel中的大多數sentinel受權後才能夠進行failover。
當ODOWN時,failover被觸發。failover一旦被觸發,嘗試去進行failover的sentinel會去得到「大多數」sentinel的受權(若是票數比大多數還要大的時候,則詢問更多的sentinel)
這個區別看起來很微妙,可是很容易理解和使用。例如,集羣中有5個sentinel,票數被設置爲2,當2個sentinel認爲一個master已經不可用了之後,將會觸發failover,可是,進行failover的那個sentinel必須先得到至少3個sentinel的受權才能夠實行failover。
若是票數被設置爲5,要達到ODOWN狀態,必須全部5個sentinel都主觀認爲master爲不可用,要進行failover,那麼得得到全部5個sentinel的受權。
爲何要先得到大多數sentinel的承認時才能真正去執行failover呢?
當一個sentinel被受權後,它將會得到宕掉的master的一份最新配置版本號,當failover執行結束之後,這個版本號將會被用於最新的配置。由於大多數sentinel都已經知道該版本號已經被要執行failover的sentinel拿走了,因此其餘的sentinel都不能再去使用這個版本號。這意味着,每次failover都會附帶有一個獨一無二的版本號。咱們將會看到這樣作的重要性。
並且,sentinel集羣都遵照一個規則:若是sentinel A推薦sentinel B去執行failover,B會等待一段時間後,自行再次去對同一個master執行failover,這個等待的時間是經過failover-timeout配置項去配置的。從這個規則能夠看出,sentinel集羣中的sentinel不會再同一時刻併發去failover同一個master,第一個進行failover的sentinel若是失敗了,另一個將會在必定時間內進行從新進行failover,以此類推。
redis sentinel保證了活躍性:若是大多數sentinel可以互相通訊,最終將會有一個被受權去進行failover.
redis sentinel也保證了安全性:每一個試圖去failover同一個master的sentinel都會獲得一個獨一無二的版本號。
一旦一個sentinel成功地對一個master進行了failover,它將會把關於master的最新配置經過廣播形式通知其它sentinel,其它的sentinel則更新對應master的配置。
一個faiover要想被成功實行,sentinel必須可以向選爲master的slave發送SLAVEOF NO ONE命令,而後可以經過INFO命令看到新master的配置信息。
當將一個slave選舉爲master併發送SLAVEOF NO ONE後,即便其它的slave還沒針對新master從新配置本身,failover也被認爲是成功了的,而後全部sentinels將會發布新的配置信息。
新配在集羣中相互傳播的方式,就是爲何咱們須要當一個sentinel進行failover時必須被受權一個版本號的緣由。
每一個sentinel使用##發佈/訂閱##的方式持續地傳播master的配置版本信息,配置傳播的##發佈/訂閱##管道是:__sentinel__:hello。
由於每個配置都有一個版本號,因此以版本號最大的那個爲標準。
舉個栗子:假設有一個名爲mymaster的地址爲192.168.1.50:6379。一開始,集羣中全部的sentinel都知道這個地址,因而爲mymaster的配置打上版本號1。一段時候後mymaster死了,有一個sentinel被受權用版本號2對其進行failover。若是failover成功了,假設地址改成了192.168.1.50:9000,此時配置的版本號爲2,進行failover的sentinel會將新配置廣播給其餘的sentinel,因爲其餘sentinel維護的版本號爲1,發現新配置的版本號爲2時,版本號變大了,說明配置更新了,因而就會採用最新的版本號爲2的配置。
這意味着sentinel集羣保證了第二種活躍性:一個可以互相通訊的sentinel集羣最終會採用版本號最高且相同的配置。
sentinel對於不可用有兩種不一樣的見解,一個叫主觀不可用(SDOWN),另一個叫客觀不可用(ODOWN)。SDOWN是sentinel本身主觀上檢測到的關於master的狀態,ODOWN須要必定數量的sentinel達成一致意見才能認爲一個master客觀上已經宕掉,各個sentinel之間經過命令SENTINEL is_master_down_by_addr來得到其它sentinel對master的檢測結果。
從sentinel的角度來看,若是發送了PING心跳後,在必定時間內沒有收到合法的回覆,就達到了SDOWN的條件。這個時間在配置中經過is-master-down-after-milliseconds參數配置。
當sentinel發送PING後,如下回復之一都被認爲是合法的:
PING replied with +PONG.
PING replied with -LOADING error.
PING replied with -MASTERDOWN error.
其它任何回覆(或者根本沒有回覆)都是不合法的。
從SDOWN切換到ODOWN不須要任何一致性算法,只須要一個gossip協議:若是一個sentinel收到了足夠多的sentinel發來消息告訴它某個master已經down掉了,SDOWN狀態就會變成ODOWN狀態。若是以後master可用了,這個狀態就會相應地被清理掉。
正如以前已經解釋過了,真正進行failover須要一個受權的過程,可是全部的failover都開始於一個ODOWN狀態。
ODOWN狀態只適用於master,對於不是master的redis節點sentinel之間不須要任何協商,slaves和sentinel不會有ODOWN狀態。
雖然sentinel集羣中各個sentinel都互相鏈接彼此來檢查對方的可用性以及互相發送消息。可是你不用在任何一個sentinel配置任何其它的sentinel的節點。由於sentinel利用了master的發佈/訂閱機制去自動發現其它也監控了統一master的sentinel節點。
經過向名爲__sentinel__:hello
的管道中發送消息來實現。
一樣,你也不須要在sentinel中配置某個master的全部slave的地址,sentinel會經過詢問master來獲得這些slave的地址的。
每一個sentinel經過向每一個master和slave的發佈/訂閱頻道__sentinel__:hello
每秒發送一次消息,來宣佈它的存在。
每一個sentinel也訂閱了每一個master和slave的頻道__sentinel__:hello
的內容,來發現未知的sentinel,當檢測到了新的sentinel,則將其加入到自身維護的master監控列表中。
每一個sentinel發送的消息中也包含了其當前維護的最新的master配置。若是某個sentinel發現
本身的配置版本低於接收到的配置版本,則會用新的配置更新本身的master配置。
在爲一個master添加一個新的sentinel前,sentinel老是檢查是否已經有sentinel與新的sentinel的進程號或者是地址是同樣的。若是是那樣,這個sentinel將會被刪除,而把新的sentinel添加上去。
網絡隔離時的一致性
redis sentinel集羣的配置的一致性模型爲最終一致性,集羣中每一個sentinel最終都會採用最高版本的配置。然而,在實際的應用環境中,有三個不一樣的角色會與sentinel打交道:
Redis實例.
Sentinel實例.
客戶端.
爲了考察整個系統的行爲咱們必須同時考慮到這三個角色。
下面有個簡單的例子,有三個主機,每一個主機分別運行一個redis和一個sentinel:
+-------------+
| Sentinel 1 | <--- Client A | Redis 1 (M) | +-------------+ | | +-------------+ | +------------+ | Sentinel 2 |-----+-- / partition / ----| Sentinel 3 | <--- Client B | Redis 2 (S) | | Redis 3 (M)| +-------------+ +------------+
在這個系統中,初始狀態下redis3是master, redis1和redis2是slave。以後redis3所在的主機網絡不可用了,sentinel1和sentinel2啓動了failover並把redis1選舉爲master。
Sentinel集羣的特性保證了sentinel1和sentinel2獲得了關於master的最新配置。可是sentinel3依然持着的是就的配置,由於它與外界隔離了。
當網絡恢復之後,咱們知道sentinel3將會更新它的配置。可是,若是客戶端所鏈接的master被網絡隔離,會發生什麼呢?
客戶端將依然能夠向redis3寫數據,可是當網絡恢復後,redis3就會變成redis的一個slave,那麼,在網絡隔離期間,客戶端向redis3寫的數據將會丟失。
也許你不會但願這個場景發生:
若是你把redis當作緩存來使用,那麼你也許能容忍這部分數據的丟失。
但若是你把redis當作一個存儲系統來使用,你也許就沒法容忍這部分數據的丟失了。
由於redis採用的是異步複製,在這樣的場景下,沒有辦法避免數據的丟失。然而,你能夠經過如下配置來配置redis3和redis1,使得數據不會丟失。
min-slaves-to-write 1 min-slaves-max-lag 10
經過上面的配置,當一個redis是master時,若是它不能向至少一個slave寫數據(上面的min-slaves-to-write指定了slave的數量),它將會拒絕接受客戶端的寫請求。因爲複製是異步的,master沒法向slave寫數據意味着slave要麼斷開鏈接了,要麼不在指定時間內向master發送同步數據的請求了(上面的min-slaves-max-lag指定了這個時間)。
snetinel的狀態會被持久化地寫入sentinel的配置文件中。每次當收到一個新的配置時,或者新建立一個配置時,配置會被持久化到硬盤中,並帶上配置的版本戳。這意味着,能夠安全的中止和重啓sentinel進程。
即便當前沒有failover正在進行,sentinel依然會使用當前配置去設置監控的master。特別是:
根據最新配置確認爲slaves的節點卻聲稱本身是master(上文例子中被網絡隔離後的的redis3),這時它們會被從新配置爲當前master的slave。
若是slaves鏈接了一個錯誤的master,將會被改正過來,鏈接到正確的master。
當一個sentinel準備好了要進行failover,而且收到了其餘sentinel的受權,那麼就須要選舉出一個合適的slave來作爲新的master。
slave的選舉主要會評估slave的如下幾個方面:
與master斷開鏈接的次數
Slave的優先級
數據複製的下標(用來評估slave當前擁有多少master的數據)
進程ID
若是一個slave與master失去聯繫超過10次,而且每次都超過了配置的最大失聯時間(down-after-milliseconds
),若是sentinel在進行failover時發現slave失聯,那麼這個slave就會被sentinel認爲不適合用來作新master的。
更嚴格的定義是,若是一個slave持續斷開鏈接的時間超過
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
就會被認爲失去選舉資格。
符合上述條件的slave纔會被列入master候選人列表,並根據如下順序來進行排序:
sentinel首先會根據slaves的優先級來進行排序,優先級越小排名越靠前。
若是優先級相同,則查看複製的下標,哪一個從master接收的複製數據多,哪一個就靠前。
若是優先級和下標都相同,就選擇進程ID較小的那個。
一個redis不管是master仍是slave,都必須在配置中指定一個slave優先級。要注意到master也是有可能經過failover變成slave的。
若是一個redis的slave優先級配置爲0,那麼它將永遠不會被選爲master。可是它依然會從master哪裏複製數據。
當一個master配置爲須要密碼才能鏈接時,客戶端和slave在鏈接時都須要提供密碼。
master經過requirepass
設置自身的密碼,不提供密碼沒法鏈接到這個master。
slave經過masterauth
來設置訪問master時的密碼。
可是當使用了sentinel時,因爲一個master可能會變成一個slave,一個slave也可能會變成master,因此須要同時設置上述兩個配置項。
詳細信息,請點擊這裏
因爲Redis出衆的性能,其在衆多的移動互聯網企業中獲得普遍的應用。Redis在3.0版本前只支持單實例模式,雖然如今的服務器內存能夠到100GB、200GB的規模,可是單實例模式限制了Redis無法知足業務的需求(例如新浪微博就曾經用Redis存儲了超過1TB的數據)。Redis的開發者Antirez早在博客上就提出在Redis 3.0版本中加入集羣的功能,但3.0版本等到2015年才發佈正式版。各大企業在3.0版本還沒發佈前爲了解決Redis的存儲瓶頸,紛紛推出了各自的Redis集羣方案。這些方案的核心思想是把數據分片(sharding)存儲在多個Redis實例中,每一片就是一個Redis實例。
下面介紹Redis的集羣方案。
一、客戶端分片
客戶端分片是把分片的邏輯放在Redis客戶端實現,經過Redis客戶端預先定義好的路由規則,把對Key的訪問轉發到不一樣的Redis實例中,最後把返回結果聚集。這種方案的模式以下圖所示。
客戶端分片的好處是全部的邏輯都是可控的,不依賴於第三方分佈式中間件。開發人員清楚怎麼實現分片、路由的規則,不用擔憂踩坑。
客戶端分片方案有下面這些缺點:
●這是一種靜態的分片方案,須要增長或者減小Redis實例的數量,須要手工調整分片的程序。
●可運維性差,集羣的數據出了任何問題都須要運維人員和開發人員一塊兒合做,減緩了解決問題的速度,增長了跨部門溝通的成本。
●在不一樣的客戶端程序中,維護相同的分片邏輯成本巨大。例如,系統中有兩套業務系統共用一套Redis集羣,一套業務系統用Java實現,另外一套業務系統用PHP實現。爲了保證分片邏輯的一致性,在Java客戶端中實現的分片邏輯也須要在PHP客戶端實現一次。相同的邏輯在不一樣的系統中分別實現,這種設計原本就很是糟糕,並且須要耗費巨大的開發成本保證兩套業務系統分片邏輯的一致性。
二、Twemproxy
Twemproxy是由Twitter開源的Redis代理,其基本原理是:Redis客戶端把請求發送到Twemproxy,Twemproxy根據路由規則發送到正確的Redis實例,最後Twemproxy把結果聚集返回給客戶端。
Twemproxy經過引入一個代理層,將多個Redis實例進行統一管理,使Redis客戶端只須要在Twemproxy上進行操做,而不須要關心後面有多少個Redis實例,從而實現了Redis集羣。
Twemproxy集羣架構以下圖所示:
Twemproxy的優勢以下:
●客戶端像鏈接Redis實例同樣鏈接Twemproxy,不須要改任何的代碼邏輯。
●支持無效Redis實例的自動刪除。
●Twemproxy與Redis實例保持鏈接,減小了客戶端與Redis實例的鏈接數。
Twemproxy的缺點以下:
●因爲Redis客戶端的每一個請求都通過Twemproxy代理才能到達Redis服務器,這個過程當中會產生性能損失。
●沒有友好的監控管理後臺界面,不利於運維監控。
●最大的問題是Twemproxy沒法平滑地增長Redis實例。對於運維人員來講,當由於業務須要增長Redis實例時工做量很是大。
Twemproxy做爲最被普遍使用、最久經考驗、穩定性最高的Redis代理,在業界被普遍使用。
三、Redis 3.0集羣
Redis 3.0集羣採用了P2P的模式,徹底去中心化。Redis把全部的Key分紅了16384個slot,每一個Redis實例負責其中一部分slot。集羣中的全部信息(節點、端口、slot等),都經過節點之間按期的數據交換而更新。
Redis客戶端在任意一個Redis實例發出請求,若是所需數據不在該實例中,經過重定向命令引導客戶端訪問所需的實例。
Redis 3.0集羣的工做流程以下圖所示:
如圖所示Redis集羣內的機器按期交換數據,工做流程以下:
(1) Redis客戶端在Redis2實例上訪問某個數據。
(2) 在Redis2內發現這個數據是在Redis3這個實例中,給Redis客戶端發送一個重定向的命令。
(3) Redis客戶端收到重定向命令後,訪問Redis3實例獲取所需的數據。
Redis 3.0的集羣方案有如下兩個問題:
●一個Redis實例具有了「數據存儲」和「路由重定向」,徹底去中心化的設計。這帶來的好處是部署很是簡單,直接部署Redis就行,不像Codis有那麼多的組件和依賴。但帶來的問題是很難對業務進行無痛的升級,若是哪天Redis集羣出了什麼嚴重的Bug,就只能回滾整個Redis集羣。
●對協議進行了較大的修改,對應的Redis客戶端也須要升級。升級Redis客戶端後誰能確保沒有Bug?並且對於線上已經大規模運行的業務,升級代碼中的Redis客戶端也是一個很麻煩的事情。
綜合上面所述的兩個問題,Redis 3.0集羣在業界並無被大規模使用。
四、雲服務器上的集羣服務
國內的雲服務器提供商阿里雲、UCloud等均推出了基於Redis的雲存儲服務。
這個服務的特性以下。
(1)動態擴容
用戶能夠經過控制面板升級所需的Redis存儲空間,擴容的過程當中服務部不須要中斷或中止,整個擴容過程對用戶透明、無感知,這點是很是實用的,在前面介紹的方案中,解決Redis平滑擴容是個很煩瑣的任務,如今按幾下鼠標就能搞定,大大減小了運維的負擔。
(2)數據多備
數據保存在一主一備兩臺機器中,其中一臺機器宕機了,數據還在另一臺機器上有備份。
(3)自動容災
主機宕機後系統能自動檢測並切換到備機上,實現服務的高可用。
(4)實惠
不少狀況下爲了使Redis的性能更高,須要購買一臺專門的服務器用於Redis的存儲服務,但這樣子CPU、內存等資源就浪費了,購買Redis雲存儲服務就很好地解決了這個問題。
有了Redis雲存儲服務,能使App後臺開發人員從煩瑣運維中解放出來。App後臺要搭建一個高可用、高性能的Redis服務,須要投入至關的運維成本和精力。若是使用雲存儲服務,就不必投入這些成本和精力,可讓App後臺開發人員更專一於業務。
Redis 集羣中內置了 16384 個哈希槽,當須要在 Redis 集羣中放置一個 key-value時,redis 先對 key 使用 crc16 算法算出一個結果,而後把結果對 16384 求餘數,這樣每一個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大體均等的將哈希槽映射到不一樣的節點。
Redis 集羣沒有使用一致性hash, 而是引入了哈希槽的概念。
Redis 集羣有16384個哈希槽,每一個key經過CRC16校驗後對16384取模來決定放置哪一個槽.集羣的每一個節點負責一部分hash槽。這種結構很容易添加或者刪除節點,而且不管是添加刪除或者修改某一個節點,都不會形成集羣不可用的狀態。
使用哈希槽的好處就在於能夠方便的添加或移除節點。
當須要增長節點時,只須要把其餘節點的某些哈希槽挪到新節點就能夠了;
當須要移除節點時,只須要把移除節點上的哈希槽挪到其餘節點就好了;
在這一點上,咱們之後新增或移除節點的時候不用先停掉全部的 redis 服務。
"用了哈希槽的概念,而沒有用一致性哈希算法,不都是哈希麼?這樣作的緣由是爲何呢?"
Redis Cluster是本身作的crc16的簡單hash算法,沒有用一致性hash。Redis的做者認爲它的crc16(key) mod 16384的效果已經不錯了,雖然沒有一致性hash靈活,但實現很簡單,節點增刪時處理起來也很方便。
"爲了動態增刪節點的時候,不至於丟失數據麼?"
節點增刪時不丟失數據和hash算法沒什麼關係,不丟失數據要求的是一份數據有多個副本。
「還有集羣總共有2的14次方,16384個哈希槽,那麼每個哈希槽中存的key 和 value是什麼?」
當你往Redis Cluster中加入一個Key時,會根據crc16(key) mod 16384計算這個key應該分佈到哪一個hash slot中,一個hash slot中會有不少key和value。你能夠理解成表的分區,使用單節點時的redis時只有一個表,全部的key都放在這個表裏;改用Redis Cluster之後會自動爲你生成16384個分區表,你insert數據時會根據上面的簡單算法來決定你的key應該存在哪一個分區,每一個分區裏有不少key。
Redis 提供了多種不一樣級別的持久化方式:
RDB 持久化能夠在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot)。
AOF 持久化記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集。 AOF 文件中的命令所有以 Redis 協議的格式來保存,新命令會被追加到文件的末尾。 Redis 還能夠在後臺對 AOF 文件進行重寫(rewrite),使得 AOF 文件的體積不會超出保存數據集狀態所需的實際大小。
Redis 還能夠同時使用 AOF 持久化和 RDB 持久化。 在這種狀況下, 當 Redis 重啓時, 它會優先使用 AOF 文件來還原數據集, 由於 AOF 文件保存的數據集一般比 RDB 文件所保存的數據集更完整。
你甚至能夠關閉持久化功能,讓數據只在服務器運行時存在。
RDB知識點
RDB 的優勢
RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 好比說,你能夠在最近的 24 小時內,每小時備份一次 RDB 文件,而且在每月的每一天,也備份一個 RDB 文件。 這樣的話,即便趕上問題,也能夠隨時將數據集還原到不一樣的版本。
RDB 很是適用於災難恢復(disaster recovery):它只有一個文件,而且內容都很是緊湊,能夠(在加密後)將它傳送到別的數據中心,或者亞馬遜 S3 中。
RDB 能夠最大化 Redis 的性能:父進程在保存 RDB 文件時惟一要作的就是 fork 出一個子進程,而後這個子進程就會處理接下來的全部保存工做,父進程無須執行任何磁盤 I/O 操做。
RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
RDB 的缺點
若是你須要儘可能避免在服務器故障時丟失數據,那麼 RDB 不適合你。 雖然 Redis 容許你設置不一樣的保存點(save point)來控制保存 RDB 文件的頻率, 可是, 由於RDB 文件須要保存整個數據集的狀態, 因此它並非一個輕鬆的操做。 所以你可能會至少 5 分鐘才保存一次 RDB 文件。 在這種狀況下, 一旦發生故障停機, 你就可能會丟失好幾分鐘的數據。
每次保存 RDB 的時候,Redis 都要 fork() 出一個子進程,並由子進程來進行實際的持久化工做。 在數據集比較龐大時, fork() 可能會很是耗時,形成服務器在某某毫秒內中止處理客戶端; 若是數據集很是巨大,而且 CPU 時間很是緊張的話,那麼這種中止時間甚至可能會長達整整一秒。 雖然 AOF 重寫也須要進行 fork() ,但不管 AOF 重寫的執行間隔有多長,數據的耐久性都不會有任何損失。
AOF知識點
AOF 的優勢
使用 AOF 持久化會讓 Redis 變得很是耐久(much more durable):你能夠設置不一樣的 fsync 策略,好比無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略爲每秒鐘 fsync 一次,在這種配置下,Redis 仍然能夠保持良好的性能,而且就算髮生故障停機,也最多隻會丟失一秒鐘的數據( fsync 會在後臺線程執行,因此主線程能夠繼續努力地處理命令請求)。
AOF 文件是一個只進行追加操做的日誌文件(append only log), 所以對 AOF 文件的寫入不須要進行 seek , 即便日誌由於某些緣由而包含了未寫入完整的命令(好比寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也能夠輕易地修復這種問題。
Redis 能夠在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操做是絕對安全的,由於 Redis 在建立新 AOF 文件的過程當中,會繼續將命令追加到現有的 AOF 文件裏面,即便重寫過程當中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件建立完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操做。
AOF 文件有序地保存了對數據庫執行的全部寫入操做, 這些寫入操做以 Redis 協議的格式保存, 所以 AOF 文件的內容很是容易被人讀懂, 對文件進行分析(parse)也很輕鬆。 導出(export) AOF 文件也很是簡單: 舉個例子, 若是你不當心執行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那麼只要中止服務器, 移除 AOF 文件末尾的 FLUSHALL 命令, 並重啓 Redis , 就能夠將數據集恢復到 FLUSHALL 執行以前的狀態。
AOF 的缺點
對於相同的數據集來講,AOF 文件的體積一般要大於 RDB 文件的體積。
根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在通常狀況下, 每秒 fsync 的性能依然很是高, 而關閉 fsync 可讓 AOF 的速度和 RDB 同樣快, 即便在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 能夠提供更有保證的最大延遲時間(latency)。
AOF 在過去曾經發生過這樣的 bug : 由於個別命令的緣由,致使 AOF 文件在從新載入時,沒法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引發過這樣的 bug 。) 測試套件裏爲這種狀況添加了測試: 它們會自動生成隨機的、複雜的數據集, 並經過從新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 可是對比來講, RDB 幾乎是不可能出現這種 bug 的。