Redis入门概述

本文是Redis入门教程,介绍了NoSql的基本概念,对比了与关系型数据库的区别。接着阐述了学习Redis的原因,如解决高并发、海量数据等问题。详细讲解了Redis的特性,包括内存操作、线程安全、原子性操作、持久化等功能。重点讨论了Redis的五大数据类型:string、hash、list、set和sorted set,并举例说明了各自的应用场景。此外,还涵盖了Redis的安装、通用命令、多数据库性、持久化机制以及Jedis和Springboot集成Redis的基本使用。

Redis

1、NoSql概述

NoSql(Not only sql )就是不仅仅是sql,泛指非关系型数据库.

1.1、关系型数据库回顾

  • 关系型数据库具有统一的标准数据语言:SQL 一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作。

  • 关系型数据都是表格式的,数据存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。

  • 关系型数据库强调强事务性,支持对事务原子性细粒度控制,并且易于回滚事务,数据安全性高;

  • 主流的关系型数据库包括 Oracle、MySQL、SQL Server、Microsoft Access、DB2 等。

1.2、非关系型数据库

  • 主流的关系型数据库外的数据库,都认为是非关系型。 非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。

  • 非关系型数据库虽然支持事务,但稳定性方面没法和关系型数据库比较,它们主要价值针对关系数据库的不足之处:大数量存储,高并发,快速响应,高扩展等方面进行体现。

  • 对数据库高并发读写需求; 对海量数据高效存储与大量访问需求; 对数据库高可扩展性与高可用性需求

  • 主流的 NoSQL 数据库有 Redis、MongBD、Hbase等。

2、为什么要学习NoSql

学习NoSql,主要是为了解决传统关系型数据库出现性能瓶颈的问题.

web程序在现在这个时代不能仅仅专注在功能上,同时也要追求性能.NoSql的出现就是为了解决"三高"问题.

  • 对数据库高并发读写的要求

  • 对海量数据的高效率存储和访问的需求

  • 对数据库的高可扩展性和高可用性的需求

3、Redis概述

3.1、什么是Redis

Redis 是C语言开发的单线程模型的一个开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL(not-only sql,非关系型数据库)的数据库;支持丰富的数据类型,如:String、list、set、zset、hash;支持数据持久化。

支持的数据类型有:

  • 字符串类型 string

  • 散列类型 hash

  • 列表类型 list

  • 集合类型 set

  • 有序集合类型 sortedset

Redis特性

  • 支持高并发操作:由于redis基于 内存 操作,读写异常快速,支持高并发;内存读操作更快,写操作慢一些。读操作11万/秒。写操作8万/秒.

  • 线程安全:由于redis单线程模型设计,redis指令执行天然是线程安全的,每个请求操作在执行时,不会有其他请求也在操作。

  • 原子性操作:redis所有单个指令操作都是原子性的,不可拆分。支持多指令原子性,即事务。

  • 基于内存做数据存储。内存可靠性不高,但内存寻址快。redis最好用来做缓存,不要取代mysql数据库。一般采用redis+mysql模式

  • 持久化:可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

  • 丰富的数据存储类型。string,list,set,hash,zset,适合各种业务场景。

  • 支持集群:支持主从模式,支持配置集群,更利于支撑大型的项目。

4、Redis的应用场景

  • 在高并发业务的一些操作,比如:商品秒杀,抢购。削峰操作

  • 高频热点数据。频繁被查询的数据。比如:活跃用户,大多数用户进入系统后都要查询的数据。

  • 常量数据。系统中从添加成功后,基本不变动的数据。比如:字典项数据。

  • 临时数据存储:比如:验证码,临时订单

5、Redis的安装

下载地址:Releases · microsoftarchive/redis · GitHub

Redis官方提倡使用linux版本的Redis,所以官网只提供了mac和linux版本的redis.

在今天资源中有windows版本的redis,只需要解压到一个没有空格,没有中文和特殊符号的文件夹下即可.

redis-server 就是redis的服务器

redis-cli 就是redis的客户端

Redis默认服务器的端口号是6379

redis.windows.conf 配置文件

# 白名单ip
bind 127.0.0.1
# 服务器端口
port 6379
# 在timeout时间内如果没有数据交互,redis侧将关闭连接。0:永不断开
timeout 0
# 默认数据库个数
databases 16
​
# rdb持久化策略,save 改变数 秒数
save 900 1
save 300 10
save 60 10000
# rdb持久化文件名
dbfilename dump.rdb
# 持久化文件目录
dir ./
​
# 客户端连接密码
requirepass root
​
# 是否开启aof持久化
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
# 设置aof重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
auto-aof-rewrite-percentage 100
# 设置aof文件重写的基准值,最小文件64MB。达到这个值开始重写。
auto-aof-rewrite-min-size 64mb 
# 例如:aof文件达到70MB开始重写,压缩降到50MB,下次什么时候开始重写?100MB

1,服务端的启动,在redis的目录下执行

redis-server.exe  redis.windows.conf

2,客户端的启动

redis-cli.exe -h ip地址 -p 端口-a 密码

3,也可以安装可视化的客户端

6、Redis的数据类型

  • string

  • hash

  • set

  • list

  • sortedset

6.1、string(字符串)

6.1.1、概述

string是redis中最基本的类型,也是用的最多的类型.一个key对应一个value,一个key最多能存储512MB.

6.1.2、应用场景
  • 缓存功能:字符串最经典也是最常用的使用场景.redis作为缓存层,mysql作为存储层,绝大部分的请求数据都是在redis中操作.由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低后端压力的作用.

  • 计数器功能:比如视频播放次数,点赞次数

  • ID递增

6.1.3、常见命令
set key value    设置指定key的值
get key          获取指定key的值
mset key value key value...   同时设置多个值
mget key1 key2...             同时获取多个值
del key          删除key
getset key value   将给定key的值设为value,并返回key的旧值
setex key seconds value  将值value关联到key,并将key的过期时间设为seconds秒
incr key              将key中存储的数字值加1
decr key              将key中存储的数字值减1
incrby key increment  将key中所存储的值加上给定的增量值
decrby key decrement  将key中所存储的值减去给定的减量值

6.2、hash(哈希)

6.2.1、概述

Redis中hash是一个键值对集合,实际上是一个string类型的field和value的映射表,特别适合存储对象;每个 hash 最多可以存储 (40多亿)键值对。

6.2.2、应用场景

用一个对象来存储信息时可以考虑使用hash

6.2.3、常见命令
hset key field value   将哈希表key中的字段field的值设为value
hget  key field          获取存储在哈希表中指定字段field的值
hmset  key field1 value1 [field2 value2]  同时将多个field-value值设置给key
hmget key field1 [field2]    获取多个给定字段的值
hdel key field1 [field2]      删除一个或者多个哈希表中的字段
hlen key                   获取哈希表中字段的数量
del key               删除整个hash(对象)
hgetall key              获取在哈希表中指定key的所有的字段和值
hkeys key                获取所有哈希表中的字段
hvals key                 获取哈希表中所有的值

6.3、list(列表)

6.3.1、概述

列表类型可以存储一个有序的字符串列表,常用的操作是向列表的两端添加元素,或者获取列表的某一个片段. 列表的内部使用的是双向链表实现的.向列表两端添加元素的时间复杂度是O(1),获取越接近两端的元素速度就越快.

每个列表最多可以存储 (40多亿)个元素

list使用的是栈结构.

6.3.2、常用命令
lpush key value1 value2... 将一个或者多个值插入到列表左边
lpop key                   从列表左边弹出一个值
rpush key value1 value2... 将一个或者多个值插入到列表右边
rpop key                   从列表右边弹出一个值
llen key                   返回指定key所对应的list中元素个数
lindex key index           通过索引获取列表中的元素
LRANGE [KEY] [START] [STOP] – 获取列表指定范围内的元素

6.4、set(集合)

6.4.1、概述

set是string类型的无序集合,集合成员是唯一的.

Redis还提供了多个集合之间的交集,并集,差集的运算

每个set集合最多可以存储(40多亿)个元素。

特点:无序+唯一

6.4.2、应用场景
  • 投票记录

  • 共同好友,共同兴趣

6.4.3、常用命令
sadd key member1 [member2]   向集合添加一个或者多个成员
srem key member1 [member2]   移出一个或者多个成员
smembers key                 返回集合中的所有成员,查看所有
spop key                     移出并返回集合中的一个随机元素
sdiff  key1 [key2]           返回的是第一个集合的差集
sunion key1 key2             返回所有给定集合中的并集
sinter key1 key2             返回所有给定集合中的交集

6.5、sorted set(有序集合)

6.5.1、概述

redis有序集合和集合一样也是string类型元素的集合,并且不允许有重复的成员.

不同的是每一个元素都会关联一个double类型的分数,redis通过这个分数值来进行排序.

一般在排行榜、热度排序等业务场景中使用。每个集合最多可以存储(40多亿)个元素

分数值可以重复,成员不可重复.

特点:有序 + 唯一

6.5.2、常见命令
zadd key  score   member [score member...]    添加元素
zrange key start stop   获取排名在某个返回内的元素列表
zscore key member   获取元素的分数
zcard key            获取集合中的元素的数量
zrem  key  member [member...]    删除元素

7、Redis通用的操作和多数据库性

7.1、Redis通用命令

Redis通用命令就是上面5种数据类型都可以使用的命令

keys *  :  查询所有的key
exists key : 判断是否有指定的key.如果有返回1,否则返回0
expire key 秒数: 设置这个key在缓存中的存活时间
ttl key : 展示指定key的剩余时间
    返回值是-2:已过期或者不存在
    返回值是-1:永不过期
del key:  删除指定的key
rename key newKey:重命名
type key:判断一个key的类型

7.2、多数据库性

redis中默认有16个数据库,编号从0-15

select index :切换数据库
move key index:把key移动到几号库(index是库的编号)
flushdb:清空当前数据库
flushall:清空当前所有的数据库

8、Redis的持久化

8.1、概述

Redis的高性能是由于其将所有的数据默认都存储在了内存中,为了使redis在重启之后仍能够访问数据,需要将数据从内存中保存到硬盘中,这个过程称之为持久化.

8.2、RDB持久化机制

RDB持久化是指在指定的时间间隔内,将内存中的数据集快照写入硬盘.这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名是dump.rbd.这种方式默认已经开启了,不需要额外的配置.

  • 在redis.windows.conf配置文件中有如下配置

save 900 1
save 300 10
save 60 10000

  • 解释

关键字时间(秒)key修改数量解释
save9001每900秒至少有一个key发生变化,则dump内存快照
save30010每300秒至少有10个key发生变化,则dump内存快照
save6010000每60秒至少有10000个key发生变化,则dump内存快照

8.3、AOF持久化机制

AOF持久化机制会将每一个收到的写命令都通过write函数追加到文件中,默认文件名是appendonly.aof,这种方式默认是没有开启的,使用时需要配置.

  • 开启AOF持久化机制

    • 在redis.windows.conf配置文件中

    `appendonly yes`把no改为yes

  • 使用AOF

关键字持久化时机解释
appendfsyncalways每执行一次更新命令,持久化一次
appendfsynceverysec每秒钟持久化一次
appendfsyncno不持久化

9、Jedis

9.1、基本使用

1,导入依赖

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.0.1</version>
</dependency>

2,测试代码

package com.javasm;
​
import redis.clients.jedis.Jedis;
​
import java.util.HashMap;
import java.util.Map;
​
public class JedisTest {
    /**
     * Jedis的基本使用
     *
     * @param args
     */
    public static void main(String[] args) {
        // 创建jedis对象 默认ip是localhost 端口号是6379
        Jedis jedis = new Jedis("localhost", 6379);
        // string
        jedis.set("string1", "张三");
        System.out.println(jedis.get("string1"));
        jedis.mset("string2", "aaa", "string3", "bbb");
        System.out.println(jedis.mget("string2", "string3"));
​
        // hash
        jedis.hset("user", "username", "zs");
        System.out.println(jedis.hget("user", "username"));
​
        Map<String, String> map = new HashMap<>();
        map.put("nickname", "laozhang");
        map.put("password", "123");
        jedis.hmset("user", map);
        System.out.println(jedis.hget("user", "nickname"));
​
        // list
        jedis.lpush("l1", "aa", "bb", "cc");
        System.out.println(jedis.lpop("l1"));
​
        // set
        jedis.sadd("set1", "aaa", "bbb", "ccc");
        System.out.println(jedis.smembers("set1"));
​
        // sortedset
        Map<String, Double> map1 = new HashMap<>();
        map1.put("b", 89.9);
        map1.put("c", 91.1);
        jedis.zadd("z1", 90.0, "a");
        jedis.zadd("z1", map1);
//        升序排列
        System.out.println(jedis.zrange("z1", 0, -1));
//        降序排列
        System.out.println(jedis.zrevrange("z1", 0, -1));
        // 关闭资源
        jedis.close();
    }
}

9.2 Redis使用连接池

package com.javasm;
​
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
​
import java.util.HashMap;
import java.util.Map;
​
public class JedisUtil {
    private static JedisPool jedisPool;
​
    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(50);
        jedisPoolConfig.setMaxIdle(10);
        jedisPool = new JedisPool(jedisPoolConfig, "localhost", 6379);
    }
​
    public static Jedis getJedis() {
        Jedis resource = jedisPool.getResource();
        return resource;
    }
​
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

10、Springboot集成redis

10.1.基本使用

1,保证是个springboot项目, 依赖导入

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

2,认识自动配置类RedisAutoConfiguration

  • 此配置类导入了LettuceConnectionConfiguration与JedisConnectionConfiguration两个组件的配置类。这两个组件原则上只有一个生效,目的是对连接池对象进行了封装,并支持集群连接;

  • 此配置类注册了RedisTempalte与StringRedisTemplate两个操作工具对象,依赖的RedisConnectionFactory连接工厂对象,提供了redis常用的5种数据类型的操作指令封装,简化原生jedis代码。

3,配置文件application.yml 中需要加入

spring:
  redis:
    host: localhost
    port: 6379

10.2、RedisTemplate常用方法

  • RedisTemplate默认采用ObjectOutputStrema与ObjectInputStream对key与value进行序列化与反序列化,保存到redis中的数据阅读性差,但使用方便,不用手工转json,对象必须从Serializable接口派生;

  • 需要在配置类中对其序列化方式进行配置

0,定义配置类配置RedisTemplate

package com.javasm.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
​
​
@Configuration
public class RedisConfiguration {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        //key采用String的序列化方式
       template.setKeySerializer(RedisSerializer.string());
        //value序列化方式采用jackson
        template.setValueSerializer(RedisSerializer.json());
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(RedisSerializer.json());
        template.setConnectionFactory(factory);
        return template;
    }
}

1,常见方法

  • 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口

    ValueOperations:简单K-V操作 SetOperations:set类型数据操作 ZSetOperations:zset类型数据操作 HashOperations:针对map类型的数据操作 ListOperations:针对list类型的数据操作

  • 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key

    BoundKeyOperations BoundValueOperations BoundSetOperations BoundListOperations BoundSetOperations BoundHashOperations

2,字符串键值对操作

 @Test
    public void test1() {
//        简单K-V操作
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("key1", "value1");
        Object key2 = valueOperations.get("key1");
        System.out.println(key2);
//        带有过期时间设置 10秒后过期
        valueOperations.set("key2", "value2", 10, TimeUnit.SECONDS);
        valueOperations.set("key3", new Student(1, "aa"));
        Object key1 = valueOperations.get("key3");
        System.out.println(key1);
    }

3,hash字典表操作

 @Test
    public void test2() {
//      针对map类型的数据操作
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.put("stu1", "name", "张三");
        hashOperations.put("stu1", "age", "12");
        Object name = hashOperations.get("stu1", "name");
        Object age = hashOperations.get("stu1", "age");
        System.out.println("name: " + name);
        System.out.println("age: " + age);
//        绑定主键的简化操作
        BoundHashOperations stu1 = redisTemplate.boundHashOps("stu1");
        stu1.put("address", "郑州市");
        Object address = stu1.get("address");
        System.out.println("address: " + address);
    }

4,list操作

 @Test
    public void test3() {
//      针对list类型的数据操作
        ListOperations listOperations = redisTemplate.opsForList();
        listOperations.leftPush("list1", 1);
        listOperations.leftPush("list1", 2);
        listOperations.leftPush("list1", 3);
        listOperations.rightPush("list1", "java1");
        listOperations.rightPush("list1", "java2");
        listOperations.rightPush("list1", "java3");
//        删除左边第一个元素 并返回
        Object leftPop = listOperations.leftPop("list1");
//        删除右边第一个元素 并返回
        Object rightPop = listOperations.rightPop("list1");
        System.out.println("leftPop: " + leftPop);
        System.out.println("rightPop: " + rightPop);
//        起始索引 与结束索引
        List list = listOperations.range("list1", 0, 3);
        System.out.println(list);
        Long size = listOperations.size("list1");
        System.out.println("size: " + size);
    }
​
    /**
     * list绑定key的操作
     */
    @Test
    public void test4() {
        BoundListOperations list1 = redisTemplate.boundListOps("list1");
        list1.leftPush("python1");
        list1.rightPush("python2");
//        删除并返回左边第一个元素
        Object leftPop = list1.leftPop();
//        删除并返回右边第一个元素
        Object rightPop = list1.rightPop();
// list 的大小
        Long size = list1.size();
        System.out.println("size: " + size);
//        指定索引对应的元素值
        Object index = list1.index(0);
        System.out.println("index: " + index);
    }

5,对set的操作

@Test
    public void test5() {
//        对set的操作
        SetOperations setOperations = redisTemplate.opsForSet();
        setOperations.add("set1", "v1", "v2", "v3", "v4");
//        获取set的所有元素
        Set set = setOperations.members("set1");
        System.out.println("set1: " + set);
        setOperations.add("set2", "v3", "v4", "v5", "v6");
        Set difference = setOperations.difference("set1", "set2");
        System.out.println("diff差集: " + difference);
        Set union = setOperations.union("set1", "set2");
        System.out.println("union并集: " + union);
        Set intersect = setOperations.intersect("set1", "set2");
        System.out.println("intersect交集" + intersect);
//    绑定key的操作
        BoundSetOperations set1 = redisTemplate.boundSetOps("set1");
        set1.add("java1", "java2");
        Set members = set1.members();
        Set set2 = set1.union("set2");
        System.out.println("并集: " + set2);
    }

6,对zset的操作

 @Test
    public void test6() {
//        对zset类型的操作
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add("zset1", "v1", 99.2);
        zSetOperations.add("zset1", "v2", 69.6);
        zSetOperations.add("zset1", "v3", 89.6);
        zSetOperations.add("zset1", "v4", 59.6);
//        获取指定分值范围内的元素
        Set range = zSetOperations.rangeByScore("zset1", 50, 80);
        System.out.println("range : " + range);
//        指定分数之间有几个元素
        Long count = zSetOperations.count("zset1", 50, 80);
        System.out.println("count: " + count);
//        查看指定元素的排名
        Long rank = zSetOperations.rank("zset1", "v4");
        System.out.println("rank: " + rank);
//        指定元素的分值 加上一个指定的分数
        zSetOperations.incrementScore("zset1", "v1", 10);
//        查询所有元素
        Set range1 = zSetOperations.range("zset1", 0, -1);
        System.out.println("所有元素: " + range1);
//绑定操作
        BoundZSetOperations zset1 = redisTemplate.boundZSetOps("zset1");
        Long count1 = zset1.count(50, 80);
//        获取指定元素的分值
        Double v1 = zset1.score("v1");
        System.out.println("v1: "+v1);
    }

7,通用方法

   @Test
    public void remove() {
//        是否包含指定key
        Boolean b = redisTemplate.hasKey("set1");
        System.out.println("是否包含指定key list1: " + b);
// 获取指定的key的类型
        DataType dataType = redisTemplate.type("list1");
        System.out.println("dataType: " + dataType);
//        指定的key设置过期时间 10秒后过期
        redisTemplate.expire("list1", 10, TimeUnit.SECONDS);
//        指定的某个时间点过期
        redisTemplate.expireAt("list1", Instant.parse("2023-07-14T12:00:57.2Z"));
//        剩余时间
        Long expire = redisTemplate.getExpire("list1");
        System.out.println("expire: " + expire);
//        获取所有的key 集合
        Set keys = redisTemplate.keys("*");
        System.out.println("keys: " + keys);
//        删除所有的key
        redisTemplate.delete(keys);
//       删除指定的key
        Boolean list1 = redisTemplate.delete("list1");
    }

10.3、StringRedisTemplate使用

  • StringRedisTemplate是RedisTemplate子类,支持key与value的类型都是String,调用String的getBytes方法对key与value进行序列化,调用new Stirng(byte[] bytes)进行反序列化;使用时,需要手工把对象转字符串进行操作。

1,基本使用

 @Test
    public void method1() throws JsonProcessingException {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set("kk1", "郑州市");
        String kk1 = operations.get("kk1");
        System.out.println(kk1);
        ObjectMapper objectMapper = new ObjectMapper();
        operations.set("kk2",objectMapper.writeValueAsString(new Student(1, "李四")));
        BoundHashOperations<String, Object, Object> hash1 =
                stringRedisTemplate.boundHashOps("hash1");
        hash1.put("hak1",objectMapper.writeValueAsString(new Student(1, "李四")));
        Object hak1 = hash1.get("hak1");
        System.out.println(hak1);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值