从零开始学Redis之半步神游(下)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 前言文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…种一棵树最好的时间是十年前,其次是现在

Redis Sentinel 搭建(可以自己试试)


1.环境说明

主机名称 IP地址 redis版本和角色说明
redis-master 192.168.56.11:6379 redis 5.0.3(主)
redis-slave01 192.168.56.12:6379 redis 5.0.3(从)
redis-slave02 192.168.56.13:6379 redis 5.0.3(从)
redis-master 192.168.56.11:26379 Sentinel01(主)
redis-slave01 192.168.56.12:26379 Sentinel02(从)
redis-slave02 192.168.56.13:26379 Sentinel03(从)

2.部署Sentinel

# 端口
port 26379
# 是否后台启动
daemonize yes
# pid文件路径
pidfile /var/run/redis-sentinel.pid
# 日志文件路径
logfile "/var/log/sentinel.log"
# 定义工作目录
dir /tmp
# 定义Redis主的别名, IP, 端口,这里的2指的是需要至少2个Sentinel认为主Redis挂了才最终会采取下一步行为
sentinel monitor mymaster 127.0.0.1 6379 2
# 如果mymaster 30秒内没有响应,则认为其主观失效
sentinel down-after-milliseconds mymaster 30000
# 如果master重新选出来后,其它slave节点能同时并行从新master同步数据的台数有多少个,显然该值越大,所有slave节点完成同步切换的整体速度越快,但如果此时正好有人在访问这些slave,可能造成读取失败,影响面会更广。最保守的设置为1,同一时间,只能有一台干这件事,这样其它slave还能继续服务,但是所有slave全部完成缓存更新同步的进程将变慢。
sentinel parallel-syncs mymaster 1
# 该参数指定一个时间段,在该时间段内没有实现故障转移成功,则会再一次发起故障转移的操作,单位毫秒
sentinel failover-timeout mymaster 180000
# 不允许使用SENTINEL SET设置notification-script和client-reconfig-script。
sentinel deny-scripts-reconfig yes
复制代码


修改三台Sentinel的配置文件,如下

[root@redis-master ~]# grep -Ev "^$|#" /usr/local/redis/sentinel.conf 
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
logfile "/var/log/sentinel.log"
dir "/tmp"
sentinel monitor mymaster 192.168.56.11 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
[root@redis-slave01 ~]# grep -Ev "^$|#" /usr/local/redis/sentinel.conf 
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
logfile "/var/log/sentinel.log"
dir "/tmp"
sentinel monitor mymaster 192.168.56.11 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
[root@redis-slave02 ~]# grep -Ev "^$|#" /usr/local/redis/sentinel.conf 
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
logfile "/var/log/sentinel.log"
dir "/tmp"
sentinel monitor mymaster 192.168.56.11 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
复制代码


3.启动Sentinel 启动的顺序:主Redis --> 从Redis --> Sentinel1/2/3

[root@redis-master ~]# redis-sentinel /usr/local/redis/sentinel.conf 
[root@redis-master ~]# ps -ef |grep redis
root      1295     1  0 14:03 ?        00:00:06 /usr/local/redis/src/redis-server 192.168.56.11:6379
root      1407     1  1 14:40 ?        00:00:00 redis-sentinel *:26379 [sentinel]
root      1412  1200  0 14:40 pts/1    00:00:00 grep --color=auto redis
[root@redis-slave01 ~]# redis-sentinel /usr/local/redis/sentinel.conf 
[root@redis-slave01 ~]# ps -ef |grep redis
root      1625     1  0 14:04 ?        00:00:06 /usr/local/redis/src/redis-server 192.168.56.12:6379
root      1715     1  1 14:41 ?        00:00:00 redis-sentinel *:26379 [sentinel]
root      1720  1574  0 14:41 pts/0    00:00:00 grep --color=auto redis
[root@redis-slave02 ~]# redis-sentinel /usr/local/redis/sentinel.conf 
[root@redis-slave02 ~]# ps -ef |grep redis
root      1628     1  0 14:07 ?        00:00:06 /usr/local/redis/src/redis-server 192.168.56.13:6379
root      1709     1  0 14:42 ?        00:00:00 redis-sentinel *:26379 [sentinel]
root      1714  1575  0 14:42 pts/0    00:00:00 grep --color=auto redis
复制代码


4.Sentinel操作

[root@redis-master ~]# redis-cli -p 26379   #哨兵模式查看
127.0.0.1:26379> sentinel master mymaster   #输出被监控的主节点的状态信息
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "192.168.56.11"
 5) "port"
 6) "6379"
 7) "runid"
 8) "bae06cc3bc6dcbff7c2de1510df7faf1a6eb6941"
 9) "flags"
10) "master"
......
127.0.0.1:26379> sentinel slaves mymaster   #查看mymaster的从信息,可以看到有2个从节点
1)  1) "name"
    2) "192.168.56.12:6379"
    3) "ip"
    4) "192.168.56.12"
    5) "port"
    6) "6379"
    7) "runid"
    8) "c86027e7bdd217cb584b1bd7a6fea4ba79cf6364"
    9) "flags"
   10) "slave"
......
2)  1) "name"
    2) "192.168.56.13:6379"
    3) "ip"
    4) "192.168.56.13"
    5) "port"
    6) "6379"
    7) "runid"
    8) "61597fdb615ecf8bd7fc18e143112401ed6156ec"
    9) "flags"
   10) "slave"
......
127.0.0.1:26379> sentinel sentinels mymaster    #查看其它sentinel信息
1)  1) "name"
    2) "ba12e2a4023d2e9bcad282395ba6b14030920070"
    3) "ip"
    4) "192.168.56.12"
    5) "port"
    6) "26379"
    7) "runid"
    8) "ba12e2a4023d2e9bcad282395ba6b14030920070"
    9) "flags"
   10) "sentinel"
......
2)  1) "name"
    2) "14fca3f851e9e1bd3a4a0dc8a9e34bb237648455"
    3) "ip"
    4) "192.168.56.13"
    5) "port"
    6) "26379"
    7) "runid"
    8) "14fca3f851e9e1bd3a4a0dc8a9e34bb237648455"
    9) "flags"
   10) "sentinel"
复制代码


Redis Lua 脚本


再这里我想说下,为啥我们要用Lua脚本呢? Lua脚本的好处

redis对lua脚本的调用是原子性的,所以一些特殊场景,比如像实现分布式锁,我们可以放在lua中实现

下面我带大家搭建一个最简单lua脚本demo

  • 添加依赖
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
         <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
复制代码
  • 编写Lua脚本 命名为 Test.lua 放在 resources下
local key = KEYS[1]
    --- 获取value
    local val = KEYS[2]
    --- 获取一个参数
    local expire = ARGV[1]
    --- 如果redis找不到这个key就去插入
    if redis.call("get", key) == false then
        --- 如果插入成功,就去设置过期值
        if redis.call("set", key, val) then
            --- 由于lua脚本接收到参数都会转为String,所以要转成数字类型才能比较
            if tonumber(expire) > 0 then
                --- 设置过期时间
                redis.call("expire", key, expire)
            end
            return true
        end
        return false
    else
        return false
    end
复制代码


  • 编写配置类
@Configuration
public class LuaConfiguration {
    @Bean
    public DefaultRedisScript<Boolean> redisScript() {
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("Test.lua")));
        redisScript.setResultType(Boolean.class);
        return redisScript;
    }
}
复制代码


  • 测试
@Test
    public void TestLua(){
        System.out.println("测试Lua开始");
        List<String> keys = Arrays.asList("testLua", "hello六脉神剑");
        Boolean execute = stringRedisTemplate.execute(redisScript, keys, "10000");
        System.out.println("测试Lua结束,并在下面打印结果");
        String testLua = stringRedisTemplate.opsForValue().get("testLua");
        System.out.println("结果是:"+testLua);
    }
复制代码


  • 结果
测试Lua开始
2019-12-03 10:48:01.469  INFO 246868 --- [           main] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2019-12-03 10:48:01.471  INFO 246868 --- [           main] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
测试Lua结束,并在下面打印结果
结果是:hello六脉神剑
复制代码


Redis使用Lua的好处


1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。

2.原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。

3.复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。

Redis使用Lua要注意的点


1.Lua脚本的bug特别可怕,由于Redis的单线程特点,一旦Lua脚本出现不会返回(不是返回值)得问题,那么这个脚本就会阻塞整个redis实例。

2.Lua脚本应该尽量短小实现关键步骤即可。(原因同上)

3.Lua脚本中不应该出现常量Key,这样会导致每次执行时都会在脚本字典中新建一个条目,应该使用全局变量数组KEYS和ARGV, KEYS和ARGV的索引都从1开始

4.传递给lua脚本的的键和参数:传递给lua脚本的键列表应该包括可能会读取或者写入的所有键。传入全部的键使得在使用各种分片或者集群技术时,其他软件可以在应用层检查所有的数据是不是都在同一个分片里面。另外集群版redis也会对将要访问的key进行检查,如果不在同一个服务器里面,那么redis将会返回一个错误。(决定使用集群版之前应该考虑业务拆分),参数列表无所谓。。

5.lua脚本跟单个redis命令和事务段一样都是原子的已经进行了数据写入的lua脚本将无法中断,只能使用SHUTDOWN NOSAVE杀死Redis服务器,所以lua脚本一定要测试好。

结尾


我擦就随便写了个主从和Lua,就这么多,哎,一把辛酸一把泪。下一章是最后一章了,看看怎么写吧。

因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
存储 移动开发 NoSQL
Redis - 原理篇-2
Redis - 原理篇-2
67 0
Redis - 原理篇-2
|
8月前
|
存储 缓存 NoSQL
一篇吃透redis
一篇吃透redis
35 0
|
3月前
|
NoSQL 网络安全 Redis
Redis进阶-Redis使用建议一二事
Redis进阶-Redis使用建议一二事
18 0
|
4月前
|
存储 NoSQL Linux
【Redis入门】 —— 关于Redis的一点儿知识
【Redis入门】 —— 关于Redis的一点儿知识
|
6月前
|
存储 NoSQL Linux
Redis - 原理篇-1
Redis - 原理篇-1
35 0
|
9月前
|
NoSQL 网络协议 Redis
Redis从入门到精通之Redis事件机制详解
Redis采用事件驱动机制来处理大量的网络IO,这是Redis实现高性能的关键之一。Redis并没有采用成熟的开源方案如libevent或libev,而是自己实现了一个非常简洁的事件驱动库ae_event。
253 1
Redis从入门到精通之Redis事件机制详解
|
11月前
|
存储 缓存 监控
一文带你吃透Redis
一文带你吃透Redis
181 0
|
11月前
|
存储 缓存 NoSQL
Redis入门及在项目中的使用
Redis入门及在项目中的使用
150 0
|
存储 JSON NoSQL
Redis 入门教程
Redis 入门教程
141 1
Redis 入门教程
|
监控 NoSQL 算法
从零开始学Redis之半步神游(上)
前言 文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820… 种一棵树最好的时间是十年前,其次是现在
153 0