第一 Redis 簡介
1.1 什麼是Redis
Redis 是完全開源免費的,遵守BSD協議,是一個高性能的key-value數據庫。
Redis 與其他 key - value 緩存產品有以下三個特點:
Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啓的時候可以再次加載進行使用。
Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
Redis支持數據的備份,即master-slave模式的數據備份。
1.2 Redis 應用場景
主要能夠體現 解決數據庫的訪問壓力。
例如:短信驗證碼時間有效期、session共享解決方案
1.3 Redis 優勢
性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並後的原子性執行。
豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
第二 Redis 基本數據類型之字符串(String)
Redis 字符串命令
命令 |
描述說明 |
此命令設置指定鍵的值。 |
|
獲取指定鍵的值。 |
|
獲取存儲在鍵上的字符串的子字符串。 |
|
設置鍵的字符串值並返回其舊值。 |
|
返回在鍵處存儲的字符串值中偏移處的位值。 |
|
獲取所有給定鍵的值 |
|
存儲在鍵上的字符串值中設置或清除偏移處的位 |
|
使用鍵和到期時間來設置值 |
|
設置鍵的值,僅當鍵不存在時 |
|
在指定偏移處開始的鍵處覆蓋字符串的一部分 |
|
獲取存儲在鍵中的值的長度 |
|
爲多個鍵分別設置它們的值 |
|
爲多個鍵分別設置它們的值,僅當鍵不存在時 |
|
設置鍵的值和到期時間(以毫秒爲單位) |
|
將鍵的整數值增加1 |
|
將鍵的整數值按給定的數值增加 |
|
將鍵的浮點值按給定的數值增加 |
|
將鍵的整數值減1 |
|
按給定數值減少鍵的整數值 |
|
將指定值附加到鍵 |
第三 Redis 基本數據類型之列表(List)
Redis列表:是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。
一個列表最多可以包含 232 - 1 個元素 (4294967295, 每個列表超過40億個元素)。
Redis列表命令
命令及描述 |
BLPOP key1 [key2 ] timeout |
BRPOP key1 [key2 ] timeout |
BRPOPLPUSH source destination timeout |
LINDEX key index |
LINSERT key BEFORE|AFTER pivot value |
LLEN key |
LPOP key |
LPUSH key value1 [value2] |
LPUSHX key value |
LRANGE key start stop |
LREM key count value |
LSET key index value |
LTRIM key start stop |
RPOP key |
RPOPLPUSH source destination |
RPUSH key value1 [value2] |
RPUSHX key value |
第四 Redis 基本數據類型之集合(Set)
Redis集合:是string類型的無序集合。集合成員是唯一的,這就意味着集合中不能出現重複的數據。
Redis 中 集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。
集合中最大的成員數爲 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
Redis集合命令
命令及描述 |
SADD key member1 [member2] |
SCARD key |
SDIFF key1 [key2] |
SDIFFSTORE destination key1 [key2] |
SINTER key1 [key2] |
SINTERSTORE destination key1 [key2] |
SISMEMBER key member |
SMEMBERS key |
SMOVE source destination member |
SPOP key |
SRANDMEMBER key [count] |
SREM key member1 [member2] |
SUNION key1 [key2] |
SUNIONSTORE destination key1 [key2] |
第五 Redis 基本數據類型之有序集合(Sorted Set)
Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重複的成員。
不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重複。
集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。 集合中最大的成員數爲 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
Redis有序集合命令
第六 Redis 哈希(Hash)
Redis hash 是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
Redis 中每個 hash 可以存儲 232 - 1 鍵值對(40多億)。
Redis 哈希命令
命令及描述 |
HDEL key field2 [field2] |
HEXISTS key field |
HGET key field |
HGETALL key |
HINCRBY key field increment |
HINCRBYFLOAT key field increment |
HKEYS key |
HLEN key |
HMGET key field1 [field2] |
HMSET key field1 value1 [field2 value2 ] |
HSET key field value |
HSETNX key field value |
HVALS key |
HSCAN key cursor [MATCH pattern] [COUNT count] |
第七 Windows 系統安裝Redis
7.1 CMD窗口啓動Redis服務
切換至Redis解壓文件的bin目錄,執行如下代碼:redis-server redis.windows.conf
7.2 將Redis作爲服務安裝在本機電腦中:
切換至Redis解壓文件的bin目錄,執行如下代碼:redis-server --service-install redis.windows.conf
安裝完之後,就可看到Redis已經作爲windows服務了:
但是安裝好之後,Redis並沒有啓動,啓動命令如下:redis-server --service-start
Redis 服務停止,執行如下命令: redis-server --service-stop
Redif 服務協助,執行如下命令:redis-server --service-uninstall
7.3 redis.bat 腳本指令啓動Redis 服務
在redis的目錄下新建一個redis.bat腳本指令,腳本文件內容如下:
d:
cd redis
redis-server.exe redis.windows.conf
第8 Java項目之Redis 集成
8.1 Java Maven 項目集成Redis ,pom.xml 文件添加如下依賴:
<!-- redis jar包 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
8.2 Redis 之字符串
package com.zzg.redis.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisFactory {
public static JedisPool jedisPool; // 池化管理jedis鏈接池
static {
JedisPoolConfig config = new JedisPoolConfig();
// 設置最大連接數
config.setMaxTotal(300);
// 設置最大空閒數
config.setMaxIdle(600);
// //設置超時時間
config.setMaxWaitMillis(10000);
// 初始化連接池
jedisPool = new JedisPool(config, "127.0.0.1", 6379, 10 * 1000);
}
/**
* 獲取 redis鏈接
*
* @return 2017年9月13日
*/
public static Jedis getResource() {
return jedisPool.getResource();
}
public static void close() {
jedisPool.close();
}
}
package com.zzg.redis.string;
/**
* Redis 字符串類型
* @author Administrator
*
* @param <K>
* @param <V>
*/
public interface RedisString<K, V> {
/**
* 寫入緩存 可以是對象
* @param key
* @param value
*/
void set(final K key, V value);
/**
* 讀取String緩存 可以是對象
* @param key
* @return
*/
V get(final K key);
/**
* 獲取存儲在鍵上的字符串的子字符串
* @param key
* @param start
* @param end
* @return
*/
V getrange(final K key, int start, int end);
/**
* 設置鍵的字符串值並返回其舊值。
* @param key
* @param value
* @return
*/
V getset(final K key, V value);
/**
* 移除指定key
* @param key
*/
void del(final K key);
/**
* 同時設置多個key-value
* @param keyvalues
*/
void mset(String ...keyvalues);
/**
* 屬性值增加
* @param key
*/
void incr(final K key);
/**
* 屬性值追加
* @param value
*/
void append(final K key, V value);
}
package com.zzg.redis.string;
import com.zzg.redis.util.RedisFactory;
public class RedisStringHandler implements RedisString<String, String> {
private RedisFactory factory;
public RedisFactory getFactory() {
return factory;
}
public void setFactory(RedisFactory factory) {
this.factory = factory;
}
public RedisStringHandler(RedisFactory factory) {
super();
this.factory = factory;
}
@Override
public void set(String key, String value) {
// TODO Auto-generated method stub
factory.getResource().set(key, value);
}
@Override
public String get(String key) {
// TODO Auto-generated method stub
return factory.getResource().get(key);
}
@Override
public String getrange(String key, int start, int end) {
// TODO Auto-generated method stub
return factory.getResource().getrange(key, start, start);
}
@Override
public String getset(String key, String value) {
// TODO Auto-generated method stub
return factory.getResource().getSet(key, value);
}
@Override
public void del(String key) {
// TODO Auto-generated method stub
factory.getResource().del(key);
}
@Override
public void mset(String... keyvalues) {
// TODO Auto-generated method stub
factory.getResource().mset(keyvalues);
}
@Override
public void incr(String key) {
// TODO Auto-generated method stub
factory.getResource().incr(key);
}
@Override
public void append(final String key, String value) {
// TODO Auto-generated method stub
factory.getResource().append(key, value);
}
}
測試代碼:
package com.zzg.redis;
import com.zzg.redis.string.RedisStringHandler;
import com.zzg.redis.util.RedisFactory;
public class StringTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
RedisStringHandler handler = new RedisStringHandler(new RedisFactory());
// redis 設置
handler.set("key", "zzg");
// redis 獲取
String value = handler.get("key");
System.out.println("value is:" + value);
// redis 追加
handler.append("key", "is java developer");
System.out.println("value is:" + handler.get("key"));
// redis 移除
handler.del("key");
System.out.println("value is:" + handler.get("key"));
// redis 設置多個key-value
handler.mset("name", "zzg", "age", "28","qq","605739");
handler.incr("age"); //進行加1操作
System.out.println(handler.get("name") + "-" + handler.get("age") + "-" + handler.get("qq"));
handler.getFactory().close();
}
}
8.3 Redis 之Map
package com.zzg.redis.string;
import java.util.List;
import java.util.Set;
public interface RedisMap<K, V> {
/**
* 寫入緩存
* @param key
* @param value
*/
void hmset(final K key, V value);
/**
* 獲取緩存
* @param keys
* @return
*/
List<String> hmget(String key, String ...keys);
/**
* 移除Map 指定屬性值
* @param key
* @param properties
*/
void hdel(final K key, String properties);
/**
* 獲取指定Map中的指定屬性
* @param key
* @param properties
* @return
*/
Object hmget(final K key, String properties);
/**
* 返回指定Map中的個數
* @param key
* @return
*/
Long hlen(final K key);
/**
* 判斷指定key Map是否存在
* @param key
* @return
*/
boolean exists(final K key);
/**
* 返回指定Map 所包含的key 值
* @param key
*/
Set<String> hkeys(final K key);
}
package com.zzg.redis.string;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.zzg.redis.util.RedisFactory;
public class RedisMapHandler implements RedisMap<String, Map> {
private RedisFactory factory;
public RedisFactory getFactory() {
return factory;
}
public void setFactory(RedisFactory factory) {
this.factory = factory;
}
public RedisMapHandler(RedisFactory factory) {
super();
this.factory = factory;
}
@Override
public void hmset(String key, Map value) {
// TODO Auto-generated method stub
factory.getResource().hmset(key, value);
}
@Override
public List<String> hmget(String key, String... fields) {
// TODO Auto-generated method stub
return factory.getResource().hmget(key, fields);
}
@Override
public void hdel(String key, String properties) {
// TODO Auto-generated method stub
factory.getResource().hdel(key, properties);
}
@Override
public Object hmget(String key, String properties) {
// TODO Auto-generated method stub
return factory.getResource().hmget(key, properties);
}
@Override
public Long hlen(String key) {
// TODO Auto-generated method stub
return factory.getResource().hlen(key);
}
@Override
public boolean exists(String key) {
// TODO Auto-generated method stub
return factory.getResource().exists(key);
}
@Override
public Set<String> hkeys(String key) {
// TODO Auto-generated method stub
return factory.getResource().hkeys(key);
}
}
測試代碼:
package com.zzg.redis;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.zzg.redis.string.RedisMapHandler;
import com.zzg.redis.util.RedisFactory;
public class MapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
RedisMapHandler handler = new RedisMapHandler(new RedisFactory());
Map<String, String> map = new HashMap<String, String>();
map.put("name", "zzg");
map.put("age", "28");
map.put("qq", "60573");
// redis 設置
handler.hmset("user", map);
// redis 獲取
List<String> rsmap = handler.hmget("user", "name", "age", "qq");
System.out.println(rsmap);
// redis 刪除
handler.hdel("user","age");
System.out.println(handler.hmget("user", "age")); //因爲刪除了,所以返回的是null
System.out.println(handler.hlen("user")); //返回key爲user的鍵中存放的值的個數2
System.out.println(handler.exists("user"));//是否存在key爲user的記錄 返回true
System.out.println(handler.hkeys("user"));//返回map對象中的所有key
Iterator<String> iter=handler.hkeys("user").iterator();
while (iter.hasNext()){
String key = iter.next();
System.out.println(key+":"+handler.hmget("user",key));
}
}
}
8.5 Redis 之List
package com.zzg.redis.string;
import java.util.List;
public interface RedisList<K, V> {
/**
* Redis 移除指定key
* @param key
*/
void del(final K key);
/**
* Redis List 寫入緩存
* @param key
* @param values
*/
void lpush(final K key, String ...values);
/**
* Redis List 查詢
* @param key
* @param start
* @param stop
* @return
*/
List<String> lrange(final K key, long start, long stop);
/**
* Redis List 寫入緩存
* @param key
* @param values
*/
void rpush(final K key, String ...values);
}
package com.zzg.redis.string;
import java.util.List;
import com.zzg.redis.util.RedisFactory;
public class RedisListHandler implements RedisList<String, List> {
private RedisFactory factory;
public RedisFactory getFactory() {
return factory;
}
public void setFactory(RedisFactory factory) {
this.factory = factory;
}
public RedisListHandler(RedisFactory factory) {
super();
this.factory = factory;
}
@Override
public void del(String key) {
// TODO Auto-generated method stub
factory.getResource().del(key);
}
@Override
public void lpush(String key, String... values) {
// TODO Auto-generated method stub
factory.getResource().lpush(key, values);
}
@Override
public List<String> lrange(String key, long start, long stop) {
// TODO Auto-generated method stub
return factory.getResource().lrange(key, start, stop);
}
@Override
public void rpush(String key, String... values) {
// TODO Auto-generated method stub
factory.getResource().rpush(key, values);
}
}
測試功能代碼:
package com.zzg.redis;
import com.zzg.redis.string.RedisListHandler;
import com.zzg.redis.util.RedisFactory;
public class ListTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
RedisListHandler handler = new RedisListHandler(new RedisFactory());
handler.del("java framework");
System.out.println(handler.lrange("java framework",0,-1));
//先向key java framework中存放三條數據
handler.lpush("java framework","spring");
handler.lpush("java framework","struts");
handler.lpush("java framework","hibernate");
//再取出所有數據jedis.lrange是按範圍取出,
// 第一個是key,第二個是起始位置,第三個是結束位置,jedis.llen獲取長度 -1表示取得所有
System.out.println(handler.lrange("java framework",0,-1));
handler.del("java framework");
handler.rpush("java framework","spring");
handler.rpush("java framework","struts");
handler.rpush("java framework","hibernate");
System.out.println(handler.lrange("java framework",0,-1));
handler.getFactory().close();
}
}
8.6 Redis 之Set
package com.zzg.redis.string;
import java.util.List;
import java.util.Set;
public interface RedisSet<K, V> {
/**
* redis 緩存對象
* @param key
* @param value
*/
void sadd(final K key, V value);
/**
* redis 移除緩存對照
* @param key
* @param value
*/
void srem(final K key, V value);
/**
* 獲取指定key
* @param key
* @return
*/
Set<V> smembers(final K key);
/**
* redis 指定key 是否包含指定的value
* @param key
* @param value
* @return
*/
boolean sismember(final K key, V value);
/**
* redis 返回指定key 的數量
* @param key
* @return
*/
long scard(final K key);
}
package com.zzg.redis.string;
import java.util.Set;
import com.zzg.redis.util.RedisFactory;
public class RedisSetHandler implements RedisSet<String, String> {
private RedisFactory factory;
public RedisFactory getFactory() {
return factory;
}
public void setFactory(RedisFactory factory) {
this.factory = factory;
}
public RedisSetHandler(RedisFactory factory) {
super();
this.factory = factory;
}
@Override
public void sadd(String key, String value) {
// TODO Auto-generated method stub
this.factory.getResource().sadd(key, value);
}
@Override
public void srem(String key, String value) {
// TODO Auto-generated method stub
this.factory.getResource().srem(key, value);
}
@Override
public Set<String> smembers(String key) {
// TODO Auto-generated method stub
return this.factory.getResource().smembers(key);
}
@Override
public boolean sismember(String key, String value) {
// TODO Auto-generated method stub
return this.factory.getResource().sismember(key, value);
}
@Override
public long scard(String key) {
// TODO Auto-generated method stub
return this.factory.getResource().scard(key);
}
}
測試功能代碼:
package com.zzg.redis;
import com.zzg.redis.string.RedisSetHandler;
import com.zzg.redis.util.RedisFactory;
public class SetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
RedisSetHandler handler = new RedisSetHandler(new RedisFactory());
handler.sadd("users","liuling");
handler.sadd("users","xinxin");
handler.sadd("users","ling");
handler.sadd("users","zhangxinxin");
handler.sadd("users","who");
//移除noname
handler.srem("users","who");
System.out.println(handler.smembers("users"));//獲取所有加入的value
System.out.println(handler.sismember("users", "who"));//判斷 who 是否是user集合的元素
System.out.println(handler.scard("users"));//返回集合的元素個數
handler.getFactory().close();
}
}
第9 SpringBoot項目之Redis 集成
參考文章地址:SpringBoot 集成Redis
第10 Windows 之Redis 主從複製
10.1 Redis 主從複製 概述
1、redis的複製功能是支持多個數據庫之間的數據同步。一類是主數據庫(master)一類是從數據庫(slave),主數據庫可以進行讀寫操作,當發生寫操作的時候自動將數據同步到從數據庫,而從數據庫一般是隻讀的,並接收主數據庫同步過來的數據,一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。
2、通過redis的複製功能可以很好的實現數據庫的讀寫分離,提高服務器的負載能力。主數據庫主要進行寫操作,而從數據庫負責讀操作。
10.2 Redis 主從複製 圖解
過程:
1:當一個從數據庫啓動時,會向主數據庫發送sync命令,
2:主數據庫接收到sync命令後會開始在後臺保存快照(執行rdb操作),並將保存期間接收到的命令緩存起來
3:當快照完成後,redis會將快照文件和所有緩存的命令發送給從數據庫。
4:從數據庫收到後,會載入快照文件並執行收到的緩存的命令。
10.3 Windows 系統配置Redis 主從配置
目標:windows 系統實現Redis 主從配置,主端口:7001 ,從端口:7002
主從配置一般流程:
1、在redis根目錄建二個文件夾,分別命名爲7001、7002。這二個名稱分爲表示redis啓動後的端口號。
2、再建立二個bat,分別命名爲7001、7002。內容如下,爲了方便啓動二個redis服務使用。
3、將redis根目錄下的redis.windows.conf文件分別拷貝至7001、7002文件夾。
4、打開7001文件夾下的redis.windows.conf文件。
修改項目內容:port 。值7001。
5、打開7002文件夾下的redis.windows.conf文件。
修改項目內容:port、slaveof。
7002的文件修改端口爲7002,
slaveof 127.0.0.1 7001
這樣一一主一從就配置完畢了,簡單點說只是修改了一句話即可。slaveof 127.0.0.1 7001
6、先從7001、7002依次啓動二個服務。
啓動服務後二個服務的控制檯分別會顯示主從信息。
主控制檯會顯示那些從服務加入進來。內容顯示7002分別加入進來了並同步數據
從控制檯會顯示加入了那個主服務。
7、主從測試
redis-cli.exe -h 127.0.0.1 -p 7001 #首先連接7001
127.0.0.1:7001> keys * #查看是否存在key
(empty list or set) #顯示不存在任何key
127.0.0.1:7001> set testkey tesvvalue #設置一個名字叫做testkey的key,值爲tesvvalue。
OK #賦值成功
127.0.0.1:7001> quit #退出7001
redis-cli.exe -h 127.0.0.1 -p 7002 #連接7002
127.0.0.1:7002> keys * #查看是否存在key
1) "testkey" #顯示存在一個名字叫做testkey的key
127.0.0.1:7002> get testkey #獲得testkey的值
"tesvvalue" #值爲tesvvalue
127.0.0.1:7002> set noinsert yes #測試是否可以在7002set數據。
(error) READONLY You can't write against a read only slave. #出錯,提示這是一個只讀的從服務。
經過以上測試表示我們的主從複製成功了。
第11 Windows 之Redis 哨兵機制
11.1 哨兵機制概述
Redis的哨兵(sentinel) 系統用於管理多個 Redis 服務器,該系統執行以下三個任務:
· 監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常。
· 提醒(Notification):當被監控的某個 Redis出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程序發送通知。
· 自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級爲新的Master, 並讓失效Master的其他Slave改爲複製新的Master; 當客戶端試圖連接失效的Master時,集羣也會向客戶端返回新Master的地址,使得集羣可以使用Master代替失效Master。
哨兵(sentinel) 是一個分佈式系統,你可以在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議(gossipprotocols)來接收關於Master是否下線的信息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪個Slave作爲新的Master.
每個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送消息,以確認對方是否”活”着,如果發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的”主觀認爲宕機” Subjective Down,簡稱sdown).
若“哨兵羣”中的多數sentinel,都報告某一master沒響應,系統才認爲該master"徹底死亡"(即:客觀上的真正down機,Objective Down,簡稱odown),通過一定的vote算法,從剩下的slave節點中,選一臺提升爲master,然後自動修改相關配置.
雖然哨兵(sentinel) 釋出爲一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你可以在啓動一個普通 Redis 服務器時通過給定 --sentinel 選項來啓動哨兵(sentinel).
哨兵(sentinel) 的一些設計思路和zookeeper非常類似
11.2 哨兵機制圖解
11.3 windows 搭建哨兵機制
哨兵機制搭建一般流程:
參考 :10.3 Windows 系統配置Redis 主從配置
哨兵配置:
1、在每一個7001 和7002文件夾中都創建一個文sentinel.conf文件。
2、7001的sentinel.conf文件內容:
#當前Sentinel服務運行的端口
port 26379
#master
#Sentinel去監視一個名爲mymaster的主redis實例,這個主實例的IP地址爲本機地址127.0.0.1,端口號爲6379,
#而將這個主實例判斷爲失效至少需要2個 Sentinel進程的同意,只要同意Sentinel的數量不達標,自動failover就不會執行
sentinel monitor mymaster 127.0.0.1 7001 1
#指定了Sentinel認爲Redis實例已經失效所需的毫秒數。當 實例超過該時間沒有返回PING,或者直接返回錯誤,那麼Sentinel將這個實例標記爲主觀下線。
#只有一個 Sentinel進程將實例標記爲主觀下線並不一定會引起實例的自動故障遷移:只有在足夠數量的Sentinel都將一個實例標記爲主觀下線之後,實例纔會被標記爲客觀下線,這時自動故障遷移纔會執行
sentinel down-after-milliseconds mymaster 5000
#指定了在執行故障轉移時,最多可以有多少個從Redis實例在同步新的主實例,在從Redis實例較多的情況下這個數字越小,同步的時間越長,完成故障轉移所需的時間就越長
sentinel config-epoch mymaster 12
#如果在該時間(ms)內未能完成failover操作,則認爲該failover失敗
sentinel leader-epoch mymaster 13
3、7002 的sentinel.conf文件內容:
#當前Sentine2服務運行的端口
port 26479
#slave1
sentinel monitor mymaster 127.0.0.1 7001 1
sentinel down-after-milliseconds mymaster 5000
sentinel config-epoch mymaster 12
sentinel leader-epoch mymaster 13
4、編寫7001_sentinel.bat 和7002_sentinel.bat 腳本指令
7001_sentinel.bat 內容如下:
cd 7001
title sentinel-7001
redis-server.exe sentinel.conf --sentinel
說明:title命名規則 啓動成功後顯示的title, 注意“--sentinel”不是註釋,必須存在。
7002_sentinel.bat 內容如下:
cd 7002
title sentinel-7002
redis-server.exe sentinel.conf --sentinel
說明:title命名規則 啓動成功後顯示的title, 注意“--sentinel”不是註釋,必須存在。
5、測試
啓動redis 主服務(7001.bat) 和redis 從服務(7002bat)
啓動redis 主服務的哨兵機制(7001_sentinel.bat) 和redis 從服務的哨兵機制(7002_sentinel.bat)
查看是主庫還是從庫,cmd進入對應的redis文件夾-》redis-cli.exe -p 服務端口
然後再輸入: info replication
7001 主:
7002 從:
3、查看哨兵sentinel狀態,cmd進入對應的redis文件夾-》redis-cli.exe -p 哨兵配置端口,輸入info sentinel
主:
從:
4、關閉掉主庫,然後再按照步驟5測試一遍+觀察。從庫會變成主庫,再啓動原來被關閉掉的主庫後,會自動變成從庫。
第12 Redis 事務
Redis 事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:
事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。
一個事務從開始到執行會經歷以下三個階段:
開始事務。
命令入隊。
執行事務。
12.1 實例
以下是一個事務的例子, 它先以 MULTI 開始一個事務, 然後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的所有命令:
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
12.2 事務常用命令
序號 |
命令及描述 |
1 |
DISCARD |
2 |
EXEC |
3 |
MULTI |
4 |
UNWATCH |
5 |
WATCH key [key ...] |
第13 Redis 持久化
13.1 什麼是持久化
什麼是Redis持久化,就是將內存數據保存到硬盤。
Redis 持久化存儲 (AOF 與 RDB 兩種模式)
13.2 RDB持久化
RDB 是在某個時間點將數據寫入一個臨時文件,持久化結束後,用這個臨時文件替換上次持久化的文件,達到數據恢復。
優點:使用單獨子進程來進行持久化,主進程不會進行任何 IO 操作,保證了 redis 的高性能
缺點:RDB 是間隔一段時間進行持久化,如果持久化之間 redis 發生故障,會發生數據丟失。所以這種方式更適合數據要求不嚴謹的時候
這裏說的這個執行數據寫入到臨時文件的時間點是可以通過配置來自己確定的,通過配置redis 在 n 秒內如果超過 m 個 key 被修改這執行一次 RDB 操作。這個操作就類似於在這個時間點來保存一次 Redis 的所有數據,一次快照數據。所有這個持久化方法也通常叫做 snapshots。
RDB 默認開啓,redis.conf 中的具體配置參數如下;
#dbfilename:持久化數據存儲在本地的文件
dbfilename dump.rdb
#dir:持久化數據存儲在本地的路徑,如果是在/redis/redis-3.0.6/src下啓動的redis-cli,則數據會存儲在當前src目錄下
dir ./
##snapshot觸發的時機,save
##如下爲900秒後,至少有一個變更操作,纔會snapshot
##對於此值的設置,需要謹慎,評估系統的變更操作密集程度
##可以通過“save “””來關閉snapshot功能
#save時間,以下分別表示更改了1個key時間隔900s進行持久化存儲;更改了10個key300s進行存儲;更改10000個key60s進行存儲。
save 900 1
save 300 10
save 60 10000
##當snapshot時出現錯誤無法繼續時,是否阻塞客戶端“變更操作”,“錯誤”可能因爲磁盤已滿/磁盤故障/OS級別異常等
stop-writes-on-bgsave-error yes
##是否啓用rdb文件壓縮,默認爲“yes”,壓縮往往意味着“額外的cpu消耗”,同時也意味這較小的文件尺寸以及較短的網絡傳輸時間
rdbcompression yes
13.3 AOF持久化
Append-only file,將“操作 + 數據”以格式化指令的方式追加到操作日誌文件的尾部,在 append 操作返回後(已經寫入到文件或者即將寫入),才進行實際的數據變更,“日誌文件”保存了歷史所有的操作過程;當 server 需要數據恢復時,可以直接 replay 此日誌文件,即可還原所有的操作過程。AOF 相對可靠,它和 mysql 中 bin.log、apache.log、zookeeper 中 txn-log 簡直異曲同工。AOF 文件內容是字符串,非常容易閱讀和解析。
優點:可以保持更高的數據完整性,如果設置追加 file 的時間是 1s,如果 redis 發生故障,最多會丟失 1s 的數據;且如果日誌寫入不完整支持 redis-check-aof 來進行日誌修復;AOF 文件沒被 rewrite 之前(文件過大時會對命令進行合併重寫),可以刪除其中的某些命令(比如誤操作的 flushall)。
缺點:AOF 文件比 RDB 文件大,且恢復速度慢。
我們可以簡單的認爲 AOF 就是日誌文件,此文件只會記錄“變更操作”(例如:set/del 等),如果 server 中持續的大量變更操作,將會導致 AOF 文件非常的龐大,意味着 server 失效後,數據恢復的過程將會很長;事實上,一條數據經過多次變更,將會產生多條 AOF 記錄,其實只要保存當前的狀態,歷史的操作記錄是可以拋棄的;因爲 AOF 持久化模式還伴生了“AOF rewrite”。
AOF 的特性決定了它相對比較安全,如果你期望數據更少的丟失,那麼可以採用 AOF 模式。如果 AOF 文件正在被寫入時突然 server 失效,有可能導致文件的最後一次記錄是不完整,你可以通過手工或者程序的方式去檢測並修正不完整的記錄,以便通過 aof 文件恢復能夠正常;同時需要提醒,如果你的 redis 持久化手段中有 aof,那麼在 server 故障失效後再次啓動前,需要檢測 aof 文件的完整性。
AOF 默認關閉,開啓方法,修改配置文件 reds.conf:appendonly yes
##此選項爲aof功能的開關,默認爲“no”,可以通過“yes”來開啓aof功能
##只有在“yes”下,aof重寫/文件同步等特性纔會生效
appendonly yes
##指定aof文件名稱
appendfilename appendonly.aof
##指定aof操作中文件同步策略,有三個合法值:always everysec no,默認爲everysec
appendfsync everysec
##在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示“不暫緩”,“yes”表示“暫緩”,默認爲“no”
no-appendfsync-on-rewrite no
##aof文件rewrite觸發的最小文件尺寸(mb,gb),只有大於此aof文件大於此尺寸是纔會觸發rewrite,默認“64mb”,建議“512mb”
auto-aof-rewrite-min-size 64mb
##相對於“上一次”rewrite,本次rewrite觸發時aof文件應該增長的百分比。
##每一次rewrite之後,redis都會記錄下此時“新aof”文件的大小(例如A),那麼當aof文件增長到A*(1 + p)之後
##觸發下一次rewrite,每一次aof記錄的添加,都會檢測當前aof文件的尺寸。
auto-aof-rewrite-percentage 100
AOF 是文件操作,對於變更操作比較密集的 server,那麼必將造成磁盤 IO 的負荷加重;此外 linux 對文件操作採取了“延遲寫入”手段,即並非每次 write 操作都會觸發實際磁盤操作,而是進入了 buffer 中,當 buffer 數據達到閥值時觸發實際寫入(也有其他時機),這是 linux 對文件系統的優化,但是這卻有可能帶來隱患,如果 buffer 沒有刷新到磁盤,此時物理機器失效(比如斷電),那麼有可能導致最後一條或者多條 aof 記錄的丟失。通過上述配置文件,可以得知 redis 提供了 3 中 aof 記錄同步選項:
always:每一條 aof 記錄都立即同步到文件,這是最安全的方式,也以爲更多的磁盤操作和阻塞延遲,是 IO 開支較大。
everysec:每秒同步一次,性能和安全都比較中庸的方式,也是 redis 推薦的方式。如果遇到物理服務器故障,有可能導致最近一秒內 aof 記錄丟失(可能爲部分丟失)。
no:redis 並不直接調用文件同步,而是交給操作系統來處理,操作系統可以根據 buffer 填充情況 / 通道空閒時間等擇機觸發同步;這是一種普通的文件操作方式。性能較好,在物理服務器故障時,數據丟失量會因 OS 配置有關。
其實,我們可以選擇的太少,everysec 是最佳的選擇。如果你非常在意每個數據都極其可靠,建議你選擇一款“關係性數據庫”吧。
AOF 文件會不斷增大,它的大小直接影響“故障恢復”的時間, 而且 AOF 文件中歷史操作是可以丟棄的。AOF rewrite 操作就是“壓縮”AOF 文件的過程,當然 redis 並沒有採用“基於原 aof 文件”來重寫的方式,而是採取了類似 snapshot 的方式:基於 copy-on-write,全量遍歷內存中數據,然後逐個序列到 aof 文件中。因此 AOF rewrite 能夠正確反應當前內存數據的狀態,這正是我們所需要的;*rewrite 過程中,對於新的變更操作將仍然被寫入到原 AOF 文件中,同時這些新的變更操作也會被 redis 收集起來(buffer,copy-on-write 方式下,最極端的可能是所有的 key 都在此期間被修改,將會耗費 2 倍內存),當內存數據被全部寫入到新的 aof 文件之後,收集的新的變更操作也將會一併追加到新的 aof 文件中,此後將會重命名新的 aof 文件爲 appendonly.aof, 此後所有的操作都將被寫入新的 aof 文件。如果在 rewrite 過程中,出現故障,將不會影響原 AOF 文件的正常工作,只有當 rewrite 完成之後纔會切換文件,因爲 rewrite 過程是比較可靠的。*
觸發 rewrite 的時機可以通過配置文件來聲明,同時 redis 中可以通過 bgrewriteaof 指令人工干預。
redis-cli -h ip -p port bgrewriteaof
因爲 rewrite 操作 /aof 記錄同步 /snapshot 都消耗磁盤 IO,redis 採取了“schedule”策略:無論是“人工干預”還是系統觸發,snapshot 和 rewrite 需要逐個被執行。
AOF rewrite 過程並不阻塞客戶端請求。系統會開啓一個子進程來完成。
13.4 AOF與RDB區別
AOF 和 RDB 各有優缺點,這是有它們各自的特點所決定:
1) AOF 更加安全,可以將數據更加及時的同步到文件中,但是 AOF 需要較多的磁盤 IO 開支,AOF 文件尺寸較大,文件內容恢復數度相對較慢。
*2) snapshot,安全性較差,它是“正常時期”數據備份以及 master-slave 數據同步的最佳手段,文件尺寸較小,恢復數度較快。
可以通過配置文件來指定它們中的一種,或者同時使用它們(不建議同時使用),或者全部禁用,在架構良好的環境中,master 通常使用 AOF,slave 使用 snapshot,主要原因是 master 需要首先確保數據完整性,它作爲數據備份的第一選擇;slave 提供只讀服務(目前 slave 只能提供讀取服務),它的主要目的就是快速響應客戶端 read 請求;但是如果你的 redis 運行在網絡穩定性差 / 物理環境糟糕情況下,建議你 master 和 slave 均採取 AOF,這個在 master 和 slave 角色切換時,可以減少“人工數據備份”/“人工引導數據恢復”的時間成本;如果你的環境一切非常良好,且服務需要接收密集性的 write 操作,那麼建議 master 採取 snapshot,而 slave 採用 AOF。
第14 Redis 發佈訂閱
Redis 發佈訂閱(pub/sub)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。
Redis 客戶端可以訂閱任意數量的頻道。
下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:
當有新消息通過 PUBLISH 命令發送給頻道 channel1 時, 這個消息就會被髮送給訂閱它的三個客戶端:
14.1 實例
以下實例演示了發佈訂閱是如何工作的。在我們實例中我們創建了訂閱頻道名爲 redisChat:
redis 127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
現在,我們先重新開啓個 redis 客戶端,然後在同一個頻道 redisChat 發佈兩次消息,訂閱者就能接收到消息。
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
(integer) 1
# 訂閱者的客戶端會顯示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
14.2 Redis 發佈訂閱常用命令
序號 |
命令及描述 |
1 |
PSUBSCRIBE pattern [pattern ...] |
2 |
PUBSUB subcommand [argument [argument ...]] |
3 |
PUBLISH channel message |
4 |
PUNSUBSCRIBE [pattern [pattern ...]] |
5 |
SUBSCRIBE channel [channel ...] |
6 |
UNSUBSCRIBE [channel [channel ...]] |
第15 Redis 集羣
參考文章地址:https://www.cnblogs.com/yy3b2007com/p/11033009.html