Redis

0、Redis

Redis_第1张图片

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库

Redis 与其他 key - value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis的优势:

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

官网:https://redis.io/

中文官网:http://www.redis.cn/

1、Linux下的Redis的安装

1.1、下载

下载地址:https://redis.io/download

或者

wget http://download.redis.io/releases/redis-5.0.3.tar.gz

1.2、上传文件到服务器

如果是直接使用wget下载的就不用再上传

Redis_第2张图片

1.3、解压文件

tar -zxvf redis-5.0.3.tar.gz

解压后目录

Redis_第3张图片

1.4、安装依赖

redis 是基于C++开发的,所有要安装C++的环境

yum -y install gcc-c++ autoconf automake

1.5、预编译

cd redis-5.0.3 # 进入redis目录
make # 预编译

Redis_第4张图片

1.6、安装

创建一个安装目录,不然会安装到一个默认路径,后期路径不好找

mkdir -p /usr/local/redis
  • -p:级联创建,上级目录不存在也一同创建

安装到指定路径

make PREFIX=/usr/local/redis/ install
  • PREFIX:指定要安装的路径

Redis_第5张图片

  • Redis-cli:客户端
  • Redis-server:服务器端

1.7、前台启动

Redis_第6张图片

默认就是前台启动,但是前台启动有一个不好的地方,就是Redis会一致占用前台输入,无法进行别的从操作。这是为了方便就需要把Redis设置为后台启动,让它在后台运行,不耽误前台进行别的操作。

1.8、后台启动

1、ctrl+c,结束前台程序运行

2、复制解压的redis目录下的redis.conf文件到安装的路径

cp /root/redis-5.0.3/redis.conf /usr/local/redis/bin

Redis_第7张图片

3、修改bin目录下的redis.conf文件

vim redis.conf

修改daemonize ondaemonize yes

4、再次运行查看

再次启动程序,需要戴胜redis.conf文件,否则还是前台运行

./redis-server redis.conf	# 启动程序
ps -ef |grep redis # 查看和redis相关的进程

Redis_第8张图片

5、后台进程关闭

前台启动的话只需要ctrl+c就可以关闭,但是后台启动相比就麻烦了点,需要直接关闭进程

kill -9 5977

5977:进程号

Redis_第9张图片

2、windows客户端方法Redis

2.1、安装客户端软件

下载地址:https://lanzous.com/ickc0yd

常规安装,选择安装路径,下一步就可以了

2.2、配置服务器端Redis程序

默认情况下,Redis是不允许别的客户端连接访问的,只能是本机(127.0.0.1)访问,需要设置一下,否则windows客户端连接不上redis

vim redis.conf

1、注释掉bind 127.0.0.1

# bind 127.0.0.1

如果注释掉bind就表示可以是任何ip访问,如果是想要指定的IP访问,直接在127.0.0.1 后添加指定IP就可以

2、关闭保护模式,设置protected-mode为no,否则依然无法连接

protected-mode no

3、设置访问认证(密码)

默认状态下是没有开启认证的,需要手动取消#注释(该命令大概在500行左右)

requirepass root

4、保存退出,结束进程,重新启动redis

./redis-server redis.conf

2.3、windows客户端连接redis

Redis_第10张图片

Redis_第11张图片

3、Redis-cli连接操作Redis

Redis-cli就是Redis自带的一个客户端程序,可以用来操作Redis,在安装的路径里

3.1、redis-clid连接redis

./redis-cli -p 6379 -a root
  • 6379:redis的默认端口
  • root:认证密码

Redis_第12张图片

3.2、操作redis

3.2.1、String类型

set name zhangsan	# 添加一个名为name的String类型数据,值为zhangsan
get name			# 获取名为name的String类型数据
mset age 18 addr henan # 批量添加String类型数据
mget age addr		# 批量获取String类型数据,获取名为age和addr的数据

Redis_第13张图片

3.2.2、Hash类型

hset userInfo name list # 添加一个名为UserInfo的hash类型,其中一个名是name,值为list
hget userInfo name # 获取名为name的数据
hmset userInfo age 18 addr henan # 添加多条数据
hmget userInfo age addr # 获取多个数据
hgetall userInfo # 获取全部数据
hdel userInfo addr # 删除指定数据

Redis_第14张图片

3.2.3、List类型

这里的List类型比较特殊,分为左右两种,一种是从左边添加,一种是从右边添加。左边添加就类似于一种压栈效果,先进的排到后面,右边添加就是常规的追加。

lpush students zhangsan lisi # 左边添加
lrange students 0 2	# 遍历list,0起始索引,2结束索引

Redis_第15张图片

可以看到zhangsan排在了后面,而lisi排在前面,类似于一个压栈的效果

rpush students wangwu zhaoliu # 右边添加
lrange students 0 8	# 遍历,结束索引可以是一个比list长度大的数字

Redis_第16张图片

可以看到,右边添加wangwu在zhaoliu前边,就是一个追加的效果

llen students #查看长度
lrem students 1 lisi # 删除指定数量的指定元素
  • lrem students 1 lisi:删除students中的1个lisi

Redis_第17张图片

lpop students # 移除左边开始的第一个元素
rpop students # 移除右边开始的第一个元素

Redis_第18张图片

3.2.4、Set类型

这里的set类型和java的一样,无序,不重复

sadd letters aaa bbb ccc ddd # 添加set类型数据
smembers letters	# 查看set类型数据
scard letters		# 获取set类型数据条数
srem letters aaa ccc	# 删除set类型里的指定元素

Redis_第19张图片

3.2.5、Sorted Set类型

一个有序的Set类型

zadd score 5 zhangsan 6 lisi 3 wangwu 9 zhaoliuclear # 添加参数,数值代表等级,数值越低,排名越靠前
zrange score  0 8	# 遍历数据
zcard score		# 数据元素条数
zrem score lisi # 删除指定元素

Redis_第20张图片

3.2.6、层级关系|目录结构存储数据

mset user:01 ahh # 以层级关系存储数据,:分割层级
mget user:01 # 以层级关系查找数据,:分割层级

Redis_第21张图片

3.2.7、设置Key的失效时间

有几种不同的命令可以设置失效时间

方式一:

set code test ex 10 # 10秒后失效

方式二:

set code test # 添加数据
expire code 10 # 设置10秒后失效
  • expire:秒
  • pexpire:毫秒
  • expireat:秒的时间戳
  • pexpireat:毫秒的时间戳

方式三:

set code test nx ex 10 # code存在时才设置失效时间
  • code:key
  • test:value
  • nx:不存在时设置时间,还有一个可选值xx,存在时设置失效时间
  • ex:秒[px:毫秒]

3.2.8、删除

set code test # 添加数据
del code # 删除数据

4、Redis持久化储存

Redis是内存中的数据结构储存系统,不是直接存储在磁盘上的,所有就有可能会在机器宕机的时候无法有效的储存数据,所以就需要持久化储存。

Redis有三种方式可以用于持久化储存数据

4.1、bgsave

使用bgsave手动持久化储存数据

set test test
bgsave

这种方式后进行持久化操作很方便,但是很麻烦,需要重复多个的执行bgsave命令

4.2、save配置

Redis还自带了一种save的持久化储存方式,这是一种自动化的储存方式(默认开启)。可以在redis.conf文件中看到(大概220行左右),这种方式会在指定时间内,进行自动保存

默认配置如图,表示在

900秒内,有一个key被改动,则在900秒后被自动持久化保存

300秒内,有10个key被改动,则在300秒后被自动持久化保存

60秒内,有10000个key被改动,则在60秒后被自动持久化保存

但是这种情况也不是很完善,比如在60秒内有10000的数据被改动,但是在58秒的时候机器断电了,不到低60秒,它也不会自动持久化储存。

4.3、appendonly

默认情况就该配置是关闭的,需要手动开启,设置on为yes(redis.conf文件的大概700行左右),该配置开启后会自动关闭上面的save持久化方式。该方式的特点就是会保存写过的所有的命令,会把所有的命令保存到一个appendonly.aof文件中,每次启动redis都会先执行该文件,以达到持久化储存的效果。

但是这种方式也有弊端,那就是会保存很多没用的代码,后续文件会越来越大,影响redis的运行效果。

5、Java操作Redis

5.1、创建SpringBoot项目

Redis_第22张图片

Redis_第23张图片

Redis_第24张图片

Redis_第25张图片

5.2、修改POM配置文件

1、修改Test依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
    <scope>testscope>
dependency>

2、修改Redis依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
    
    
    <exclusions>
        <exclusion>
            <groupId>io.lettucegroupId>
            <artifactId>lettuce-coreartifactId>
        exclusion>
    exclusions>
dependency>

3、添加Jedis依赖

<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
dependency>

完整文件


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.7.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>cn.yanghuisengroupId>
    <artifactId>redisdemoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>redisdemoname>
    <description>Demo project for Spring Bootdescription>

    <properties>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
            
            
            <exclusions>
                <exclusion>
                    <groupId>io.lettucegroupId>
                    <artifactId>lettuce-coreartifactId>
                exclusion>
            exclusions>
        dependency>
        
        <dependency>
            <groupId>redis.clientsgroupId>
            <artifactId>jedisartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

5.3、Jedis的的配置文件

application.yml

spring:
  redis:
    # Redis服务器地址
    host: 192.168.10.100
    # Redis服务器端口
    port: 6379
    # Redis服务器认证密码
    password: root
    # 要连接的数据库,默认就是0
    database: 0
    # 连接超时时间
    timeout: 10000ms
    jedis:
      pool:
        # 最大连接数,默认是8
        max-active: 1024
        # 最大连接组设时间,单位毫秒,默认-1ms
        max-wait: 10000ms
        # 最大空闲连接,默认8
        max-idle: 200
        # 最小连接,默认0
        min-idle: 5

5.4、创建连接池配置类

package cn.yanghuisen.redisdemo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author admin
 * @version 1.0
 * @date 2020/5/13 19:22
 * @Description Jedis配置类
 */
@Configuration  // 配置类
public class JedisConfig {
    // 服务器地址
    @Value("${spring.redis.host}")
    private String host;

    // 服务器端口
    @Value("${spring.redis.port}")
    private Integer port;

    // 认证密码
    @Value("${spring.redis.password}")
    private String password;

    // 连接超时时间
    @Value("${spring.redis.timeout}")
    private String timeout;

    // 最大链接数
    @Value("${spring.redis.jedis.pool.max-active}")
    private Integer maxActive;

    // 最大链接数
    @Value("${spring.redis.jedis.pool.max-wait}")
    private String maxWait;

    // 最大空闲连接
    @Value("${spring.redis.jedis.pool.max-idle}")
    private Integer maxIdle;

    // 最小空闲连接
    @Value("${spring.redis.jedis.pool.min-idle}")
    private Integer minIdle;

    @Bean
    public JedisPool redisPoolFactory(){
        // 连接池配置类
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 设置最大连接阻塞时间
        jedisPoolConfig.setMaxWaitMillis(Long.parseLong(maxWait.substring(0,maxWait.length()-2)));
        // 最大链接数
        jedisPoolConfig.setMaxTotal(maxActive);
        // 最大空闲连接
        jedisPoolConfig.setMaxIdle(maxIdle);
        // 最小空闲连接
        jedisPoolConfig.setMinIdle(minIdle);
        // 连接池,连接池配置类,服务器地址,端口,连接超时时间。认证密码
        return new JedisPool(jedisPoolConfig,host,port,Integer.parseInt(timeout.substring(0,timeout.length()-2)),password);
    }
}

5.5、注入连接池,切入点拦截

@Resource
private JedisPool jedisPool;
private Jedis jedis = null;

@BeforeEach
public void initConn(){
    jedis = jedisPool.getResource();
}

@AfterEach
public void closeCinn(){
    if (null!=jedis){
        jedis.close();
    }
}

5.5、操作String

/**
     * 操作String
     */
@Test
void testString(){
    // 添加单条数据
    jedis.set("ahh","ahh");
    jedis.set("age","18");

    // 添加多条数据,参数:奇数-key,偶数-value
    jedis.mset("addr","henan","sex","男");

    // 获取一条数据
    System.out.println(jedis.get("ahh"));

    // 获取多条数据
    jedis.mget("age","addr","sex").forEach(System.out::println);

    // 删除
    jedis.del("sex");
}

5.6、操作Hash

/**
     * 操作Hash
     */
@Test
public void testHash(){
    // 添加一条数据
    jedis.hset("userInfo","name","ahh");
    // 添加多条数据
    Map<String,String> map = new HashMap<>();
    map.put("age","20");
    map.put("sex","男");
    jedis.hmset("userInfo",map);

    // 获取一条数据
    System.out.println(jedis.hget("userInfo","name"));
    // 获取多条数据
    jedis.hmget("userInfo","age","sex").forEach(System.out::println);
    // 获取Hash类型多有的数据
    jedis.hgetAll("userInfo").forEach((k, v) -> {
        System.out.println(k+"----"+v);
    });

    // 删除
    jedis.hdel("userInfo","sex");
}

5.7、操作List

/**
     * 操作List
     */
@Test
void testList(){
    // 左添加
    jedis.lpush("aabc","a","b","c");
    // 右添加
    jedis.rpush("aabc","1","2","3");
    // 获取数据
    jedis.lrange("aabc",0,10).forEach(System.out::println);
    // 获取总条数
    System.out.println("总条数:"+jedis.llen("aabc"));
    // 删除list元素
    jedis.lrem("aabc",1,"c");
    // 左移除
    jedis.lpop("aabc");
    // 右移除
    jedis.rpop("aabc");
    // 删除
    jedis.del("aabc");
}

5.8、操作Set

/**
     * 操作Set
     */
@Test
void testSet(){
    // 添加数据
    jedis.sadd("letters","aa","bb","cc","dd");
    // 获取数据
    jedis.smembers("letters").forEach(System.out::println);
    // 获取条数
    System.out.println("总条数:"+jedis.scard("letters"));
    // 删除
    jedis.srem("letters","dd");
}

5.9、操作Sorted Set

/**
     * 操作SortedSet
     */
@Test
void testSortedSet(){
    // 添加数据
    Map<String,Double> map = new HashMap<>();
    map.put("张三",5d);
    map.put("李四",2d);
    map.put("王五",8d);
    map.put("赵六",6d);
    jedis.zadd("students",map);

    // 获取数据
    jedis.zrange("students",0,8).forEach(System.out::println);
    // 总条数
    System.out.println("总条数:"+jedis.zcard("students"));
    // 删除
    jedis.zrem("students","赵六");
}

5.10、层级关系|目录形式存储数据

/**
     * 层级关系|目录形式存储数据
     */
@Test
public void testDir(){
    jedis.set("users:user:ahh","ahh");
    jedis.set("users:user:zhangsan","zhangsan");
    System.out.println(jedis.get("users:user:ahh"));
    System.out.println(jedis.get("users:user:zhangsan"));
}

5.11、设置失效时间

/**
     * 设置失效时间
     */
@Test
public void testExpire(){
    // 方法1
    jedis.set("code","test");
    jedis.expire("code",10);    // 10秒
    jedis.pexpire("code",10000L);    // 10000毫秒
    System.out.println(jedis.ttl("code"));  // 获取剩余秒数据
    // 方法2
    jedis.setex("code",10,"test");  // 10秒
    jedis.psetex("code",10000L,"test");  // 10000毫秒
    System.out.println(jedis.pttl("code")); // 获取剩余毫秒
    // 方法3
    SetParams setParams = new SetParams();
    // 不存在时
    //        setParams.nx();
    // 存在时
    setParams.xx();
    // 设置失效时间-秒
    //        setParams.ex(10);
    // 设置失效时间-毫秒
    setParams.px(10000L);
    jedis.set("code","test",setParams);
}

5.12、获取所有的key

/**
 * 获取所有的key
 */
@Test
void testKeyAll(){
    // 获取key的数量
    System.out.println(jedis.dbSize());
    // 获取所有的key
    jedis.keys("*").forEach(System.out::println);
}

5.13、删除

/**
* 删除
*/
@Test
void testDel(){
    jedis.del("userInfo");
}

5.14、事务

/**
     * 事务
     */
@Test
void testMulti(){
    Transaction tx = jedis.multi();
    // 开启事务
    tx.set("tel","1010");
    // 提交事务
    tx.exec();
    // 事务回滚
    // tx.discard();
}

5.15、byte序列化

序列化和反序列化工具

package cn.yanghuisen.redisdemo.utils;

import java.io.*;

/**
 * @author admin
 * @version 1.0
 * @date 2020/5/13 20:40
 * @Description 序列化和反序列化工具
 */
public class SerializeUtil {
    // 序列化
    public static byte[] serialize(Object obj){
        // 对象输出流
        ObjectOutputStream oos = null;
        // 字节数组输出流
        ByteArrayOutputStream baos = null;
        try {
            // 序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 反序列化
    public static Object unserialize(byte[] bytes){
        if (null==bytes){
            return null;
        }

        ByteArrayInputStream bais = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

用户类

package cn.yanghuisen.redisdemo.pojo;

import java.io.Serializable;

/**
 * @author admin
 * @version 1.0
 * @date 2020/5/13 20:51
 * @Description TODO
 */
public class User implements Serializable {
    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

数据储存和获取

/**
     * byte序列化
     */
@Test
void testByte(){
    User user = new User();
    user.setId(1);
    user.setName("ahh");
    // 序列化
    byte[] userkey = SerializeUtil.serialize("user:"+user.getId());
    byte[] uservalue = SerializeUtil.serialize(user);
    jedis.set(userkey,uservalue);

    // 获取数据
    byte[] values = jedis.get(userkey);

    // 反序列化
    System.out.println(SerializeUtil.unserialize(values));

}

6、Redis搭建主从复用

单机版的Redis可能会出现一个问题就是,一旦服务器宕机就无法使用,所以Redis有一个主从复用的概念,就是说有一个主服务器和从服务器,主服务器提供写入和读取,从服务器提供取的功能,一旦其中一个从服务器出现了问题,还有其它的从服务区提供使用,一旦主服务器出现了问题,可以把其中的一个从服务器变为主服务器。

6.1、读写分离

把Redis分为主从关系,一个主服务器,和两个从服务器。

主服务器提供写入和获取,从服务器只提供获取功能。

1、创建三个目录

  • data:数据文件
  • log:日志文件
  • conf:配置文件

Redis_第26张图片

2、复制redis.conf配置文件到创建的配置文件目录下

3、配置配置文件,作为公共配置文件

注释掉bind,运行外界连接

Redis_第27张图片

关闭保护模式,否则外界无法连接

注释公共配置端口,后期需要运行三个redis,每个的端口不一样,需要针对每个redis进行私有的配置

Redis_第28张图片

修改为后台启动

Redis_第29张图片

注释进程编号,三个redis的进程编号都是不一样的,需要针对的进行私有配置

Redis_第30张图片

注释公共配置日志文件,三个redis有三个日志文件,私有配置

注释公共配置数据文件,修改数据文件路径为创建的数据文件路径

Redis_第31张图片

添加从数据库访问主服务器认证密码

Redis_第32张图片

设置访问认证,三个redis的认证密码都是一个,设置为公用的

Redis_第33张图片

注释公共配置追加文件,这个根据需求选择是否使用,这里关闭

Redis_第34张图片

设置从服务器只能读取,不能写入

保存退出

6.2、创建配置三个私有配置文件

1、创建三个配置文件

Redis_第35张图片

redis-xxxx:xxxx表示的每个的端口,为了方便区分

2、修改每个的配置文件

redis-6379.conf

# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6379.pid
# 进程端口号
port 6379
# 日志记录文件
logfile "/opt/redis/log/redis-6379.log"
# 数据记录文件
dbfilename dump-6379.rdb
# 追加文件名称
appendfilename "appendonly-6379.aof"

redis-6380.conf

# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6380.pid
# 进程端口号
port 6380
# 日志记录文件
logfile "/opt/redis/log/redis-6380.log"
# 数据记录文件
dbfilename dump-6380.rdb
# 追加文件名称
appendfilename "appendonly-6380.aof"
# 设置主服务器的IP地址
slaveof 192.168.10.100 6379

redis-6381.conf

# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6381.pid
# 进程端口号
port 6381
# 日志记录文件
logfile "/opt/redis/log/redis-6381.log"
# 数据记录文件
dbfilename dump-6381.rdb
# 追加文件名称
appendfilename "appendonly-6381.aof"
# 设置主服务器的IP地址
slaveof 192.168.10.100 6379

6.3、运行三个redis进程,并测试主从

1、运行三个redis进程

Redis_第36张图片

2、查看每个服务器的主从状态

主服务器

Redis_第37张图片

从服务器1

Redis_第38张图片

从服务器2

Redis_第39张图片

3、测试主服务下添加数据,在从服务器下查询是否正常

Redis_第40张图片

4、从服务器不能写入测试

Redis_第41张图片

6.4、主备切换

上面已经进行了读写分离,完成了主从服务器,但是这时还有一个问题,如果主服务器出现了问题,那么就不能再进行写入操作,只能使用从服务器获取主服务宕机之前的数据,为了解决这一问题,就引入了一个叫做哨兵的方案,通过哨兵,可以检测到主服务是否宕机,如果主服务器宕机了,就会在满足一定的要求后选举一个从服务器变为主服务,如果之前宕机的主服务恢复了,则被宕机的服务变为从服务。以此来解决,主服务宕机后,无法在提供写入操作的问题。

6.4.1、复制哨兵配置文件到创建的配置文件目录下

cp /root/redis-5.0.3/sentinel.conf /opt/redis/conf/

Redis_第42张图片

6.4.2、修改哨兵配置文件

注释哨兵监听进程端口号

指示哨兵监听一个主服务器,主服务器地址为192.168.10.100,端口为6379,2表示判断失败的要求,配置三个哨兵,一半(3个的一半为1.5,取整为2)以上都监听主服务状态为不达标就更换主服务器

Redis_第43张图片

设置密码

设置哨兵认为服务器断线所需的毫秒数。

默认是30000毫秒,也就是30秒,为了测试改为10000毫秒

设置主服务和从服务器切换时间

默认是3分钟,如果三分钟内没有切换成功,则本次切换失败

关闭哨兵的保护模式

修改哨兵为后台启动

6.4.3、添加三个私有哨兵配置文件

Redis_第44张图片

私有哨兵配置1

# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26379
# 进程编号近路文件
pidfile /var/run/sentinel-26379.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26379.log"

私有哨兵配置2

# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26380
# 进程编号近路文件
pidfile /var/run/sentinel-26380.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26380.log"

私有哨兵配置2

# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26381
# 进程编号近路文件
pidfile /var/run/sentinel-26381.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26381.log

6.4.4、哨兵启动测试

1、启动三个哨兵

Redis_第45张图片

2、查看主服务器和从服务状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG5Gs2Gg-1589464617789)(http://images.yanghuisen.cn/FitC_MDwSArK5ra8zhbOFGQStflB)]

3、杀死主服务器,模拟主服务宕机

杀死主服务器后等待10秒,让哨兵选择新的主服务器,并重新启动被杀死的服务器

4、查看哨兵选重新选举后的服务器状态

Redis_第46张图片

7、SpringDataRedis

SpringBoot1.X版本默认使用的是jedis作为Redsi的Java客户端,而2.X版本使用的是lettuce作为Redis的Java客户端。

两者的区别为:

  • Jedis:在实现上直接连接的Redis-Server,在多个线程间共享一个Jedis实例。线程不安全。如果要在多线程的场景下使用Jedis,需要使用连接池,每个线程都使用自己的Jedis实例,当连接数量增多时,会小号大量的物理资源。
  • lettuce:基于Netty的连接,是一个可伸缩的线程安全的Redis客户端,支持同步、一步和响应式模式。多个线程可以共享一个连接实例,而不必安全多线程并发的问题。它基于NettyNIO框架构建,支持redis的高级功能,如Sentinel(哨兵)、集群、流水线、自动连接和Redis数据模型。

7.1、创建SpringBoot项目

Redis_第47张图片

7.2、添加依赖

<dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

        
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-pool2artifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

7.3、添加application.yml配置文件

spring:
  redis:
    # Redis服务器地址
    host: 192.168.10.100
    # Redis服务器端口
    port: 6379
    # Redis服务器认证密码
    password: root
    # Redis数据库
    database: 0
    # 连接超时时间
    timeout: 10000ms
    lettuce:
      pool:
        # 最大连接数,默认8
        max-active: 1024
        # 最大阻塞等待时间,单位毫秒,默认-1ms
        max-wait: 10000ms
        # 最大空闲连接,默认8
        max-idle: 200
        # 最小空闲连接,默认0
        min-idle: 5

7.4、测试环境是否搭建成功

@SpringBootTest
class SpringDataRedisDemoApplicationTests {

    @Resource
    private RedisTemplate redisTemplate;    // 默认模板
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Test
    void initConn() {
        ValueOperations<String,String> ops = stringRedisTemplate.opsForValue();
        ops.set("userName","ahh");
        ValueOperations<String,String> value = redisTemplate.opsForValue();
        value.set("name","zhangsan");
        System.out.println(ops.get("name"));
    }

}

7.5、序列化问题

上面的测试程序,虽然成功的把数据写入了,但是写入的是一个二进制字节码,需要处理一下。

RedisTemplate使用的是JdkSerializationRedisSerializer进行序列化,会序列化为二进制字节码储存。这时需要自定义模板解决。当自定义模板后,又想储存String字符串可以使用StringRedisTemplate。自定义的模板和StringRedisTemplate不冲突。

序列化的选择:

  • JdkSerializationRedisSerializer:该序列化为RedisTemplate的默认序列化工具,是JDK提供的,有点是反序列化时不需要提供类型信息(class),但是缺点就是序列化后的结果非常大,而且看不懂,时json格式的5倍左右,会消耗redis服务器的大量内存。
  • Jackson2JsonRedisSerializer:使用的时Jackson库将对象序列化为json字符串,优点是速度快,序列化后的字符串短小精悍,但是缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息,通过查看源代码,发现其只再反序列化过程中用到了类型信息。
  • GenericJackson2JsonRedisSerializer:通用型序列化,这种序列化方式不用自己手动指定对象的Class。

自定义模板

package cn.yanghuisen.springdataredisdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author admin
 * @version 1.0
 * @date 2020/5/14 20:52
 * @Description 自定义模板
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
        // 创建模板
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        // 设置String类型的Key的序列器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 设置String类型的value的序列器
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // 设置Hash类型的Key的序列器
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // 设置hash类型的value的序列器
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        // 设置连接方式
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        return redisTemplate;
    }
}

序列化测试

@Test
void testSerial(){
    User user = new User();
    user.setId(1);
    user.setName("ahh");
    ValueOperations ops = redisTemplate.opsForValue();
    ops.set("user",user);
    System.out.println(ops.get("user"));
}

Ok

7.6、操作String

@Test
void testString(){
    ValueOperations vos = redisTemplate.opsForValue();

    // 添加一条数据
    vos.set("userName","zhangsan");
    // 以层级关系|目录形式存储数据
    vos.set("user:01","ahh");
    // 添加多条数据
    Map<String,String> userMap = new HashMap<>();
    userMap.put("age","18");
    userMap.put("sex","男");
    vos.multiSet(userMap);
    // 获取一条数据
    System.out.println(vos.get("userName"));
    // 获取多条数据
    List<String> keys = Arrays.asList("userName","age","sex");
    vos.multiGet(keys).forEach(System.out::println);
}

7.7、操作Hash

@Test
void testHash(){
    HashOperations<String,String,String> hos = redisTemplate.opsForHash();
    // 添加一条数据
    hos.put("userInfo","name","zhangsan");
    // 添加多条数据
    Map<String,String> map = new HashMap<>();
    map.put("age","20");
    map.put("sex","男");
    hos.putAll("userInfo",map);
    // 获取一条数据
    System.out.println(hos.get("userInfo","name"));
    // 获取多条数据
    List<String> keys = Arrays.asList("age","sex");
    hos.multiGet("userInfo",keys).forEach(System.out::println);
    // 获取Hash类型的所有的数据
    hos.entries("userInfo").forEach((k,v)->{
        System.out.println(k+"---"+v);
    });
    // 删除数据
    hos.delete("userInfo","name");
}

7.8、操作List

@Test
void testList(){
    ListOperations los = redisTemplate.opsForList();
    // 左添加
    los.leftPush("students","张三");
    los.leftPush("students","李四");
    // 指定位置前面左添加
    los.leftPush("students","李四","王五");
    // 右添加
    los.rightPush("students","赵六");
    // 获取
    los.range("students",1,10).forEach(System.out::println);
    // 根据下标获取
    System.out.println(los.index("students",1));
    // 获取总条数
    System.out.println(los.size("students"));
    // 删除单条数据
    los.remove("students",1,"张三");
}

7.9、操作oSet

@Test
void testSer(){
    SetOperations sos = redisTemplate.opsForSet();
    // 添加数据
    sos.add("letters",new String[]{"aaa","bbb","ccc","ddd"});
    sos.members("letters").forEach(System.out::println);
    sos.remove("letters","aaa","bbb");
}

7.10、操作SortedSet

@Test
void testSoredSet(){
    ZSetOperations zsos = redisTemplate.opsForZSet();
    // 添加数据
    zsos.add("score","zhangsan",5D);
    zsos.add("score","lisi",2D);
    zsos.add("score","wangwu",9D);
    zsos.add("score","zhaoliu",3D);
    // 获取
    zsos.range("score",0,5).forEach(System.out::println);
    // 获取总条数
    System.out.println(zsos.size("score"));
    // 删除
    zsos.remove("score","wangwu","zhangsan");
}

7.11、获取所有的Key

@Test
void testAllKeys(){
    redisTemplate.keys("*").forEach(System.out::println);
}

7.12、删除

@Test
void testDelete(){
    // 删除
    redisTemplate.delete("score");
}

7.13、设置Key的失效时间

@Test
void testEx(){
    ValueOperations vos = redisTemplate.opsForValue();
    // 添加一条数据,并设置失效时间
    vos.set("code","abcd",180, TimeUnit.SECONDS);
    // 给已经存在的key设置失效时间
    redisTemplate.expire("code",180,TimeUnit.SECONDS);
    // 获取指定key的失效时间
    System.out.println(redisTemplate.getExpire("code"));
}

7.14、整合使用哨兵机制

配置文件

spring:
  redis:
    # Redis服务器地址
    host: 192.168.10.100
    # Redis服务器端口
    port: 6380
    # Redis服务器认证密码
    password: root
    # Redis数据库
    database: 0
    # 连接超时时间
    timeout: 10000ms
    lettuce:
      pool:
        # 最大连接数,默认8
        max-active: 1024
        # 最大阻塞等待时间,单位毫秒,默认-1ms
        max-wait: 10000ms
        # 最大空闲连接,默认8
        max-idle: 200
        # 最小空闲连接,默认0
        min-idle: 5
    # 哨兵模式
    sentinel:
      # 主节点
      master: mymaster
      nodes: 192.168.10.100:26379,192.168.10.100:26380,192.168.10.100:26381

配置类

@Bean
RedisSentinelConfiguration redisSentinelConfiguration(){
    RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration().
        // 主节点名称
        master("mymaster")
        // 服务器地址
        .sentinel("192.168.10.100",26379)
        .sentinel("192.168.10.100",26380)
        .sentinel("192.168.10.100",26381);
    // 设置密码
    sentinelConfiguration.setPassword("root");
    return sentinelConfiguration;
}

你可能感兴趣的:(JAVA,Linux)