Redis学习 --(一)基础操作

一. 概述

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

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

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

  1. 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  2. 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  3. 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
  • Redis 特点
  1. 支持数据持久化,可将内存中的数据保存在磁盘,重启时再次加载
  2. 支持 KV 类型数据,也支持其他丰富的数据结构存储
  3. 支持数据备份,即 master-slave 模式的数据备份
  • Redis 支持哪些数据结构
  1. STRING:字符串、整数或浮点数
  2. LIST:列表,可存储多个相同的字符串
  3. SET:集合,存储不同元素,无序排列
  4. HASH:散列表,存储键值对之间的映射,无序排列
  5. ZSET:有序集合,存储键值对,有序排列

二. Key的命名建议

redis单个key 存入512M大小

  1. key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
  2. key也不要太短,太短的话,key的可读性会降低;
  3. 在一个项目中,key最好使用统一的命名模式,例如user:123:password;
  4. key名称区分大小写

三. Redis数据类型

3.1 String

  • string是redis最基本的类型,一个key对应一个value。
  • string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
  • string类型是Redis最基本的数据类型,一个键最大能存储512MB。

3.1.1 语法

赋值语法:
SET KEY_NAME VALUE
Redis SET 命令用于设置给定 key 的值。如果 key 已经存储值, SET 就覆写旧值,且无视类型

SETNX key value //解决分布式锁 方案之一
只有在 key 不存在时设置 key 的值。Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值

MSET key value [key value …]
同时设置一个或多个 key-value 对

取值语法:
GET KEY_NAME
Redis GET命令用于获取指定 key 的值。如果 key 不存在,返回 nil 。如果key 储存的值不是字符串类型,返回一个错误。

GETRANGE key start end
用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)

扫描二维码关注公众号,回复: 10720107 查看本文章

GETBIT key offset
对 key 所储存的字符串值,获取指定偏移量上的位(bit)

MGET key1 [key2…]
获取所有(一个或多个)给定 key 的值

GETSET语法: GETSET KEY_NAME VALUE
Getset 命令用于设置指定 key 的值,并返回 key 的旧值,当 key 不存在时,返回 nil

STRLEN key
返回 key 所储存的字符串值的长度

删除语法:
DEL KEY_Name
删除指定的KEY,如果存在,返回值数字类型。

自增/自减:
INCR KEY_Name
Incr 命令将 key 中储存的数字值增1。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作
自增:INCRBY KEY_Name 增量值
Incrby 命令将 key 中储存的数字加上指定的增量值
自减:DECR KEY_NAMEDECYBY KEY_NAME 减值
decR 命令将 key 中储存的数字减1

字符串拼接:APPEND KEY_NAME VALUE
Append 命令用于为指定的 key 追加至未尾,如果不存在,为其赋值

3.1.2 应用场景:

1、String通常用于保存单个字符串或JSON字符串数据
2、因String是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储
3、计数器(常规key-value缓存应用。常规计数: 微博数, 粉丝数)
INCR等指令本身就具有原子操作的特性,所以我们完全可以利用redisINCRINCRBYDECRDECRBY等指令来实现原子计数的效果。假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum的值一定是5。
不少网站都利用redis的这个特性来实现业务上的统计计数需求。

3.2. 哈希(Hash)

  • Redis hash 是一个string类型的fieldvalue的映射表,hash特别适合用于存储对象。 Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)
  • 可以看成具有KEYVALUEMAP容器,该类型非常适合于存储值对象的信息, 如:uname,upass,age等。该类型的数据仅占用很少的磁盘空间(相比于JSON

3.2.1 语法

赋值语法:
HSET KEY FIELD VALUE //为指定的KEY,设定FILD/VALUE
HMSET KEY FIELD VALUE [FIELD1,VALUE1]…… 同时将多个 field-value (域-值)对设置到哈希表 key 中。

取值语法:
HGET KEY FIELD //获取存储在HASH中的值,根据FIELD得到VALUE
HMGET key field[field1] //获取key所有给定字段的值
HGETALL key //返回HASH表中所有的字段和值

HKEYS key //获取所有哈希表中的字段
HLEN key //获取哈希表中字段的数量

删除语法:
HDEL KEY field1[field2] //删除一个或多个HASH表字段

其它语法:
HSETNX key field value
只有在字段 field 不存在时,设置哈希表字段的值

HINCRBY key field increment
为哈希表 key 中的指定字段的整数值加上增量 increment 。

HINCRBYFLOAT key field increment
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。

HEXISTS key field //查看哈希表 key 中,指定的字段是否存在

3.2.2 应用场景

Hash的应用场景:(存储一个用户信息对象数据)

  1. 常用于存储一个对象
  2. 为什么不用string存储一个对象?
    hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转换成hashmap存放在redis中。
    用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
    • 第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
    • 第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。

总结:
Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口

四. 使用Redis的各种方式

在官方网站列一些Java客户端访问,有:Jedis/Redisson/Jredis/JDBC-Redis等,其中官方推荐使用JedisRedisson。常用Jedis

  1. 开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。 Java的安装配置可以参考我们的 Java开发环境配置 接下来让我们安装 Java redis 驱动
		<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.4.2</version>
        </dependency> 

  1. 测试是否连接成功
public class Redis {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        Jedis jedis = new Jedis(host);
        System.out.println(jedis.ping());
    }
}
  1. 开放端口(如下命令只针对Centos7以上)
    查看已经开放的端口:firewall-cmd --list-ports
    开启端口:
    firewall-cmd --zone=public --add-port=6379/tcp --permanent
    重启防火墙
    firewall-cmd --reload #重启

  2. Java操作Redis 设置密码
    在这里插入图片描述
    这个问题是由于Redis没有配置密码的原因导致的,只需要为redis设置密码即可
    config get requirepass: 这是查询redis是否配置密码,如果返回为空,则表明未配置密码
    在这里插入图片描述
    config set requirepass “guoweixin”这是将redis的密码设置为“guoweixin”
    在这里插入图片描述
    客户端登录:用redis-cli 密码登陆(redis-cli -a password

4.1 通过Jedis连接池

4.1.1 普通连接使用

public static void main(String[] args) {
        String host = "127.0.0.1";
        Jedis jedis = new Jedis(host);
        System.out.println(jedis.ping());
    }

4.1.2 通过连接池获得redis

public class JedisUtils {
    private static JedisPool jedisPool;
    private static String host = "localhost";

    static{
        //设置连接池的参数
        JedisPoolConfig config = new JedisPoolConfig();
        //设置空闲连接数
        config.setMaxTotal(5);
        //设置最大连接数
        config.setMaxIdle(100);
        //设置连接池
        //。。。还有很多设置
        jedisPool = new JedisPool(config, host);
    }

    public static Jedis getJedis(){
        Jedis jedis = jedisPool.getResource();
        return jedis;
    }
    public static void close(Jedis jedis){
        jedis.close();
    }
}

@Test
    public void test_02(){
        Jedis jedis = JedisUtils.getJedis();
        System.out.println(jedis.ping());
    }

4.1.3 redis 存储hash值

	/**
     * jedis 存储hash 值
     */
    @Test
    public void test_03(){
        Jedis jedis = JedisUtils.getJedis();
       	//现在redis中查询
        if(jedis.exists("user")){
        	//返回值类型为map
            Map<String, String> map = jedis.hgetAll("user");
			System.out.println("redis查询到的对象是:"+map);
        }else {
        	//redis 没有的话在数据库中查找(假装)
            User user = new User();
            user.setId("1");
            user.setAge(20);
            user.setUname("zhangsan");
            jedis.hset("user", "id", "1");
            jedis.hset("user", "age", "20");
            jedis.hset("user", "uname", "zhangsan");
			System.out.println("mysql查询到的对象是:"+user);

        }
        //关闭连接
        jedis.close();
    }

4.2 RedisTemplate (springboot整合redis)

Spring data 提供了RedisTemplate模版
它封装了redis连接池管理的逻辑,业务代码无须关心获取,释放连接逻辑;spring redis同时支持了Jedis,Jredis,rjc 客户端操作;
在RedisTemplate中提供了几个常用的接口方法的使用,分别是

在这里插入图片描述
在这里插入图片描述

4.2.1 环境配置

  1. jar:Redis和SpringBoot整合
    首先添加POM文件
<!-- redis 和SpringBoot整合 -->
		<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId>
       </dependency>

由于我们使用SpringBoot 进行测试所以还需要添加一个POM文件

	<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
  1. 编写SpringBoot 运行主类
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
  1. 编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes={App.class})// 指定启动类
public class Redis {
    @Autowired
    RedisTemplate redisTemplate;
    
    @Test
    public void test_04(){
        ValueOperations<String, String> result = redisTemplate.opsForValue();
        if(redisTemplate.hasKey("abc")){
            String value = result.get("abc");
            System.out.println("--redis中--"+value);
        }else{
            //到数据库中查
            String b = "hahahaah";
            result.set("abc", b);
            System.out.println("--mysql中--"+ b);
        }
    }
}

在这里插入图片描述

4.2.2 redis-springboot-RedisTemplate JDK序列化方式更改

默认的是JdkSerializationRedisSerializer 我们需要修改一下

if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }
  1. 加入maven依赖
	<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.29</version>
    </dependency>

	<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
    </dependency>
  1. 编写RedisConfig 配置类
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //1, 解决key的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);

        //2.解决value 的序列化方式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        ObjectMapper objectMapper = new ObjectMapper();
        //针对时间类型的自定义序列化方式
//        SimpleModule simpleModule = new SimpleModule();
//        simpleModule.addSerializer(DateTime.class, new JodaDateTimeJsonSerializer());
//        simpleModule.addDeserializer(DateTime.class, new JodaDateTimeJsonDeserializer());
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        objectMapper.registerModule(simpleModule);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        return redisTemplate;
    }
}

我们可以与上面的结果进行对比发现序列化方式的结果没有乱码了。
在这里插入图片描述

4.2.3 RedisTemplate 操作hash数据

redisTemplate 与用客户端插入hash数据的对比
阿斯顿

@Test
    public void test_05(){
        User user = new User();
        user.setId("1");
        user.setAge(12);
        user.setUname("张三");
        redisTemplate.opsForHash().put("user", user.getId(), user);
        System.out.println("存储完毕");
    }

在这里插入图片描述
在存储对象的时候记得要把要存储的对象实现序列化接口public class User implements Serializable 否则的话会报下面的异常。

org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.redis.User]

4.2.4 redis-springboot-RedisTemplate hash序列化方式更改

从源码中我们可以看到默认的hash序列化方式是jdk提供的,这里我们要更改为String

if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

我们在redisConfig 中设置hash的key value 的序列化方式

 //设置hash存储的Ksy value 的序列化方式
       redisTemplate.setHashKeySerializer(stringRedisSerializer);
       redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

更改序列化方式前后结果对比:
在这里插入图片描述
在这里插入图片描述

五. redis 多数据库

Redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0
redis配置文件中下面的参数来控制数据库总数:
database 16 //(从0开始 1 2 3 …15)
select 数据库//数据库的切换

移动数据(将当前key移动另个库)
move key名称 数据库
数据库清空:
flushdb //清除当前数据库的所有key
flushall //清除整个Redis的数据库所有key

发布了118 篇原创文章 · 获赞 5 · 访问量 8732

猜你喜欢

转载自blog.csdn.net/weixin_43672855/article/details/105055345