Redis安裝配置、內存維護、數據持久化、遠程與GUI、Docker安裝、基本數據類型和命令、springboot與Jedis和Lettuce、事務、集羣

1、安裝和啓動

使用VMware創建一個CentOS7的系統環境。到Redis官網可以查看下載、編譯和運行的操作命令:

wget http://download.redis.io/releases/redis-5.0.7.tar.gz
tar xzf redis-5.0.7.tar.gz
# 解壓到指定目錄可以如下指令
tar zxvf redis-5.0.7.tar.gz -C /opt
cd redis-5.0.7
make
# 指定安裝目錄可以用以下命令
make PREFIX=/usr/local/redis install
# 但最好加上 MALLOC=libc這個設置,不然安裝會報錯找不到文件jemalloc/jemalloc.h
make PREFIX=/usr/local/redis MALLOC=libc install

redis是C語言開發的,編譯需要gcc,所以如果沒有安裝gcc的話,make命令會報錯,那麼就安裝一下(標準版的CentOS是沒有gcc的需要安裝):

# 養成習慣,第一次使用yum的時候先更新一下
yum -y update
yum -y install gcc automake autoconf libtool make

然後進入到安裝redis的目錄,啓動redis。

# 啓動服務端
./redis-server
# 啓動客戶端時-h -p可省略,默認是127.0.0.1和6379
# 如果沒有啓動服務端直接啓動客戶端的話會提示連接被拒絕
./redis-cli
# 啓動客戶端後可以使用如下命令看看是否成功連接了服務端
set name eric
get name
# 當然也可以使用如下命令查看redis相關進程是否開啓
ps -ef | grep redis

2、Redis配置

redis的配置文件在安裝目錄中是沒有的,用戶可以自己創建一個,但是redis的源文件中有個配置文件,我們可以基於這個配置來自定義成我們自己需要的樣子。所以我們可以把解壓縮後源文件中的配置文件複製到安裝目錄中,和bin文件夾同目錄。

cp /opt/redis-5.0.7/redis.conf /usr/local/redis/

可以大概看一下核心的幾個配置:

# 默認只允許本地訪問
bind 127.0.0.1
# 端口設置
port 6379
# 默認不以守護進程方式運行,所以啓動redis服務端後佔用終端,實際中我們可以設置成yes
daemonize no
# 如果以守護進程方式運行,進程信息記錄在如下文件
pidfile /var/run/redis_6379.pid
# 日誌相關
loglevel notice
# 數據庫數量,以0開始,可以切換
databases 16
# 這是快照的規則,60秒內檢查是否有10000次變更,如果有就存一次快照;300秒內是否有10次變更,如果有就...
save 900 1
save 300 10
save 60 10000
# 默認開啓持久化文件的壓縮算法
rdbcompression yes
# 持久化文件,默認在/bin目錄下的dump.rdb
dbfilename dump.rdb
# 還有一些master主從集羣的設置,具體使用到時再說
# 默認是不需要密碼的
# requirepass foobared
# 最大客戶端連接
# maxclients 10000
# 最大內存使用
# maxmemory <bytes>

還有很多配置,redis的配置文件中針對每一項都有詳細的說明,如果要搜索的話,可以在非編輯狀態下,輸入/和關鍵字進行搜索,搜索到多個的話,可以使用n切換到下一個進行查看。

我們自己需要設置的3個主要配置就是:

# 註釋掉允許所有用戶訪問,開發時用
# bind 127.0.0.1
# 設置成yes
daemonize yes
# 設置密碼,不設置密碼的話可能遠程無法連接,所以建議開啓
requirepass 123456

那我們啓動redis-server的時候需要帶上我們自定義的配置文件:

./bin/redis-server ./redis.conf

設置了密碼後,客戶端連接的話,也要設置密碼,也就是認證:

./redis-cli -a 123456

3、內存維護策略

redis的使用是基於內存的,如果內存不能有效維護好,那麼內存很容易被爆滿掉,發生內存泄露等問題。所以可以有以下2個方式來維護內存。

(1)合理利用key的有效性,部分key應該設置有效期,這樣可以不需要一直佔用內存。

set name eric
# ttl查看key的剩餘時間,-1表示永久,-2表示過期,時間單位秒
ttl name
# 設置過期時間10秒
expire name 10
# 10秒後發現沒有這個key了
keys *
# ttl查看的話發現返回-2表示過期
ttl name
# 如果要設置不過期
persist name

(2)有一個內存維護策略,就是按照什麼規則刪除一些補償應的數據。最前面2種是比較常用的。

# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used

4、關閉redis數據是否持久化的問題

正常情況下,數據要持久化的話要滿足我們看的那個redis配置文件中的規定時間內達到一定變更次數的規則要求,所以如果正常滿足的話,那麼就會正常持久化。

(1)如果遇到非正常關閉的話,比如斷電,我們可以用殺掉進程的方式模擬:

ps -ef | grep redis
# 得到redis-server的PID後kill
kill -9 PID
# 然後再次啓動redis-server後在客戶端keys * 查看發現之前的數據沒有持久化

(2)如果正常關閉的話,會持久化數據。正常關閉是在客戶端輸入:

shutdown
# 然後再次啓動redis-server後在客戶端keys * 查看發現之前的數據可以查詢到,說明關閉之前做了持久化操作

5、遠程連接

RedisDestopManager現在收費了。網上貌似可以搜索到老舊的版本,這些老舊的版本還繼續可以使用。基礎的使用應該是沒有問題的。

這裏遠程連接的時候,需要開放一下centos的防火牆。

# 添加一個放行端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 重啓防火牆
firewall-cmd --reload
# 查看開放的端口
firewall-cmd --list-ports

遠程連接後,可以發現默認有16個庫。

6、使用Docker部署redis

安裝好docker,設置好阿里雲或國內其他的倉庫鏡像後:

docker pull redis:5.0.7
# -d後臺運行,--requirepass設置密碼,-p設置端口映射
docker run -d --name redis6379 -p 6379:6379 redis:5.0.7 --requirepass "123456"
# 如果是docker開啓的,那麼我們想進入docker並打開客戶端的話可以操作
docker exec -it redis6379 redis-cli -a 123456

同樣的如果centos的防火牆沒有開放6379的話設置一下,如果之前已經設置過了,那麼久不用設置了,直接在外部就可以遠程連接了。

7、常見命令

# 設置一個鍵值對
set key value
# 獲取一個key的value
get key
# 查詢所有key
keys *
# 查看匹配na*的key,比如name
keys na*
# 是否存在key,存在返回1,不存在返回0
exists key名
# 設置剩餘過期時間
expire key名 多少秒
# 刪除key
del key名
# 查看過期剩餘時間,-1永不過期,-2已過期,正整數表示秒數
ttl key名
# 設置永不過期
persist key名
# 選擇數據庫,默認是index爲0的數據庫中操作
select index
# 重命名key
rename 舊的key名 新的key名
# 設置過期時間單位爲毫秒
PEXPIRE key milliseconds
# 以毫秒爲單位返回剩餘過期時間
PTTL key
# 將key移動到數據庫index爲x的庫中
MOVE key dbx
# 返回key的數據類型
type key

一些限時優惠、驗證碼、數據緩存、限制訪問頻率等場景下,可以結合exists keyexpire key seconds使用。

key的命名建議,不太太長或者雜亂,一般如下爲key設置某種規律來命名:

set user:eric:1111 1
set user:tom:2222 2

8、基本數據類型

(1)string
一個key對應的value最大可以達到512M,所以這個string類型的值可以存很多種類的數據,設置序列化的字符串都可以存在裏面。

# 如果這個key不存在,則賦值,因爲直接使用set的話如果存在該key,它的值會被覆蓋
setnx key value
# 賦值並設置過期時間
setex key seconds value
# 修改key的值,比如原先name是eric,執行後變成etic
setrange name 1 t

# 獲取值得一個範圍子字符串也是同理
# 比如value是eric,start是1,end是2,那麼結果是ri,包含start和end,如果是1,1,那麼返回的就是r
getrange key start end
# 設置新值返回舊值,如果之前不存在,則返回nil
getset key value
# 長度
strlen key
# 批量寫讀
mset key1 value1 key2 value2 ...
mget key1 key2 ...
# 加1,如果一開始不存在key,值創建key並設置value爲1,以後每次執行就爲value+1
incr key
# 減1
decr key
# 以下自定義自增
incrby key 自增值
decyby key 自增值
# 把value追加在原來value值後面
append key value

string數據類型的使用場景包括:常用字符串或者json字符串;因爲是二進制安全的,所以可以把圖片的內容當做字符串存儲;常見的計數器,比如關注數粉絲數等,因爲redis的incr和decr操作具有原子性,不會發生現成不安全的問題。

(2)hash
操作命令和之前的string操作命令類似,大都是在命令之前加了一個h字母,表示對hash的操作。

# 單個field
hset key field value
# 多個field
hmset key field1 value1 field2 value2 ...
# 查看所有
hgetall key
# 查看某個field值
hget key field
# 刪除所有
del key
# 刪除某個field
hdel key field
# 查看所有fields
hkeys key
# 查看字段數量
hlen key
# 賦值前檢查是否存在
hsetnx key field value
# 自增
hincrby key field 自增量
# 自增浮點
hincrfloat key field 自增量
# 字段是否存在
hexists key field

一般用hash來存儲對象。因爲string存儲對象沒有什麼優勢,比如:

  • 如果用json字符串存儲對象的話,如果需要修改這個對象中某個屬性的值,那麼需要取出整個字符串然後反序列化,修改後再序列化成json字符串,開銷太大
  • 如果把一個對象的多個屬性分開用多個key-value存在redis中,那恐怕是瘋了。

所以,hash目前看來是最適合存儲對象的一種數據類型。

9、springboot+jedis

jedis是很早的一個對redis封裝的客戶端,供java開發時使用。主要的使用包括(使用Lombok的話,entity上加個註解@Data即可):

springboot2.0的話默認的redis客戶端時Lettuce,所以我們要使用jedis的話不能選擇默認的,所以需要自己增加一個依賴:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

配置:

spring.redis.host=172.16.25.137
spring.redis.port=6379
spring.redis.password=123456
spring.redis.timeout=2000
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.pool.max-idle=6
spring.redis.jedis.pool.max-active=10

設置pool,創建一個JedisConfig:

@Configuration
public class JedisConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Bean
    public JedisPool getJedisPool(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxTotal(maxActive);

        return new JedisPool(jedisPoolConfig, host, port, timeout, password);
    }
}

然後就可以用這個Pool獲取Jedis客戶端來使用了,下面直接寫在Controller中。可以看到,jedis中的方法基本上都是對應着redis的基礎命令的。

@RestController
public class UserController {

    @Autowired
    JedisPool jedisPool;

    @GetMapping("/user")
    public String getUser(Integer id){
        Jedis jedis = jedisPool.getResource();
        String stringKey = "name:" + id;
        String hashKey = "user:" + id;

        StringBuilder res = new StringBuilder();

        if (jedis.exists(stringKey) && jedis.exists(hashKey)){
            res.append("從redis查詢的數據:");
            res.append(jedis.get(stringKey));
            res.append(jedis.hget(hashKey, "name"));
        }else{
            // 模擬從數據庫查詢出這個user的全部信息
            User user = new User();
            user.setId(id);
            user.setName("eric");
            user.setAge(30);
            // 設置string數據類型
            jedis.set(stringKey, user.getName());
            // 設置hash數據類型
            Map<String, String> map = new HashMap<>();
            map.put("name", user.getName());
            map.put("age", String.valueOf(user.getAge()));
            map.put("id", String.valueOf(user.getId()));
            jedis.hmset(hashKey, map);
            res.append("從數據庫查詢的數據");
        }
        jedis.close();
        return res.toString();
    }
}

我們再瀏覽器中,訪問localhost:8080/user?id=123可以看到效果,並結合rdm客戶端查看redis中是否有數據。

10、springboot+lettuce

初始化項目時選擇spring data redis或者增加如下依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置:

spring.redis.host=172.16.25.138
spring.redis.port=6379
spring.redis.password=123456
spring.redis.lettuce.pool.max-active=10
spring.redis.lettuce.pool.min-idle=2
spring.redis.lettuce.pool.max-idle=6
spring.redis.lettuce.shutdown-timeout=200

自己編寫一個User類,同上。然後使用默認的RedisTemplate,全部寫在Controller測試一下:

@RestController
public class UserController {

    @Autowired
    RedisTemplate redisTemplate;

    @GetMapping("/user")
    public String getUser(Integer id){
        String stringKey = "name:" + id;
        String hashKey = "user:" + id;

        StringBuilder res = new StringBuilder();

        if (redisTemplate.hasKey(stringKey) && redisTemplate.hasKey(hashKey)){
            res.append("從redis查詢的數據:");
            res.append(redisTemplate.opsForValue().get(stringKey));
            res.append(redisTemplate.opsForHash().get(hashKey, "name"));
        }else{
            // 模擬從數據庫查詢出這個user的全部信息
            User user = new User();
            user.setId(id);
            user.setName("eric");
            user.setAge(30);
            // 設置string數據類型
            redisTemplate.opsForValue().set(stringKey, user.getName());
            // 設置hash數據類型
            Map<String, String> map = new HashMap<>();
            map.put("name", user.getName());
            map.put("age", String.valueOf(user.getAge()));
            map.put("id", String.valueOf(user.getId()));
            redisTemplate.opsForHash().putAll(hashKey, map);
            res.append("從數據庫查詢的數據");
        }
        return res.toString();
    }
}

啓動發現報錯,因爲spring data redis需要commons-pool2依賴。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

我們再瀏覽器中,訪問localhost:8080/user?id=123可以看到效果,並結合rdm客戶端查看redis中是否有數據。

但這裏發現一個問題,就是我們在代碼中讀取沒有問題,但是在rdm中查看的時候發現有亂碼,key和value都有亂碼(在key的前面增加了\xac\xed\x00\x05t\x00\b一串字符),導致開發的時候不方便,在redis-cli查詢時也會有問題。

可以先刪除之前的所有的key,也就是刪除當前數據庫或者所有數據庫:

# 刪除當前數據庫
flushdb
# 刪除所有數據庫
flushall

這個時候我們就需要自定義配置RedisTemplate,也就是不用默認的。創建一個RedisConfig類:

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> getRedisTemplate(LettuceConnectionFactory factory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        // 以下就是自定義的部分,主要是客戶端查看時的亂碼問題,替換了原先默認的序列化方式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

然後一切就好了。當然,這裏可以做其他自定義的配置,不僅僅是序列化的配置,其他的還包括緩存策略等。

RedisTemplate的操作方法和之前的Jedis明顯不同,它封裝了很多方法提供給我們,所以在具體使用時需要學習一下,我們也可以通過一些設置或者創建一些工具類,方便每次的調用。

11、Set數據類型

# 以下命令只會添加1 2 3 4 6,因爲是不可重複的
sadd scores 1 2 3 4 4 6
# 所以查詢數量時返回5
scard scores
# 檢查是否是集合中元素,以下返回0,如果是則返回1
sismember scores 5
# 返回集合中所有元素
smembers scores
# 隨機返回幾個元素,比如隨機返回2個元素
srandmember scores 2
# 刪除某個元素,比如2,如果2存在就返回1,如果2不存在返回0,可以刪除多個
srem scores 2
# 把scores中元素5移動到number集合中,如果目標集合中已有了,那移動過去就自動合併了
smove scores number 5
# 返回減去nums2中元素的nums1剩餘的元素,即在num1中不在num2中的元素
sdiff nums1 nums2
# 把差集結果存在nums3中
sdiffstore nums3 nums1 nums2
# 返回交集
sinter nums1 nums2
# 直接存儲交集結果到新的集合
sinter nums4 nums1 nums2
# 下面是丙級和存儲
sunion nums1 nums2
sunionstore nums5 nums1 nums2

使用場景可以結合集合的特點:集合之間可以做並集交集等操作,可以查詢共同好友共同興趣等;集合的唯一性,可以記錄獨立ip等信息。

12、Zset數據類型

# 添加。不同點是每個元素前有個分數,排序就是根據這個分數來的
zadd z1 99 eric 88 tom 77 jerry 66 jason
# 返回幾個元素,4
zcard z1
# 返回最小最大之間個數,2
zcount z1 70 90
# 返回索引,是3.如果元素不在,則返回nil
# 內部默認從小打到排列,所以eric是最後一個,索引是3
zrank z1 eric
# 從小到大返回索引起止間的元素,0 -1表示全部
zrange z1 0 -1
# 降序排列,0 -1仍然是起止索引
zrevrange z1 0 -1
# 按照分數從小到大
zrangebyscore z1 70 90
# 按照分數降序
zrevrangebyscore z1 70 90
# 刪除key
del key
# 刪除元素
zrem key member
# 按照索引刪除,zremrangebyrank z1 0 1 刪除0和1的共2個
zremrangebyrank key start stop
# 按照分數刪除,zremrangebyscore z1 80 90 刪除分數在80到90之間的
zremrangebyscore key min max
# 給某一個member增加分數
zincrby key 增加量 menber

zset的應用場景可以是排行榜,獲取其他可以使用權重排行的場景。

13、HyperLogLog結構

# 創建一個HyperLogLog
pfadd p1 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
# 它返回的是基數的個數,5
pfcount p1
# 合併
pfmerge destkey sourcekey1 sourcekey2 ...

應用場景可以統計每天獨立ip等信息,但是HyperLogLog的缺陷是隻能獲得基數,不能獲取具體的信息,所以可以配合其他數據結構一起使用。還可以統計文章的閱讀數,比如存儲的元素是日期+ip的形式,那麼同一天同一ip雖然pfadd增加到了裏面,但是統計基數的時候只算1個。其實有點類似於set的用法。它和set的區別就是,如果數據量很大而且不需要知道詳細信息,那麼可以用HyperLogLog,如果需要知道詳細信息則可以使用set。

14、訂閱發佈功能

# 訂閱sms頻道channel
subscribe sms
# 打開另一個終端,向這個頻道發佈信息,上面那個訂閱的終端會顯示收到這條信息
publish sms hello,world
# 還有批量訂閱,根據pattern訂閱以及退訂
psubscribe pattern
unsubscribe sms
punsubscribe pattern

訂閱發佈功能在現代軟件中使用的很多,但是redis的發佈訂閱功能被使用的較少,畢竟有其他各種消息隊列(MQ)可以使用。

15、多數據庫

默認有16個數據庫,下標索引從0開始。可以在配置文件中設置如下指定使用哪個數據庫:

spring.redis.database=0

可以使用如下命令切換數據庫:

# 切換數據庫
select 1
# 移動某個key到哪個庫,比如move name 15
move key databaseindex

多數據庫可以提供給不同的應用微服務來使用。比如支付的、用戶信息的等使用不同的數據庫。也可以根據不同開發階段使用不同的庫,比如開發時用0,測試用1,生成用2等。

16、Redis事務

redis的事務是把所有命令序列化按順序放在隊列中,可以發現有QUEUED提示,直到遇到exec開始執行,或者遇到discard放棄取消事務和隊列。不可以加塞,也不可以回滾。

在一個終端上模擬設置兩個賬戶,模擬轉賬:

set account:a 100
set account:b 50
# 然後啓動事務
multi
# a賬戶減少10元
decrby account:a 10
# b賬戶增加10元
incrby account:b 10
# 此時可以在另一個終端查詢這兩個key的數據發現沒有變化
# 提交執行。discard是取消事務
exec
# 此時可以在另一個終端查詢這兩個key的數據發現有變化了

事務的報錯:

(1)在終端中一般情況下multi開啓事務後,輸入的每一條指令redis都會做檢查,如果被檢查出來有問題,那麼這個事務就需要重寫,主要是檢查命令;

(2)但是有些問題是我們程序的問題,不是命令的錯誤。比如給非數字的字符串加1這樣的程序在事務中。那麼事務執行的時候,只有這條語句不被執行,其他的語句還是正常執行,而且不回滾。比如下面的,我們新建了一個key是name,事務中的這句命令得到了執行。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name hello
QUEUED
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range

watch監視:

127.0.0.1:6379> set a 100
OK
# 監視a,如果a被其他線程變動了,那麼此線程的事務就取消掉
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
# 在另一個終端上執行一個incr a之類的操作變動一下a
# 返回nil就是被取消了
127.0.0.1:6379> exec
(nil)

17、list數據類型

127.0.0.1:6379> lpush names eric tom jerry
(integer) 3
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"
127.0.0.1:6379> lpush names jason
(integer) 4
127.0.0.1:6379> lrange names 0 -1
1) "jason"
2) "jerry"
3) "tom"
4) "eric"
127.0.0.1:6379> rpush names hello
(integer) 5
127.0.0.1:6379> lrange names 0 -1
1) "jason"
2) "jerry"
3) "tom"
4) "eric"
5) "hello"
127.0.0.1:6379> lpop names
"jason"
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"
4) "hello"
127.0.0.1:6379> rpop names
"hello"
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"

18、集羣

redis集羣不是簡單的一主多從模式,而是無中心結構,也就是說所有的節點都是master節點,每個master節點對應一個或多個slave節點(這裏的纔是一主多從),相當於多個“一主多從”。至少需要3個master和3個slave才能建立集羣,因爲要投票半數以上,所以至少3個。這也就是說至少要部署6個獨立的節點。

master節點之間相互連接,半數以上的master如果連不上該master,那麼該master就被認爲fail了。

數據在redis集羣中的存儲是按照hash分佈在不同的節點中的,並不是所有的節點都是完整的數據。比如一共有16384個hash槽,如果有3個master節點,那麼0-5500的在master1上,5500-11000在master2上以此類推。這樣有一個key過來之後,hash過後根據值分配存儲在對應的master上。

(1)準備好6個節點

/usr/local下mkdir一個redis_cluster文件夾,名稱隨意,然後在下面新建6個文件夾,可以分別命名爲7000到7005。把redis安裝包中的conf文件拷貝到7000中,做好一些配置後,分別複製到7001-7005中去。主要修改如下(可參考官網):

# 以下是官網要求的必須
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
# 以下是其他一些配置
# 關閉保護模式可用於公網訪問
protected-mode no
daemonize yes
pidfile /var/run/redis_7000.pid
logfile "7000.log"
# dir /redis/data
# bind 127.0.0.1
# 連接主節點的密碼
masterauth 123456
# 各個節點保持一致
requirepass 123456

搜索在非編輯狀態下使用/
複製到其他文件夾下的時候,可以非編輯狀態下使用如下命令全部替換後保存。其中的冒號是要自己輸入的。

:%s/7000/7001/g

分別啓動這6個節點。啓動redis的時候需要帶上我們上面配置的6個文件。

./redis-server /usr/local/redis_cluster/7000/redis.conf
./redis-server /usr/local/redis_cluster/7001/redis.conf
./redis-server /usr/local/redis_cluster/7002/redis.conf
./redis-server /usr/local/redis_cluster/7003/redis.conf
./redis-server /usr/local/redis_cluster/7004/redis.conf
./redis-server /usr/local/redis_cluster/7005/redis.conf

可以通過ps -ef | grep redis查看是否啓動了6個redis進程。

(2)啓動集羣

./redis-cli --cluster create -a 123456 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1

可以看到因爲我們啓動集羣的時候用了6個節點,所以自動分配成3主3從,並且3主的hash槽也分配好了。再輸入yes同意設置後就好了。

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 191649ea7bd94e91e3978372ada3280a18e679bd 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
M: a5f2c009a564725a582a6b912a0a7807460c5856 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
M: 395b0a62b241bf9e0a7376e31f0af77ebdaac84d 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
S: f585f1c5a500577b031b92a64cb010db9712f167 127.0.0.1:7003
   replicates 191649ea7bd94e91e3978372ada3280a18e679bd
S: 64dd7f28856589d60537fdae2343ecbe69c7d0c4 127.0.0.1:7004
   replicates a5f2c009a564725a582a6b912a0a7807460c5856
S: 65748dd6826ea267fe355ddf8345489c6ed497e7 127.0.0.1:7005
   replicates 395b0a62b241bf9e0a7376e31f0af77ebdaac84d

(3)集羣的驗證

因爲集羣是去中心化的,意味着我們隨便連接哪個節點都可以。連接命令有一些不同,多了一個-c,表示連接的是集羣:

# 因爲在本機,所以省略了-h xxx.xxx.xxx.xxx
redis-cli -p 7000 -c -a 123456

輸入以下命令,查看當前連接節點的信息,比如是否主從節點,對應的主從節點是哪個等:

127.0.0.1:7000> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=7003,state=online,offset=462,lag=0
master_replid:d3d306c18c918f09d7c40206bb69ede376ca9564
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:462
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:462

以下命令是查看整個集羣的節點信息:

127.0.0.1:7000> cluster nodes
395b0a62b241bf9e0a7376e31f0af77ebdaac84d 127.0.0.1:7002@17002 master - 0 1581420073532 3 connected 10923-16383
64dd7f28856589d60537fdae2343ecbe69c7d0c4 127.0.0.1:7004@17004 slave a5f2c009a564725a582a6b912a0a7807460c5856 0 1581420073834 5 connected
191649ea7bd94e91e3978372ada3280a18e679bd 127.0.0.1:7000@17000 myself,master - 0 1581420072000 1 connected 0-5460
65748dd6826ea267fe355ddf8345489c6ed497e7 127.0.0.1:7005@17005 slave 395b0a62b241bf9e0a7376e31f0af77ebdaac84d 0 1581420073000 6 connected
f585f1c5a500577b031b92a64cb010db9712f167 127.0.0.1:7003@17003 slave 191649ea7bd94e91e3978372ada3280a18e679bd 0 1581420072529 4 connected
a5f2c009a564725a582a6b912a0a7807460c5856 127.0.0.1:7001@17001 master - 0 1581420073000 2 connected 5461-10922

myself表示當前登錄的節點。

slave後面跟着的就是對應master的id,這樣就可以知道主從關係。

數據讀取的時候可以看到,它會切換到某一個slot上。如果set時正好hash計算後可以存在當前slot上那麼久不會切換,如果需要存在其他slot上,它會先切換過去再存。讀取數據也是一樣的,如果在當前slot中直接返回,如果不在,則會切換slot後讀取數據返回。keys *只返回當前slot的keys數據。

主節點負責寫數據,從節點負責讀數據。

127.0.0.1:7000> set aa 123
OK
127.0.0.1:7000> get aa
"123"
127.0.0.1:7000> set bb 456
-> Redirected to slot [8620] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get bb
"456"
127.0.0.1:7001> keys *
1) "bb"
127.0.0.1:7001> get aa
-> Redirected to slot [1180] located at 127.0.0.1:7000
"123"

如果有ABC三個主節點,分別對應A1、B1、C1三個從節點,如果B節點掛掉了,那麼B1從節點會頂替B成爲主節點,保證可用性,如果B開啓後,B成爲B1的從節點。但如果B沒有啓動而B1也掛掉了,那麼整個集羣就fail了不可用。

(4)關閉集羣節點

就是關閉每個節點,但是如果節點多的話,可以寫成一個shutdown.sh文件然後執行這個文件來執行關閉。

/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7000 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7002 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7003 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7004 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7005 -a 123456 shutdown

然後給這個文件執行權限

chmod u+x shutdown.sh

最後執行即可。

同理,開啓這6個節點的命令也可以放在一個startup.sh文件中:

/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7000/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7001/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7002/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7003/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7004/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7005/redis.conf

然後給這個文件執行權限

chmod u+x shutdown.sh

執行開啓節點後,再使用我們上面提到的命令開啓集羣即可:

./redis-cli --cluster create -a 123456 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章