Redis学习笔记

Redis学习笔记

一、Redis简介

什么是redis

Redis是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存持久化的日志型、key-value数据库,并提供多语言的api

BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。 当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。

2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。

3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。

BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。

NoSQL

NoSQL,泛指非关系型数据库,NoSQL即Not-only SQL,它可以作为关系型数据库的良好补充,随着互联网web2.0网站的兴起,非关系型数据库现在成了一个极其热门的新领域,非关系型数据库产品的发展非常迅速

而传统的关系型数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,例如:

1.High performance - 对数据库高并发读写的需求
2.Huge Storage - 对海量数据的高效率存储和访问的需求
3.High Scalability && High Availability - 对数据库的高可扩展性和高可用性的需求

NoSQL数据库的产生就是为了解决大规模数据集合多重数据类型带来的挑战,尤其是大数据应用难题**

NOSQL数据库的类别

键值(key-value)存储数据库

​ 优势:快速查询

​ 劣势:存储的数据缺少结构化

列存储数据库

​ 这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但它们的特点是指向了多个列。这些列是由列家族来安排的。

​ 优势:查找速度快,可拓展性强,更容易进行分布式拓展

​ 劣势:功能相对局限

文档型数据库

​ 优势:数据结构要求不严格

​ 劣势:查询性能不高,而且缺乏统一的查询语法

图形数据库

​ 优势:利用图结构相关算法

​ 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案

Redis描述

  • 性能极高 - Redis能读的速度是11w次/s,写的速度是8.1w次每秒
  • 丰富的数据类型 - String Hash List Set 及Ordered Set 数据类型操作。
  • 原子性 - Redis的所有操作都是原子性的,意思就是要么成功要么失败完全不执行
  • 丰富的特性 - Redis还支持publish/subscribe,通知,key过期等到特性
  • 高速读写 - redis适用自己实现的分离器,代码量很短,没有实现lock(Mysql),因此效率非常高

Redis缺点

  • 持久化 - redis直接将数据放在内存中,要将数据放在硬盘,redis可以使用两种方式实现持久化过程。定时快照(snapshot):每隔一段时间将整个数据库写在磁盘上,每次均是写全部数据,代价非常高。第二种方式基于语句追加(aof):只追踪变化的数据,但是追加的log可能过大,同时所有的操作均重新执行一遍,回复速度慢
  • 耗内存,占用内存高

总结

因此,我们总结NoSQL数据库在以下的这几种情况下比较适用:

1.数据模型比较简单

2.需要灵活性更强的IT系统

3.对数据库性能要求比较高

4.不需要高度的数据一致性

5.对于给定的key,比较容易映射复杂值的环境

NoSQl数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题

(高并发)

二、Redis的安装

安装前准备

官方网站:https://redis.io/

官方下载:https://redis.io/download 可以根据需要下载不同版本

Redis安装

Redis是c语言开发,安装Redis首先要安装gcc

下载后将其解压

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

下载好就要进行安装

将文件夹解压在opt目录下

tar -xzvf redis-5.0.8.tar.gz -C /opt

cd /opt/redis-5.0.8

make MALLOC=libc

安然进行安装

make PREFIX=/usr/local/redis install

这样就算安装完毕了

三.Redis的核心配置文件Redis.conf讲解文件

获取Redis的配置文件

cp /opt/redis-5.0.8/redis.conf /usr/local/redis/

修改配置文件

首先我们要将redis进行启动设置为守护进程启动 daemonize no 修改为 yes

修改访问默认id地址 ,修改为其他地址也可访问将 bind 127.0.0.1 注释掉

设置密码 默认没有密码 不安全 所以我们要设置 requirepass将注释删除 然后改为 requirepass root

启动redis

如果启动redis要通过我们的修改的配置文件进行启动的话

我们需要在启动时携带参数

./bin/redis-server ./redis.conf

注意:这里启动要么直接是管理员操作窗口进行启动 要么启动添加管理员(也就是添加sudo进行启动) 若不采用管理员方式进行启动的话,关闭会报错 其原因是关闭时会将数据写入硬盘中,而文件位置为root用户才能更改的位置所有会报错

然后我们通过客户端进行访问

./bin/redis-cli -h 127.0.0.1 -p 6379 -a root

这里的 -h 为ip地址 -p 为端口号 -a 为密码

Redis的内存维护策略(淘汰)

redis存储数据的位置为内存当中 ,当内存不足,继续往内存中存放数据就会导致数据丢失

解决方案

一.为数据设置超时时间

expire key time (以秒为单位) -- 这时最常用的方式

setex(String key,int second,String value) --字符串独有的方式

set k1 123 (设置k1 123 键值对)
ttl k1 (查看时间) 结果应该为 -1(永不过期)
expire k1 15 (设置过期时间为15秒)
... 过了15秒后
tt1 k1 (查看时间) 结果应该为-2  -2代表已经销毁

除了字符串自己独有设置过期时间方法外,其他方法都需要依靠expire方法来设置时间

如果没有设置时间,那缓存就是永不过期

如果设置了过期时间,又想让缓存永不过期,使用persist key

二.采用LRU算法动态将不用的数据删除

内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,

操作系统会根据哪些数据属于LRU而将其移除内存而腾出空间来加载另外的数据。

1.volatile-lru : 设定超时时间的数据,删除最不常用的数据

2.allkeys-lru : 查询所有的key中最不常用的数据进行删除,这是应用最广泛的策略

3.volatile-random : 在以及设定了超时的数据中随机删除

4.allkeys-random :查询所有的key,然后随机删除

5.volatile-ttl :查询全部设定超时时间的数据,之后排序,将马上将要过期的数据进行删除操作

6.noeviction:如果设置了该属性,则不会进行删除操作,如果内存溢出,则报错返回

7.volatile-lfu :从所有配置了过期时间的键中驱逐使用频率最少的键

8.allkeys-lfu:从所有键中驱逐使用频率最少的键

四、Redis关闭

第一种关闭方式(非正常关闭,断电)

查询redis进程id

PID ps -ef | grep -i redis

kill对查询id进行强制关闭

kill -9 PID

第二种关闭方式(正常关闭,数据保存)

关闭redis服务,通过客户端进行shutdown

注意:如果redis设置了密码,需要先在客户端通过密码登录,再进行shutdown

没有设置密码 可以客户端直接shutdown

可以通过进程shutdown关闭

ps -ef | grep -i redis

./bin/redis-cli shutdown

五、Redis远程连接

目前来说比较流行的是redisdesktopmanager

连接之前首先要关闭防火墙和设置密码

关闭防火墙

firewall-cmd --zone=public --add-port=6379/tcp --permanent

(manjaro 默认是关闭防火墙)

然后查看本机ip地址(manjaro查看ip的方式为 ip addr 然后 inet后面的就是ip地址)

然后在redisdesktopmanager中填写密码 IP地址进行连接

六、Docker安装Redis并使用Redis

首先安装Docker

sudo pacman -S docker (注意:这里是在manjaro中安装docker)

启动docker服务

sudo systemctl start docker  (这里启动记得使用管理员方式)

在docker中查询redis (这里也要用管理员方式)

sudo docker search redis

下载镜像

sudo docker pull redis:latest

创建并运行容器

sudo docker run -d --name redis-6379 -p 6379:6379 redis --requirepass "root"

然后进入容器

sudo docker exec -it redis-6379 bash

通过客户端打开

sudo docker exec -it redis-6379 redis-cli -a root

七、Redis常用命令及常用操作

Reids命令用于在redis服务上执行操作。要在redis服务上执行命令需要一个redis客户端。redis客户端在我们之前下载的redis安装包中

Redis常用数据类型及应用场景

String Hash List Set Zset Hyperloglog

常用命令的管理

keys * :返回满足的所有键,可以模糊匹配,比如 keys abc* 代表以abc开头的 key 例:keys *
exists key:是否存在指定的key 存在返回1 否则返回0  例:exists name
expire key second:设置某个key的过期时间 时间为秒 例:expire name 20
del key:删除某个key 例:del name
ttl key:查看剩余时间,当key不存在时,返回 -2,存在但设置剩余生存时间时,返回 -1 否则,以秒为单位,返回key的剩余时间 例:ttl name
persist key:移除key的过期时间 例如:persist key
PEXPIRE key milliseconds 修改key 的过期时间 单位为毫秒 例如:PEXPIRE name 1500
select:选择数据库 数据库为0-15 设计成多个数据库实际上是为了数据库安全和备份
move key dbindex:将当前数据库中的key转移到其他数据库
randmkey:随机返回一个key
rename key1 key2:重命名key 例: rename name Name
echo:打印命令
dbsize:查看数据库的key数量
info:查看数据库信息
config get * 实际传输收到的请求,返回相关的配置
flushdb:清空当前数据库
flushall:情况所有数据库
pttl key:以毫秒为单位返回key的过期时间
move key db:将key移动到指定数据库
type key:返回key所储存的值的类型

应用场景

EXPIPE key seconds

1.限时的优惠活动信息

2.网站数据缓存(对于一些需要定时更新的数据,例如:积分排行榜)

3.手机验证码

4.限制网站访客访问频率(例如:3秒钟最多访问5次)

获取ip -->计数key的生存时间1s

key的命名建议

redis单个key允许存入512M大小

  • key不要太长,尽量不要超过1024字节
  • key也不要太短,如果太短,会降低可读性
  • 在一个项目中,key最好使用统一的命名模式
  • key名称区分大小写

通过Springboot2.x中的Redis进行连接(RedisTemplate[Lettuce])

八、Redis数据类型

String 命令

赋值语法:
	set key_name value:(说明:多次设置会覆盖)(Redis SET 命令用于设置给定key的值。如果key已经存储变量,set就会覆盖旧值,切无视类型)

命令:
	setnx key1 value:(no exist)如果key1不存在,则赋值并返回1,如果key1存在,则不设值并返回0;(解决分布式锁,方案之一,只有在key不存在时设置key的值,setnx(set if not exist)命令在指定key不存在时,为key设置指定的值)
	setex key1 10 1x :(expired 设置key1的值为1x 过期时间为10秒,10秒后key1清除(key也清除))
	
	setrange string range value : 替换字符串
	
取值语法:
	get key_name: redis get 命令用于获取指定key 的值,如果key不存在,返回nil。如果key存储的值不是字符串类型,返回一个错误
	getrange key start end :用于获取存储在指定key中字符串的子字符串。字符串的截取范围由start 和 end两个偏移量决定(包括start 和 end)
	getbit key offset :对key所存储的字符串值,获取指定偏移量上的位(bit)
	
	getset语法:
		getset key_name value :getset 命令用于设置key的值 并返回key的旧值 当key不存在时,返回 nil
		strlen:返回key所存存储的字符串值的长度
		
删除语法:
	del key_name :删除指定的key,如果存在,返回值数据类型
	
批量写:MSET k1 v1 k2 v2
批量读:MGET k1 k2 k3 k4

getset name value:一次性设值和读取(返回旧值,写上新值)

自增/自减:
INCR KEY_Name :Incr 命令将key中存储的数字值增1,如果key不存在那么先初始化key的值为0 然后再进行自增
	自增:INCRBY key_name munber:增量值为Incrby 命令将key中存储的数字加上指定的增量值
	自减:DECR key_name 或 DECYBY key_name number减值:DECR 命令将key中存储的数字减1 |减少指定的值
注意:这些key对应的必须是数字类型字符串,否则会出错

字符串拼接 append key_name value:Append 命令用于为指定的key追加至末尾,如果不存在,为其赋值

字符串长度:strlen key

应用场景

  • String通常用于保存单个字符串或JSON字符串数据
  • 因String是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储
  • 计数器(常规key-value缓存应用,常规计数:微博数 粉丝数)

INCR等指令本身旧就具有原子操作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果。假如,在某个场景下有三个客户同时读取了mynum(值为2),然后对其同时进行了加1操作,那么mynum的值一定是5

不少网站都利用redis的这个特性来实现业务上的统计计数需求

Hash类型

简介

  Hash类型是string类型的field和value的映射表,或者说是一一个String集合。hash特别适台用于存储对象,相比较而言,将一个对象类型存储在Hash类型要存储在String类型里占用更少的内存空间,并对整个对象的存取。可以看成具有KEY和VALUE的MAP容器,该类型非常适合于存储值对象的信息,
  如: uname,upass ,age等。该类型的数据仅占用很少的磁盘空间(相比于JSON)。Redis中每个hash可以存储2的32次方一 1键值对( 40多亿)
  将hash理解成一个对象 例如 
  hmset user:2 name zs age 23
  就是一个用户对象 对象名为user:2
  里面有两个属性 name 值为zs
  				age 值为2

Hash命令

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

取值语法:
  HGET KEY FIELD  :获取存储在HASH中的值,根据FIELD得到VALUE
  HMGET KEY field[fie1d1] : 获取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 incr ement : 为哈希表key中的指定字段的浮点数值加上增量increment。
	HEXISTS key field :查看哈希表 key中,指定的字段是否存在

应用场景

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

1.常用于存储一个对象

2.为什么不用String存储一个对象

hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转化成hashmap存放在redis中.

用户id为查找的key,存储的value用户对象包含姓名 年龄 生日等信息 如果用普通的key/value结构来存储,主要有以下两种存储方式:

​ 第一种方式用户id作为查找key,把其他信息封装成一个对象以序列化的方式进行存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要将整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题

​ 第二种方式是这个用户信息对象有多少成员就存成多少个key-value键值对,用用户id + 对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户id为重复存储,如果存在大量这样的数据内存浪费是非常可观的

总结:

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

九、常用的Redis客户端介绍及对比

Jedis api在线网址:http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html

redisson官网地址:https://redisson.org/

redisson git项目地址: https://github.com/redisson/redisson

lettuce官网地址:https://lettuce.io/

lettuce git项目地址: https://github.com/ettuce-io/lettuce-core

首先,在spring boot2之后,对redis连接的支持,默认就采用了lettuce。这就一定程度说明了lettuce 和Jedis的优劣。

概念:

Jedis :是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持

Redisson :实现了分布式和可扩展的Java数据结构。

Lettuce :高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

优点:

Jedis :比较全面的提供了Redis的操作特性
Redisson :促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列
Lettuce :基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作

可伸缩:

  Jedis :使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/0才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis.

  Redisson :基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作

  Lettuce :基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的 ,所以可以操作单个L ettuce连接来完成各种操作

  lettuce能够支持redis4,需要java8及以上。

  lettuce是基于netty实现的与redis进行同步和异步的通信。

lettuce和jedis比较:

  jedis使直接连接redis server ,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实例增加物理连接;

  lettuce的连接是基于Netty的,连接实例( StatefulRedisConnection )可以在多个线程间并发访问,

  StatefulRedisConnection是线程安全的,所以一一个连接实例可以满足多线程环境下的并发访问,当然这也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

  Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。 Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

总结:

  优先使用Lettuce,如果需要分布式锁,分布式集 合等分布式的高级特性,添加Redisson结合使用,因为Redisson本身对字符串的操作支持很差。
  在一些高并发的场景中,比如秒杀,抢票 ,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好,库存数量可能被减少到负数,出现超卖的情况,或者产生唯一-的一 个递增ID ,由于web应用部署在多个机器上,简单的同步加锁是无法实现的,给数据库加锁的话,对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害。那相对而言,redis的分布式锁,相对而言,是个很好的选择,redis官方推荐使用的Redisson就提供了分布式锁和相关服务。

在官方网站列一-些ava客户端访问,有: jedis/Redisson/jredis/jDBC Redis等,其中官方推荐使用Jedis和Redisson。常用Jedis

十、Springboot整合Jedis

注意:在进行使用redis之前记得修改配置文件中的 stop-writes-on-bgsave-error 为 no

注意:redis键值对中的键默认为String类型数据 举例上就是 有一个user他的id为2 但是这个id必须是String值而不是int值

简介

我们在使用springboot搭建微服务的时候,在很多时候还是需要redis高速缓存来缓存一些数据,存储一些高频率访问的数据,如果直接使用redis的话又比较麻烦,在这里,我们使用jedis来实现redis缓存来达到高效缓存的目的

引入Jedis依赖

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
</dependency>

因为SpringBoot内默认引用了jedis版本。

所以我们直接引入jedis依赖 无需再配置jedis的版本号了

application.yml

例如

spring:
  redis:
    host: 192.168.101.130
    password: root
    port: 6379
    jedis:
      pool:
        max-active: 10 #最大连接数
        min-idle: 2 #最小空闲数
        max-idle: 6 #最大空闲数
    timeout: 2000 #连接超时

编写config

创建类 com.fwqq.config.RedisConfig

package com.fwqq.bootjedis.config;

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

@Configuration
public class RedisConfig {
    private Logger logger = LoggerFactory.getLogger(RedisConfig.class);

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.jedis.poll.max-active}")
    private int maxActive;
    @Value("${spring.redis.jedis.poll.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.jedis.poll.min-idle}")
    private int minIdle;

    @Bean
    public JedisPool jedisPool(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxTotal(maxActive);
        jedisPoolConfig.setMinIdle(minIdle);

        JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,password);
        logger.info("jedisPool连接成功:"+host+"\t"+port);

        return jedisPool;
    }
}

注意:代码有些问题 无法读取${spring.redis.jedis.poll.min-idle}参数 待处理

测试配置

@SpringBootTest
class BootJedisApplicationTests {

    @Autowired
    private UserService service ;
    @Test
    void contextLoads() {
        String result = service.getString("pj2");
        System.out.println(result);
    }

    @Test
    void t2(){
        String key = "pj1";
        String val = "张某人到底喜不喜欢听计算机图形学";
        service.expireStr(key,val);

    }
    @Test
    void t3(){
        User user =  service.selectById(1);
        System.out.println(user);
    }
}

我们可以自定义配置类

JedisUtil.class

package com.fwqq.bootjedis.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Component
public class JedisUtil {
    @Autowired
    private JedisPool jedisPool;

    /**
     * 获取Jedis资源
     * @return
     */
    public Jedis getJedis(){
        return jedisPool.getResource();
    }

    /**
     * 释放Jedis连接
     */
    public void close(Jedis jedis){
        if(jedis!=null){
            jedis.close();
        }
    }

    public long caclTimeHour(int hours){
        long num = hours*60*60;
        return num;
    }
}

整体service层

package com.fwqq.bootjedis.Service.impl;

import com.fwqq.bootjedis.Service.UserService;
import com.fwqq.bootjedis.config.JedisUtil;
import com.fwqq.bootjedis.domian.User;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;


@Service
@Log
public class UserServiceImpl implements UserService {
    @Autowired
    private JedisPool jedisPool;

    /**  ############################### String 类型演示
     * Redis又什么命令,jedis就有什么方法
     * Redis String 类型
     * 需求:用户输入一个key,
     * 先判断Redis中是否存在该数据
     * 如果存在,在Redis中进行查询 并返回
     * 如果不存在,在mysql数据库中查询。将结果赋值给redis 并返回
     */
    @Override
    public String getString(String key) {
        //1.得到Jedis对象
        Jedis jedis = jedisPool.getResource();
        String val;
        //判断key是否存在redis
        if(jedis.exists(key)){
            val =  jedis.get(key);
        }else{
            val = "张某人虽然牛逼 但是还是缺点东西!";
            jedis.set(key,val);
        }
        //3.关闭连接
        jedis.close();
        return val;
    }

    /**
     * 测试String类型
     * 需求:用户输入一个redis数据。该key的有效期为28小时
     *
     */
    @Autowired
    JedisUtil jedisUtil;
    @Override
    public void expireStr(String key, String val) {
        Jedis jedis = jedisUtil.getJedis();
        jedis.set(key, val);
        jedis.expire(key,20);
        jedisUtil.close(jedis);
    }

    /** #################### Hash 的演示
     *  存入一个对象
     *  需求分析:根据用户id 查询用户信息
     *  用户在前端传入一个ID编写
     *  根据用户的ID 查询用户的对象信息
     *  先判断如果redis中存在,直接返回给用户结果 斌返回
     *  如果redis不存在 查询mysql,并将查询的结果赋值给redis 并返回
     */

    @Override
    public User selectById(int id){
        String key ="user:"+id;
        User user = new User();
        //1.获得Jedis对象
        Jedis jedis = jedisUtil.getJedis();
        //2.实现业务逻辑判断
        if(jedis.exists(key)){
            System.out.println("查询的是Redis数据库");
            Map<String,String> map = jedis.hgetAll(key);
            user.setName(map.get("name"));
            user.setId(Integer.parseInt(map.get("id")));
            user.setAge(Integer.parseInt(map.get("age")));
        }else{
            user.setAge(23);
            user.setName("zs");
            user.setId(id);
            System.out.println("查询的是mysql数据库"+user);
            Map<String,String> map = new HashMap<>();
            map.put("id",user.getId()+"");
            map.put("name",user.getName());
            map.put("age",user.getAge()+"");
            jedis.hmset(key,map);
            System.out.println("往redis中存入map");
        }
        return  user;
    }
}

十一、SpringBoot2.x中Redis使用(lettuce)

java代码操作Redis,需要使用Jedis,也就是redis支持java的第三方类库
注意:Jedis2.7以上的版本才支持集群操作

maven配置

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

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

hibernate-->JPA-->SpringData

yml文件配置

application.yml

server:
  port: 8080
spring:
  redis:
    port: 6379
    password: root
    host : 192.168.101.130
    lettuce:
      pool:
        max-active: 8 #连接池最大连接数(使用负值表示没有限制)
        max-idle: 8 #连接池中的最大空闲连接
        min-idle: 0 #
        max-wait: 1000
      shutdown-timeout: 100 #关闭超时时间

简介

编写缓存配置类RedisConfig用于执行redis中的方法

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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;


@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * RedisTemplate配置
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

测试

@Test
public void set() {
    redisTemplate.opsForValue().set("test:set1", "testValue1");
}

这样 redis中就会有数据了

使用lettuce操作String类型数据

@Test
public void set() {
    //存入一个String值
    redisTemplate.opsForValue().set("test:set1", "testValue1");
    //获取一个String值
    String name = (String) redisTemplate.opsForValue().get("name");
    //判断是否存在
    Boolean has = redisTemplate.hasKey("name");
    //设置过期时间
    redisTemplate.expire("name",20, TimeUnit.HOURS);
}

使用lettuce操作Hash类型数据

@Test
public void Hash(){
    int id = 1;
    if(redisTemplate.opsForHash().hasKey("user",id+"")){
        System.out.println("redis中存在该用户");
        System.out.println((User)redisTemplate.opsForHash().get("user",id+""));
    }else{
        System.out.println("redis中没有该用户");
        User user = new User();
        user.setAge(23);
        user.setId(id);
        user.setName("涛哥");
        redisTemplate.opsForHash().put("user",id+"",user);
        System.out.println((User)redisTemplate.opsForHash().get("user",id+""));
    }
}

结果

如果你的结果是16进制 那么你需要添加序列化操作

注意查看你的配置文件

Set类型数据存储

简介

Redis的Set是String类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis中集合是通过哈希表实现的, set是通过hashtable实现的集合中最大的成员数为2^32 - 1 (4294967295,每个集合可存储40多亿个成员)。类似于JAVA中的Hashtable集合

命令

  赋值语法:
  SADD key member1 [member2] : 向集合添加一一个或多个成员

  取值语法:
  SCARD key :获取集合的成员数
  SMEMBERS key :返回集合中的所有成员
  SISMEMBER key member :判断 member元素是否是集合key的成员(开发中:验证是否存在判断)
  SRANDMEMBER key [count] :返回集合中一 一个或多个随机数  
  
  删除语法:
  SREM key member1 [member2] : 移除集合中-一个或多个成员
  SPOP key [count] :移除并返回集合中的一 一个随机元素
  SMOVE source destination member :将member 元素从source 集合移动到destination 集合
  
  差集语法:
      SDIFF key1 [key2]  :返回给定所有集合的差集(左侧)
      SDIFFSTORE destination key1 [key2] : 返回给定所有集合的差集并存储在destination 中
      
  交集语法:
      SINTER key1 [key2] : 返回给定所有集合的交集(共有数据)
      SINTERSTORE destination key1 [key2] : 返回给定所有集合的交集并存储在destination 中
      
 并集语法:
      SUNION key1 [key2] : 返回所有给定集合的并集
      SUNIONSTORE destination key1 [key2] : 所有给定集合的并集存储在destination集合中

java调用Set进行操作redis

应用场景

常应用于:对两个集合间的数据[计算]进行交集、并集、差集运算
1、 利用集合操作,可以取不同兴趣圈子的交集,以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。

2、利用唯一性,可以统计访问网站的所有独立IP、存取当天[或某天]的活跃用户列表。

Zset类型数据存储

有序集合(sorted set)

简介

1、Redis 有序集合和集合一样也是string类型元素的集合,且不允许重 复的成员。

2.不同的是每个元素都会关联一-个double类型的分数。 redis正是通过分数来为集 合中的成员进行从小到大的排序。

3、有序集合的成员是谁-的,但分数(score)却可以重复。

4、集合是通过哈希表实现的。集合中最 大的成员数为2次方32- 1 (4294967295,每个集合可存储40多亿个成员)。Redis的ZSet是有序、 且不重复

(很多时候,我们都将r edis中的有序集合叫做zsets,这是因为在redis中,有序集台相关的操作指令都是以z开头的)

命令

 赋值语法:
      ZADD key score1 member1 [score2 member2] : 向有序集合添加一一个或多个成员,或者更新已存在成员的分数

取值语法:
      ZCARD key :获取有序集合 的成员数
      ZCOUNT key min max :计算在有序集合中指定区间分数的成员数
      ZRANK key member :返回有序集 合中指定成员的索引
      ZRANGE key start stop [WITHSCORES] :通过索引区间返回有序集合成指定区间内的成员(低到高)
      ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] : 通过分数返回有序集合指定区间内的成员
      ZREVRANGE key start stop [WITHSCORES] :返回有序集中指定区间内的成员,通过索引,分数从高到底
      ZREVRANGEBYSCORE key max min [WITHSCORES] :返回有 序集中指定分数区间内的成员,分数从高到低排序
删除语法:
      DEL key : 移除集合
      ZREM key member [member ...] :移除有序集合中的一 一个或多个成员
      ZREMRANGEBYRANK key start stop :移除有序集 合中给定的排名区间的所有成员(第一名是0) (低到高排序)
      ZREMRANGEBYSCORE key min max :移除有序集合中给定的分数区间的所有成员
      ZINCRBY key incr ement member  :增加memeber元素的分数i ncr ement,返回值是更改后的分数

应用场景

常用于排行榜

销量排名,积分, 成绩排名等

HyperLogLog数据类型

十二、Redis发布订阅

简介

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis客户端可以订阅任意数量的频道。

Redis发布订阅(pub/sub)是一 种消息通信模式: 发送者(pub)发送消息,订阅者(sub)接收消息。Redis客户端可以订阅任意数量的频道。
下图展示了频道channel1 ,以及订阅这个频道的三个客户端-- client2 、client5 和client1之间的关系

当有新消息通过PUBLISH命令发送给频道channel1 时,这个消息就会被发送给订阅它的三个客户端:

常用命令

订阅频道:
   SUBSCRIBE channel [channel ...] :订阅给定的一 一个或多个频道的信息
   PSUBSCRIBE pattern [pattern ...] :订阅一个或多个符合给定模式的频道。

发布频道:
   PUBLISH channe1 message :将信息发送到指定的频道。

退订频道:
   UNSUBSCRIBE [channel [channe1 ...]] :指退订给定的频道。
   PUNSUBSCRIBE [pattern [pattern ...l]: 退订所有给定模式的频道。

应用场景

这一功能最明显的用法就是构建实时消息系统,比如普通的即时聊天,群聊等功能
1在一个博客网站中 ,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们。2微信公众号模式
微博,每个用户的粉丝都是该用户的订阅者,当用户发完微博 ,所有粉丝都将收到他的动态;
新闻,资讯站点通常有多个频道,每个频道就是一个主 题,用户可以通过主题来做订阅(如RSS) ,这样当新闻发布时,订阅者可以获得更新
简单的应用场景的话,以门户网站为例,当编辑更新了某推荐板块的内容后:1. CMS发布清除缓存的消息到channel (推送者推送消息)
2.门户网站的缓存系统通过channel收到清除缓存的消息(订阅者收到消息) ,更新了推荐板块的缓存3.还可以做集中配置中心管理,当配置信息发生更改后,订阅配置信息的节点都可以收到通知消息

十三、Redis多数据库

Redis下,数据库是由一个整数索引标识,而不是一个数据库名称。默认情况下,一个客户端连接到数据库0。

redis配置文件中下面的参数来控制数据库的总数

database 16 //从0开始 123……15

selete 数据库//数据库切换

移动数据

move key名称 数据库

数据库清空

flushdb :清除当前数据库的所有key
flushall:清除整个Redis的数据库所有key

十四、Redis事务

redis事务可以一次执行多个命令,(按顺序的串行化执行,执行过程中不会被其他命令插入,不许加塞)

简介

Redis事务可以一次执行多个命令(允许在一次单独的步骤中执行一组命令),并带有以下两个重要的保证:

批量操作在发送exec命令前被放入队列缓存

收到exec命令后进入事务执行,事务中任意命令执行失败,其余命令依然被执行。

在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

1.redis会将一个事务中的所有命令序列化,然后按顺序执行

2.执行中不会被其他命令插入,不许出现加塞行为

常用命令

discard : 取消事务,放弃执行事务块内的所有命令
exec:执行所有事务块的命令
multi:标记一个事务块的命令
unwatch:取消 watch命令对所有key的监视
watch key [key ...]:监视一个/多个key,如果在事务执行之前这些key被其他命令所改动,那么事务将被打断

一个事务从开始到执行会经历以下三个阶段

开始事务。

命令入队。

执行事务。

十五、RedisCluster集群

理解:

我把集群想象成一堆连在一起的服务器,当用户进行访问是,如果一个服务器没有数据,可以访问该集群中的其他服务器来获取自己想要的数据,这样可以到达不需要每一台服务器都配备相同的数据

简介

  • 为什么使用redis Cluster
  • 为了在大流量访问下提供稳定的业务,集群化是存储的必然形态
  • 未来的发展趋势肯定是云计算和大数据的紧密结合
  • 只有分布式架构能满足要求

描述

Redis集群搭建的方式有很多种,但从redis3.0之后版本支持RedisCluster集群,至少需要3(master)+3(Slave)才能建立集群。redis-cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接

特点

  • 所有的redis节点彼此互联(ping-pong机制),内部使用二进制协议优化传输速度和宽带
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效
  • 客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
  • redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定时平均分配),cluster负责维护
  • Redis集群预分配好16384个哈希槽,当需要在Redis集群中放置一个key-value时,redis先对key使用crc16算法算出一个结果,然后把结果对16384 求余数,这样每个key都会对应一个编号在 0-16383 之间的哈希槽,redis会根据节点数量大致均等的将哈希

容错性

容错性,是指软件检测应用程序所运行的软件或者硬件中发送的错误并从错误中恢复的能力,通常可用从系统的可靠性、可用性、可测性等几个方面来衡量

redis-cluster搭建

集群中至少应该有奇数个节点,所以搭建集群最少需要三台主机。同时每个节点至少有一个备份节点,所以下面最少需要创建使用6台机器,才能完成RedisCluister集群(主节点、备份节点由redis-cluster集群确定)

真集群:准备六台服务器

假集群:一台服务器存在6个redis服务

搭建流程

5.Redis的其他功能

redis持久化

redis缓存与数据库(mysql)同步-->使用的是Kafka作为异步队列

redis知识点总结 缓存穿透、缓存雪崩、热点key等处理方案(会加入双重锁的使用)

6.Redis的高级配置

redis 5版本集群创建

JAVA客户端 远程连接RedisCluster实战应用

SpringBoot2.x+RedisCluster整合操作


posted @ 2020-06-22 18:20  非吾愆期  阅读(273)  评论(0编辑  收藏  举报