Redis快速上手并应用,看这篇就够了

1.简介

Redis是一个开源的,基于内存且支持持久化,高性能且基于key-value存储的Nosql数据库,用作数据库、缓存、消息代理和流媒体引擎。

应用场景:做为k-v数据库,其存储价值不如同类MongoDB。做为消息队列,不如Kafka,本职还是做为高速缓存,其“缓存”的性质远大于其“数据存储“的性质。

默认端口:6379

2.数据类型

Redis是基于k-v存储的,它的key类型只能是String类型,包括空字符串也是一个合格的key,而Value值类型则可以是多种多样了。

Key取值原则: 键值不需要太长,太长会消耗内存,且在数据中查找这类键值的计算成本较高;键值不宜过短,过短则可读性较差。

官方最新公布支持的值类型如下,除了我们熟知的五种基本数据类型外,还有一堆陌生的数据类型,大家感兴趣的可以通过官网了解,本文我们重点介绍常用的五种数据结构,StringListHashSetZset (即Sorted sets)。各个数据结构的具体说明看 “CRUD操作“模块。
Redis快速上手并应用,看这篇就够了_第1张图片

3.集群架构

Redis集群搭建有三种方式,感觉和MongoDB类似。

1,主从模式(master/slave)
一个master可以有多个slaves,slaves同步master节点的数据。默认配置下,master节点可以进行读和写,slave节点只能进行读操作,写操作被禁止。

缺点:master节点挂了以后,redis就不能对外提供写服务了,因为剩下的slave不能成为master。

2,sentinel哨兵模式
sentinel的中文含义是哨兵、守卫,是一个独立的进程。也就是说既然主从模式中,当master节点挂了以后,slave节点不能主动选举一个master节点出来,那么我就安排一个或多个sentinel来做这件事,当sentinel发现master节点挂了以后,sentinel就会从slave中重新选举一个master。

sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。

3,cluster模式(分片+副本模式)
cluster模式的出现是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器实现的redis分布式存储,也就是说每台redis节点上存储不同的内容。

每台节点(master)又可以引入多个slave,保证集群的高可用性,所以如果配置两个副本三个分片的话,就需要六个Redis实例。
因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量扩增时,可以很方便的新增机器进行扩容。
这种模式适合数据量巨大的缓存要求,当数据量不是很大使用sentinel即可。

Redis-Cluster采用无中心结构,客户端与redis节点直连,不需要中间代理层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

Redis集群有以下几个重要的特征:
(1)、Redis集群的分片特征在于将空间拆分为16384个槽位solt,某一个节点负责其中一些槽位,key对16384取余分配到不同的solt。
(2)、Redis集群提供一定程度的可用性,可以在某个节点宕机(其下没有slave,或者所有slave全部宕机)或者不可达的情况继续处理命令。
(3)、Redis集群不存在中心节点或代理节点,集群的其中一个最重要的设计目标是达到线性可扩展性。

4.Redis客户端shell命令

4.1.脚本命令

先介绍两个脚本命令:redis-serverredis-cli

1,redis-server命令用于启动redis服务

#启动redis
redis-server redis.conf
#查看版本
redis-server -v

2,redis-cli 命令用于连接redis服务端,进入命令行模式

#连接 (-p指定端口,-a指定密码,-c 集群模式)
redis-cli -h 172.28.226.5 -p 6379 -a $password -c

#注意:在Redis6.0之前的版本中,如上面指定的是默认用户default的密码,redis以前的版本也只支持单用户访问,也就是没有用户名这个概念。

4.2.命令行基本命令

redis提供了非常丰富的命令,下面仅介绍常用的命令,其他命令大家可以查看 https://redis.io/commands/ ,搜索各个命令更详细的用法。此外,也可以通过" help command " 查看命令的用法。
Redis快速上手并应用,看这篇就够了_第2张图片

1,运维命令

#测试连接是否可用,返回pang则表示连接可用
redis> PING
"PONG"

#查看redis统计信息
redis> info

#列出所有连接
redis> client list

#杀死某个连接
redis> client kill 127.0.0.1:43501

2,基本操作命令

dbsize        #显示数据库中所有key的数量
keys *        #显示所有的key
exists mykey  #判断该键是否存在,存在返回1,不存在返回0
flushdb       #清空数据库
del mykey     #删除该键值
type key      #查看key的值类型
help command  #帮助命令,查看命令的用法,如help get

4.3.CRUD操作

  1. Sting

    字符串类型,最简单最常用的值类型。在命令行中,key和value都可以不带引号。

    #设值,取值,删值基本操作,name就是我们设置的key值,注意以下所有命令command后紧跟的字符串都是key值。
    set name shengr
    get name
    del name
    
    #对已经存在的key设置过期时间,单位秒
    expire name 60
    
  2. List

    列表类型,类比java中数组,ArrayList。

    列表中的元素是字符串类型,元素的顺序和插入的顺序一致。列表的头尾增删速度快,中间增删速度慢,正常使用过程中增删元素是常态,列表中元素可以重复出现;列表的索引,从左至右,从0开始;从右至左,从-1开始。

    # lpush 元素依次压入列表头部,一次可以压入一个或多个元素
    lpush words a b c d  	
    # rpush 元素依次插入到列表末位
    rpush words e f g
    # lrange 获取下标从start到end的元素,下标从0开始并且包含end处的值。另外,下标可以是负数,表示倒数第几个值,-1即表示最后一位。
    lrange words 0 -1 
    # lpop 从列表首位移除一个值
    lpop words 
    # rpop 从列表末尾移除一个值
    rpop words 
    
    # 获取List长度
    llen words
    # 通过下标获取值
    lindex words 1
    # 通过下标设置
    lset words 0 a
    
  3. Hash

    哈希类型,类比java中的HashMap,所以一个key对应的是一个map类型的值(一组k-v对)。

    Hash是由field和关联的value组成的map键值对,而field和value都是字符串类型。

    # hset 依次设置map中键值对,一次可以设置一个或多个
    hset user name shengr age 12 city sz
    # hget 获取map中某个field上的值
    hget user name
    # hgetAll 获取map中的所有field值
    hgetall user
    #hdel 删除map中的一个或多个指定fileld,不存在的字段忽略
    hdel user age city
    
    # hexists 判断字段是否存在,存在返回1,不存在返回0
    hexists user age
    # hkeys 获取map上的所有field:
    hkeys user
    # hvals 获取map上的所有value:
    hvals user
    # hlen 获取map中field个数:
    hlen user
    
  4. Set

    集合类型,类比java中的HashSet,成员唯一,无序,元素是字符串类型。

    # sadd 往集合中加入元素,重复的元素无法插入
    sadd chars a b b c 
    # smembers 取出所有元素
    smembers chars 
    # srem 从集合中删除元素
    srem chars a 
    # scard 获取集合中元素个数
    scard chars
    
  5. Zset

    有序的集合类型,类比java中的TreeSet,成员唯一,有序,元素是字符串类型,每一个元素都关联着一个浮点数分值Score,并按照分值从小到大的顺序排列集合中的元素(分值可以相同,分值也可以为负数)。

    # zadd 往集合中加入带分值的元素
    zadd letters 1 a 3.1 b 4 c
    # zrange 根据下标获取集合中的元素,正序
    zrange letters 0 -1
    # zrem 从集合中删除元素
    zrem letters a
    # zcard 返回元素个数
    zcard letters
    # zscore 返回某个元素的分值
    zscore letters b
    # zincrby 给某个元素的分值加上一个值,减去一个值使用负数即可
    zincrby letters 2 b
    

5.Redis之JAVA 客户端

5.1.三种常用Java客户端

Jedis

老牌redis客户端,简单易用,提供了比较全面的Redis命令支持,也是我们下面重点介绍的。Jedis中的方法调用的是底层的Redis API,也即Jedis中的Java方法基本和Redis的API保持着一致,了解Redis的API,也就能更熟练的使用Jedis。官方地址:https://github.com/redis/jedis

注意点:jedis使用阻塞的 I/O,且其方法调用都是同步的,不支持异步。Jedis 客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

Lettuce

英文直译为“生菜”,用于线程安全同步、异步和响应使用的高级Java Redis客户端。支持集群、哨兵、流水线和编解码器。官网地址: https://lettuce.io/

Redisson

在Redis的基础上实现了Java驻内存数据网格功能(In-Memory Data Grid),是一个高级的分布式协调Redis客户端,提供了一系列的分布式的 Java 对象。Jedis、Lettuce客户端更侧重对 Reids 数据库的 CRUD(增删改查)操作,而 Redisson API 侧重于分布式开发。官网地址:https://redisson.org/
Redis快速上手并应用,看这篇就够了_第3张图片

5.2.Jedis客户端详解

Jedis客户端访问模式介绍

Jedis客户端同时支持单机模式、分片模式、集群模式的访问模式,通过构建 Jedis类对象 实现单机模式下的数据访问,通过构建 ShardedJedis类对象 实现分片模式的数据访问,通过构建 JedisCluster类对象 实现集群模式下的数据访问。其中ShardedJedis是Redis没有集群功能之前客户端实现的一个数据分布式方案,现在我们用JedisCluster替代即可。

5.2.1.单机模式数据访问

1.创建Jedis连接对象

通过ip和端口构建Jedis类对象,如果redis配置了密码,需要使用auth方法进行认证。

Jedis jedis = new Jedis("172.28.226.5",6379);
jedis.auth("$pwd");

2.String类型

//1,设置k-v值
jedis.set("name","shengr");
//2,获取k对应v值
String username=jedis.get("name");
System.out.println(username);
//3,设置值的同时指定过期时间,方法为setex,第二参数指定过期时间,单位秒
jedis.setex("flag",30,"1");
//4,删除key
jedis.del("flag");
//5,关闭连接
jedis.close();

3.List类型

//lpush 元素依次插入到列表首位
jedis.lpush("words","a","b");
//rpush 元素依次插入到列表末位
jedis.rpush("words","c","d","e");
//lrange 获取下标从start到end的元素,下标从0开始并且包含end处的值。另外,下标可以是负数,表示倒数第几个值,-1即表示最后一位。
List<String> words = jedis.lrange("words", 0, -1);
System.out.println(words);
//lpop 从列表首位弹出一个值
String word1 = jedis.lpop("words");
//rpop 从列表末位弹出一个值
String word2 = jedis.rpop("words");
System.out.println(word1+"--"+word2);

4.Hash类型

一个key对应一组 field-value 值

//设值
jedis.hset("user","name","shengr");
jedis.hset("user","age","12");
jedis.hset("user","city","sz");
//获取单个值
String name=jedis.hget("user","name");
System.out.println(name);
//遍历所有值
Map<String,String> user = jedis.hgetAll("user");
for (String key : user.keySet()) {
String value = user.get(key);
System.out.println(key+":"+value);
}
//删除key中的一个或多个指定fileld,不存在的字段忽略
jedis.hdel("user","city","sex");

5.Set类型

集合中元素唯一,添加重复的元素无效

//设值
jedis.sadd("distinctWords","a","b","a");
//获取所有值
Set<String> distinctWords = jedis.smembers("distinctWords");
System.out.println(distinctWords);

6.Zset类型

有序的Set类型,通过score指定排名分值,元素按照此分值从小到大排序

jedis.zadd("sortedset",3,"ding");
jedis.zadd("sortedset",5,"seng");
jedis.zadd("sortedset",4,"ming");
Set<String> sortedset = jedis.zrange("sortedset", 0, -1);
System.out.println(sortedset);

完整运行代码如下:

public class JedisTest {
   private static Jedis jedis;
   //通过静态代码块进行初始化值
   static {
       jedis = new Jedis("172.28.226.5",6379);
       jedis.auth("$pwd");
   }

   //1,String类型
   public static void op01(){
       //1,设置k-v值
       jedis.set("name","shengr");
       //2,获取k对应v值
       String username=jedis.get("name");
       System.out.println(username);
       //3,设置值的同时指定过期时间,方法为setex,第二参数指定过期时间,单位秒
       jedis.setex("flag",30,"1");
       //4,删除key
       jedis.del("flag");
       //5,关闭连接
       jedis.close();
   }
   //2,List类型
   public static void op02(){
       //lpush 元素依次插入到列表首位
       jedis.lpush("words","a","b");
       //rpush 元素依次插入到列表末位
       jedis.rpush("words","c","d","e");
       //lrange 获取下标从start到end的元素,下标从0开始并且包含end处的值。另外,下标可以是负数,表示倒数第几个值,-1即表示最后一位。
       List<String> words = jedis.lrange("words", 0, -1);
       System.out.println(words);
       //lpop 从列表首位弹出一个值
       String word1 = jedis.lpop("words");
       //rpop 从列表末位弹出一个值
       String word2 = jedis.rpop("words");
       System.out.println(word1+"--"+word2);
   }

   //3,Hash类型,同一个key对应一组k-v值
   public static void op03(){
       //设值
       jedis.hset("user","name","shengr");
       jedis.hset("user","age","12");
       jedis.hset("user","city","sz");
       //获取单个值
       String name=jedis.hget("user","name");
       System.out.println(name);
       //遍历所有值
       Map<String,String> user = jedis.hgetAll("user");
       for (String key : user.keySet()) {
           String value = user.get(key);
           System.out.println(key+":"+value);
       }
       //删除key中的一个或多个指定fileld,不存在的字段忽略
       jedis.hdel("user","city","sex");
   }

   //4-1,Set类型:集合中元素唯一
   public static void op04(){
       jedis.sadd("distinctWords","a","b","a");
       Set<String> distinctWords = jedis.smembers("distinctWords");
       System.out.println(distinctWords);

   }
   //4-2,有序Set类型: score指定排名分值,元素按照此分值从小到大排序
   public static void op05(){
       jedis.zadd("sortedset",3,"ding");
       jedis.zadd("sortedset",5,"seng");
       jedis.zadd("sortedset",4,"ming");
       Set<String> sortedset = jedis.zrange("sortedset", 0, -1);
       System.out.println(sortedset);
   }

   public static void main(String[] args) {
       op01();
       op02();
       op03();
       op04();
       op05();
   }
}

5.2.2.集群模式数据访问

集群模式访问,我们需要构建的是JedisCluster对象,而不是Jedis对象。虽然对象换了,但是访问以上各种数据类型的方法和Jedis对象完全一致。我们这里只需要关注如何创建一个JedisCluster的连接对象即可。

1.Jedis连接池

jedis的连接池叫JedisPool,在创建连接池后我们可以从连接池中获取连接,客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连接,而连接池的方式是可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。

连接池配置属性(通过JedisPoolConfig对象去设置属性):

  • maxTotal

    连接池容量,即可用的最大连接数。

  • maxIdle

    最大空闲连接数,默认值是8。

  • maxWaitMillis

    等待可用连接的最大时间,单位是毫秒,默认值为-1,表示永不超时。

  • testOnReturn

    布尔值,当该属性为true时,在调用borrowObject方法从连接池获取连接前,会调用validateObject方法进行校验。若校验失败,连接会从连接池中移除并销毁。同时会尝试重新借一个新的连接对象。

  • testOnBorrow

    布尔值,当该属性为true时,在调用returnObject方法归还连接到连接池时,会调用validateObject方法,校验当前连接,校验不通过,则销毁该连接。

2.创建JedisCluster连接对象

Redis快速上手并应用,看这篇就够了_第4张图片

通过构造方法创建,标准的包含全参的构造方法参数如下:

  • HostAndPort

    HostAndPort表示集群单个节点的ip和端口信息,集群的多个节点放到Set集合中即可。

  • connectionTimeout

    连接超时时间,单位毫秒

  • soTimeout

    读写数据的超时时间,单位毫秒

  • maxAttempts

    最大重连次数

  • password

    密码

  • jectPoolConfig

    封装了连接池属性的JedisPoolConfig对象

    完整运行代码如下:

    public class JedisClusterTest {
       public static JedisCluster getJedisCluster() {
    
           //1设置集群节点的ip和端口信息,集群有多个节点需要添加多个
           Set<HostAndPort> hostAndPorts = new HashSet<HostAndPort>();
           HostAndPort hostAndPort = new HostAndPort("172.28.226.5",6379);
           hostAndPorts.add(hostAndPort);
           //2设置密码
           String password = "$pwd";
           //3连接超时时间
           int connectTimeout = 5000;
           //4读写超时时间
           int soTimeout = 5000;
           //5最大重连次数
           int maxAttempts = 3;
           //6连接池参数
           JedisPoolConfig jedisPoolConfig = getJedisPoolConfig();
    
           JedisCluster jedisCluster = new JedisCluster(hostAndPorts, connectTimeout, soTimeout, maxAttempts, password, jedisPoolConfig);
           //注意:JedisCluster最好设置成单例,且一般不需要执行close操作,而是由连接池管理。
           return jedisCluster;
       }
       private static JedisPoolConfig getJedisPoolConfig() {
           //1,pool容量,即最大连接数
           int maxTotal = 1024;
           //2,pool最大空闲连接数,默认值是8
           int maxIdle = 200;
           //3,等待可用连接的最大时间,单位是毫秒,默认值为-1,表示永不超时。
           int maxWaitMillis = 10000;
           //4,在返回一个连接对象时,是否进行有效性检查
           boolean testOnReturn = true;
           //5,借用一个连接对象时,是否进行有效性检查
           boolean testOnBorrow = true;
    
           JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
           jedisPoolConfig.setMaxTotal(maxTotal);
           jedisPoolConfig.setMaxIdle(maxIdle);
           jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
           jedisPoolConfig.setTestOnReturn(testOnReturn);
           jedisPoolConfig.setTestOnBorrow(testOnBorrow);
    
           return jedisPoolConfig;
       }
       public static void main(String[] args) {
           JedisCluster jc=getJedisCluster();
           jc.set("myname","shengr");
           System.out.println(jc.get("myname"));
       }
    }
    

5.2.3.Redis常见异常

Exception in thread "main" redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 14315 172.24.226.7:6379
	at redis.clients.jedis.Protocol.processError(Protocol.java:115)
	at redis.clients.jedis.Protocol.process(Protocol.java:161)
	at redis.clients.jedis.Protocol.read(Protocol.java:215)
	at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
	at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
	at redis.clients.jedis.Jedis.set(Jedis.java:121)
	at redisClient.JedisTest.op01(JedisTest.java:24)
	at redisClient.JedisTest.main(JedisTest.java:52)

问题原因 : MOVED表示当前是Redis集群模式。您正在集群模式下使用Jedis类对象访问,而 Jedis类对象 单机模式下的数据访问方式。

解决方案: 将连接对象从 Jedis 换成 JedisCluster 就可以了。

Redis快速上手并应用,看这篇就够了_第5张图片

你可能感兴趣的:(一文入门到精通,一文快学,redis,数据库,java,nosql)