Redis全覆盖(概念,安装,命令,java操作,持久化,淘汰策略)

文章内容

  • Redis概念
  • Redis的安装
  • Redis命令入门
  • Java操作Redis
  • Redis的持久化配置
  • Redis的淘汰策略

一.Redis概念

1.NOSQL

1.1.什么是NOSQL

NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,它泛指非关系型的数据库。

关系型数据库:以关系(由行和列组成的二维表)模型建模的数据库。简单理解:有表的就是关系型数据库。

1.2.NOSQL分类

http://www.nosql-database.org/

分类Examples举例典型应用场景数据模型优点缺点
键值(key-value)Tokyo Cabinet/Tyrant, Redis, Memcached, Voldemort, Oracle BDB内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。Key 指向 Value 的键值对,通常用hash table来实现查找速度快数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库Cassandra, HBase, Riak分布式的文件系统以列簇式存储,将同一列数据存在一起查找速度快,可扩展性强,更容易进行分布式扩展功能相对局限
文档型数据库CouchDB, MongoDbWeb应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容)Key-Value对应的键值对,Value为结构化数据数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库Neo4J, InfoGrid, Infinite Graph社交网络,推荐系统等。专注于构建关系图谱图结构利用图结构相关算法。比如最短路径寻址,N度关系查找等很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群

Redis和Memcached是key-value的noSql,主要用来做缓存。

2.Redis

2.1.什么是Redis

Redis 是一个高性能的 开源的、C语言写的Nosql(非关系型数据库),数据可以存储在内存中或者磁盘中。Redis 是以key-value形式存储,和传统的关系型数据库不一样。不一定遵循传统数据库的一些基本要求,比如说,不遵循sql标准,事务,表结构等等,redis严格上不是一种数据库,应该是一种数据结构化存储方法的集合

数据结构:数组、List Set Map等

Redis是将数据保存到内存的nosql。它有很多的方法,使用特定的方法就可以将存入的字符串转化为特定的数据结构保存。

2.2.Redis的特点
  • 数据保存在内存,存取速度快,并发能力强
  • 它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(无序不可重复集合)、 zset(sorted set --有序集合)和hash(哈希类型)。
  • redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库(如MySQL)起到很好的补充作用。
  • 提供了Java,C/C++,C#,PHP,JavaScript等客户端,使用很方便。
  • Redis支持集群(主从同步)。数据可以主服务器向任意数量从的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。
  • 支持持久化
  • 支持订阅/发布

总结:

​ 1、redis是C语言写的开源免费的NoSql数据库

​ 2、数据存放在内存,还支持持久化。存取数据快,并发能力强,数据安全高。

​ 3、value支持的数据类型多。

​ 4、支持多个语言客户端。

​ 5、支持集群。(支持高并发,海量数据)

2.3.Redis、Memcached、Mysql的比较
mysqlredismemcached
类型关系型非关系型非关系型
存储位置磁盘磁盘和内存内存
存储过期不支持支持支持
读写性能非常高非常高

存储过期:一个数据存储时为他设置过期时间,时间一到数据就没有. 道具,会员,优惠券,订单,红包等

2.4.Redis的使用场景
  • 缓存

    经常查询数据,放到读速度很快的空间(内存),以便下次访问减少时间。减轻数据库压力,减少访问时间.而redis就是存放在内存中的。就如同:Mybatis 二级缓存 , ehcache框架 缓存。

  • 计数器

    网站通常需要统计注册用户数,网站总浏览次数等等 ,新浪微博转发数、点赞数。

  • 实时防攻击系统

    防止暴力破解,如使用工具不间断尝试各种密码进行登录。解决方案使用Redis记录某ip一秒访问到达10次以后自动锁定IP,30分钟后解锁

  • 设定有效期的应用

    设定一个数据,到一定的时间失效。验证码,登录过期, 自动解锁,购物券,红包。

  • 自动去重应用

    Uniq 操作,获取某段时间所有数据排重值 这个使用 Redis 的 set 数据结构最合适了,只需要不断地将数据往 set 中扔就行了,set 意为 集合,所以会自动排重。

  • 排行榜 可以利用redis的zset来进行处理

  • 队列

    构建队列系统 使用 list 可以构建队列系统,使用 sorted set 甚至可以构建有优先级的队列系统。

    秒杀:可以把名额放到内存队列(redis),内存就能处理高并发访问。

  • 消息订阅系统:

    Pub/Sub 构建实时消息系统 Redis 的 Pub/Sub 系统可以构建实时的消息系统,比如很多用 Pub/Sub 构建的实时聊天系统 的例子。如QQ群

二.Redis的安装

1.下载和安装

1.1.下载Redis
  • linux版本:http://redis.io/download

  • 中文网地址:http://redis.cn/

  • windows版本: https://github.com/microsoftarchive/redis/releases

1.2.安装Redis
  • window版本解压Redis包即可

  • linux版本安装暂时不讲,后面会讲到,有兴趣可以自己百度

1.3.Redis目录介绍
redis.window.conf    #【重要】reids配置文件

redis-benchmark.exe   #reids性能测试工具

redis-check-aof.exe    #aof文件校验、修复功能

redis-check-dump.exe    #rdb文件校验、修复功能

redis-cli.exe    #【重要】命令行客户端,连接Redis服务端可对Redis进行相关操作

redis-server.exe  #【重要】Redis服务器,启动Redis

2.Redis启动和测试

2.1.启动redis-server

进入到Redis安装目录 ,虽然双击也可以启动,但是建议使用CMD执行

redis-server.exe redis.windows.conf
2.2.启动redis-client
  • 连接本机Redis直接双击 redis-cli.exe 或者执行命令
redis-cli.exe
  • 如果连接其他服务的Redis需要跟上 -h参数
redis-cli.exe -h ip -p 端口   #如  redis-client.exe -h 192.168.0.11 -p 6379
2.3 测试Redis
set name zs	#设置数据

get name 	#获取数字

expire name 30  # 设置过期时间

ttl name  #获取过期时间

keys *  # 获取所有的key
2.4 设置密码
2.4.1 临时设置
  • CONFIG SET 命令可以动态地调整 Redis 服务器的配置而无须重启,重启后失效
config set requirepass 123456
2.4.2 永久设置
  • 修改配置文件 redis.widows.conf ,增加代码:
requirepass 123456
2.4.3 登录
auth 123456

三.Redis命令入门

1.String的操作

2.1.String结构
  • String结构模拟图
keyvalue
namezs
age18
2.2.set key value
  • 将单个字符串值value关联到key,存储到Redis
set name zs			#key为 name, 值为zs 
2.3.get key
  • 返回key关联的字符串值
get name			#获取值,key为 name
2.4.mset key value key value
  • 同时设置一个或多个 key-value 对
mset name zs age 18		#设置了两对key ,name=zs ; age=18
2.5.mget key key
  • 获取多个值
mget name age 		#获取key为name和age的数据的值
2.6.incr key
  • 将 key 中储存的数字值增1(key不存在,则初始化为0,再加1)
incr age		#age的值增加1
2.7.decr key
  • 将 key 中储存的数字值减1(key不存在,则初始化为0,再减1)
decr age		#将age的值减去1
2.8.incrby key number
  • 将 key 中储存的数字值增加指定数字
incrby age	2		#在age的值的基础上增加2	
2.9.decrby key number
  • 将 key 中储存的数字值减少指定数字
decrby age	2		#在age的值的基础上减去2	
2.10.SETEX key seconds value
  • 设置key-value,并设置过期时间
setex mykey 10 "Hello"  #设置 mykey的值为“hello” ,过期时间为 10s ,是set和expire的组合命令,且是原子性的
2.11.SETNX key value
  • 设置一个key-value,如果这个key不存在则设置成功返回1,否则设置不成功,返回0
setnx name zs
2.12.GETSET key value
  • 设置一个key-value,把这个key以前的值返回
getset name ls

2.key的操作

2.1.keys
  • 查看所有的key
keys *
2.2.del key
  • 删除指定的某个key
del username	#删除key为username的数据
2.3.expire key secnods
  • 设置key的过期时间(secnods秒后过期)
expire name 10 	#设置name的过期时间10s
2.4.ttl key
  • 查看key的过期时间
ttl name		#查看name的过期时间
2.5.flushall
  • 清空整个redis服务器数据,所有的数据库全部清空
flushall
2.6.flushdb
  • 清除当前库
flushdb
2.7.select index
  • 选择数据库,redis中默认有16个数据库,名称分别为0,1,2,15 , index数据库索引
select 1		#选择第2个数据库
2.8.exists key
  • 查询key是否存在
exists name

3.List的操作

3.1.List结构

list集合可以看成是一个左右排列的队列(列表)

  • List机构模拟图

    keyvaluevaluevalue
    nameszslsls
    ages111820
3.2.lpush key value value
  • 将一个或多个值 value 插入到列表 key 的表头(最左边)
lpush names zs ls	#往key为 names 的list左边添加值“zs”和“ls”
3.3.lrange key start stop
  • 返回列表 key 中指定 区间内的元素,查询所有的stop为-1即可
lrange names 0 -1		#查看names的所有元素
3.4.rpush key value value
  • 将一个或多个值 value 插入到列表 key 的表尾(最右边)
rpush names zl cq	#往key为 names 的list右边添加值“zl”和“cq”
3.5.lpop key
  • 移除并返回列表 key 的头(最左边)元素。
lpop names	#移除并返回names列表的头(最左边)元素
3.6.rpop key
  • 移除并返回列表 key 的尾(最右边)元素。
rpop names	#移除并返回names列表的尾(最右边)元素。
3.7.lrem key count value
  • 根据count值移除列表key中与参数 value 相等的元素count >0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。count = 0 : 移除表中所有与 value 相等的值。
lrem names 1 zs		#删除names列表中左边第1个“zs” 
lrem names 0 ls		#删除names列表中所有的“ls”
lrem names -1 cq	#删除names列表中右边第1个“cq”
3.8.lindex key index
  • 返回列表 key 中,下标为 index 的元素
lindex names 2	#取names列表中索引为 2 的元素
3.9.ltrim key start stopa
  • 对一个列表进行修剪 ,保留范围内的,范围外的删除
ltrim names 2 4		#删除names列表中索引为 2 - 4 以外的元素
3.10.Redis中如何实现栈和队列
  • list控制同一边进,同一边出就是栈
  • list控制一边进,另一边出就是队列

4.Set的操作

set集合是一个无序的不含重复值的队列

4.1.Set结构
  • Set机构模拟图
keyvaluevaluevalue
idcards110120130
phones182135136
4.2.sadd key value value
  • 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略
sadd colors red green yellow blue		#往colors这个set集合中存放元素: red,green,yellow,blue
4.3.smembers key
  • 返回集合 key 中的所有成员。
smembers colors
4.4.srem key member
  • 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略
srem colors red		#删除colors中的 red元素
4.5.SCARD key
  • 返回集合存储的key的基数 (集合元素的数量).如果key不存在,则返回 0。
scard colors
4.6.SDIFF key [key …]
  • 返回一个集合与给定集合的差集的元素
sdiff colors names
4.7.SISMEMBER key member
  • 返回成员 member 是否是存储的集合 key的成员.
  • 如果member元素是集合key的成员,则返回1
  • 如果member元素不是key的成员,或者集合key不存在,则返回0
sismember names zs		#判断names中是否包含 zs

5.ZSet的操作

ZSet(sorted sets)在Set基础上增加了“分数”,让set集合有了排序功能

5.1.ZSet结构
  • ZSet机构模拟图
keyvalue(score)value(score)value(score)
nameszs(10)ls(20)ww(30)
5.2.zadd key score value score value
  • 将所有指定成员添加到键为key有序集合(sorted set)里面,如果指定添加的成员已经是有序集合里面的成员,则会更新改成员的分数(scrore)并更新到正确的排序位置
zadd heights   150 zs 160 ls		#有序集合heights中zs的分数为150 ,ls的分数是 160
5.3.ZCARD key
  • 返回key的有序集元素个数。key存在的时候,返回有序集的元素个数,否则返回0。
zcard heights
5.4.ZCOUNT key min max
  • 返回有序集key中,score值在min和max之间(默认包括score值等于min或max)的成员。
zcount heights 150 160		#获取heignhts中分数为 150到160的元素数量
5.5.ZPOPMAX key [count]
  • 删除并返回有序集合key中的最多count个具有最高得分的成员。如未指定,count的默认值为1。
zpopmax heights 2			#删除最高分数的前2个元素
5.6.ZPOPMIN key [count]
  • 删除并返回有序集合key中的最多count个具有最低得分的成员。如未指定,count的默认值为1。
zpopmin heights 2		#删除最低分数的前2个元素
5.7.ZRANGE key start stop [WITHSCORES]
  • 返回存储在有序集合key中的指定范围的元素。 返回的元素可以认为是按得分从最低到最高排列。 如果得分相同,将按字典排序。返回给定范围内的元素列表(如果指定了WITHSCORES选项,将同时返回它们的得分)。
zrange heights 0 10 WITHSCORES	#返回heights中索引 0 到 10 的元素和其分数
5.8.ZRANK key member
  • 返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底,也就是说,score值最小的成员排名为0。
zrank heights zs	#返回 zs在heights的分数从小到大排名
5.9.ZREVRANK key member
  • 返回有序集key中成员member的排名,其中有序集成员按score值从大到小排列。排名以0为底,也就是说,score值最大的成员排名为0。
zreverank heights zs	#返回 zs在heights的分数从大到小排名
5.10.ZSCORE key member
  • 返回有序集key中,成员member的score值。如果member元素不是有序集key的成员,或key不存在,返回nil。
zscore heights zs		#返回 zs的分数
5.11.ZREVRANGE key start stop [WITHSCORES]
  • 返回有序集key中,指定区间内的成员。其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典序的反序排列。
ZREVRANGE heights 1	2	WITHSCORES		#返回索引1 - 2 的成员,按分数大到小排序
5.12.ZRANGEBYSCORE key min max WITHSCORES LIMIT offset count
  • 返回有序集合中指定分数区间内的成员,分数由低到高排序,LIMIT控制分页
ZRANGEBYSCOREkey heights 0 170 WITHSCORES LIMIT 0 10	#查询heights中0-170分之间的元素,低到高排序,0条														   #开始查询,每页10条
5.13.ZREVRANGEBYSCORE key max min WITHSCORES LIMIT offset count
  • 返回有序集合中指定分数区间内的成员,分数由高到低排序,LIMIT控制分页
指令是否必须说明
ZREVRANGEBYSCORE指令
key有序集合键名称
max最大分数值,可使用"+inf"代替
min最小分数值,可使用"-inf"代替
WITHSCORES将成员分数一并返回
LIMIT返回结果是否分页,指令中包含LIMIT后offset、count必须输入
offset返回结果起始位置
count返回结果数量
ZREVRANGEBYSCORE heights 170 0 WITHSCORES LIMIT 0 10	#返回heights中分数为170-0之间从第0条数查,10条

6.Hash的操作

Hash类似于jdk中的Map,一个key下面以键值对的方式存储数据

6.1.Hash结构
  • Hash机构模拟图

在这里插入图片描述

6.2.HSET key field value
  • 设置 key 指定的哈希集中指定字段的值。如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联。如果字段在哈希集中存在,它将被重写。
hset user:1 name zs			#给"user:1"这个key设置name=zs键值对
6.3.HGet key name
  • 获取hash类型的name键对应的值
hget user:1 name	#获取user:1总的name字段
6.4.HMSET key field value field value
  • 批量添加name=value键值对到key这个hash类型
hmset user:2 name zs age 18		#给"user:2"这个key设置name=zs键值对和age=18键值对
6.5.HMGET key field field
  • 批量获取hash类型的键对应的值
hmget user:2 name age	#获取user:2总的name和age字段
6.6.hkeys key
  • 返回哈希表 key 中的所有键
hkeys user:2	#返回user:2总的所有字段
6.7.hvals key
  • 返回哈希表 key 中的所有值
hvals user:2	#返回user:2中的所有值
6.8.hgetall key
  • 返回哈希表 key 中,所有的键和值
hgetall user:2	#返回user:2中所有key和value
6.9.存储对象的两种方式
  • 使用string结构
set user:1 {id:1,name:zs}
  • 使用hash
hset user:2 {id:2,name:ls}

7.扩展

7.1.SORT key
  • 对 list ,set ,zset进行排序
SORT ages 			#对年龄集合进行排序 
SORT ages DESC 		#对年龄集合进行排序,倒排
SORT names ALPHA  	#对姓名集合进行字典顺序排序
SORT names LIMIT 0 10 # 取names集合中,从第 0 个元素,往后取10个元素
SORT names LIMIT 0 5  ALPHA DESC # 取集合中前5个元素,按字典顺序倒排

四.Java操作Redis

1.Java集成Jedis

开始在 Java 中使用 Redis 前, 我们需要确保已经安装并启动 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。可以取Maven仓库下载驱动包 下载 jedis.jar,。

1.1.搭建工程
  • 搭建普通java项目,项目名:jedis-demo
1.2.导入Jedis依赖
  • 把Jedis和连接池包一起导入进来
commons-pool2-2.2.jar		#连接池
jedis-2.5.2.jar			    #Jedis核心包
2.3.编写测试类
  • 通过Jedis客户端对象连接Redis,调用API进行Redis
@Test
public void testJedis()throws Exception{
    //创建连接
    String host ="127.0.0.1";
    //端口
    int port = 6379;
	//超时时间,1秒超时
    int timeout = 1000;
    //jedis客户端
    Jedis jedis = new Jedis(host,port,timeout);
    //认证
    jedis.auth("123456");
    //执行操作,保存值
    jedis.set("username","wang da cui");
    //获取值
    String result = jedis.get("username");
    System.out.print(result);
    //关闭连接
    jedis.close();
}
2.4.连接池的使用

如果直接使用 Jedis链接Redis会造成频繁的Jedis对象创建和销毁,对性能会有很大的影响,所以我们会选择使用连接池来链接性能。原理同Mysql连接池

  • 编写测试方法,使用JedisPool连接池对象
@Test
public void test()throws Exception{
    //1 创建jedispool配置对象
    JedisPoolConfig config = new JedisPoolConfig();
    //2 做配置
    //最大空闲连接数
    config.setMaxIdle(2);
    //最大链接对象数
    config.setMaxTotal(10);
    //链接超时时间
    config.setMaxWaitMillis(1*1000);
    //获取连接是测试连接是否畅通
    config.setTestOnBorrow(true);
    //3 创建jedispool连接池对戏
    //参数:配置对象,redis主机地址 ,超时时间,密码
    JedisPool pool = new JedisPool(config,"127.0.0.1",6379,1*1000,"123456");
    //4 通过jedispool获取连接
    Jedis jedis = pool.getResource();
    //5 执行操作
    jedis.set("age",10);
    String result = jedis.get("age");
    System.out.println(result);
    // 6 释放连接 , 底层做了兼容,如果是连接池操作就是释放,如果是连接操作就是关闭
    jedis.close(); 
    // 7 摧毁连接池-如果是真正项目中它应该是一个受spring管理的单例
    pool.destroy();
}
  • 工具类封装:JedisPool应该是单例的(想想你的Mybatis的DataSource也只是创建了一个),为了方便使用,将JedisPool封装成工具。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.Properties;

/**
 * 获取连接池对象
 */
public enum RedisUtils {
    //使用枚举实现单例
    INSTANCE;
	//连接池对象
    private static JedisPool jedisPool = null;

    static {
        //初始化链接池
        //1 创建连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        //2 进行配置-四个配置
        最小连接数
        config.setMaxIdle(1);
		//最大连接数
        config.setMaxTotal(11);
		//链接最长等待时间
        config.setMaxWaitMillis(10 * 1000L);
		//测试连接时是否畅通
        config.setTestOnBorrow(true);
       
        String host = "127.0.0.1";
        int port = 6379;
        String password = "123456";
        int timeout = 10000;

		//创建连接池
        jedisPool = new JedisPool(config, host,port,timeout, password);
    }

    //获取连接
    public Jedis getSource() {
        return jedisPool.getResource();
    }

    //关闭资源
    public void closeSource(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }

    }

    /**
     * 设置字符值
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        Jedis jedis = getSource();
        jedis.set(key, value);
        closeSource(jedis);
    }
    /**
     * 设置字符值
     *
     * @param key
     * @param value
     * @param seconds :过期时间
     */
    public void setex(String key,int seconds, String value) {
        Jedis jedis = getSource();
        jedis.setex(key,seconds, value);
        closeSource(jedis);
    }

    /**
     * 设置字符值
     *
     * @param key
     */
    public String get(String key) {
        Jedis jedis = getSource();
        try {
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeSource(jedis);
        }

        return null;

    }

    /**
     * 设置
     * @param key
     * @param value
     */
    public void set(byte[] key, byte[] value) {
        Jedis jedis = getSource();
        jedis.set(key, value);
        closeSource(jedis);
    }

    /**
     *
     * @param key
     * @return
     */
    public byte[]  get(byte[] key) {
        Jedis jedis = getSource();
        try {
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeSource(jedis);
        }
        return null;

    }

}

2.Jedis的API操作

使用jedis来操作redis的key和value,而value有很多种类型,和命令操作一样。

2.1.key的操作
System.out.println(jedis.keys("*"));	//查看所有的key
2.2.String的操作
System.out.println(jedis.set("name","zhangsan"));//新增
System.out.println(jedis.get("name"));//获取
System.out.println(jedis.set("name","zhangsan1"));//修改
System.out.println(jedis.get("name"));//获取
System.out.println(jedis.del("name"));//删除
2.3.List的操作
jedis.lpush("students1","1","2","3","5","6");	//添加数据
System.out.println(jedis.lrange("students1", 0, 3));	//获取数据
  • 排序操作,对于数字元素排序不需要加SortingParams,对于字符串需要指定SortingParams,并且指定使用assii值排序
//对数字排序
jedis.flushDB();
//添加数据
jedis.lpush("nums","1","2","3","4","8","5","3","1");
//排序
System.out.println(jedis.sort("nums"));
//指定排序方式
SortingParams sortingParams1 = new SortingParams();
sortingParams1.desc();
System.out.println(jedis.sort("nums", sortingParams1));

//对字母排序
jedis.lpush("langues","java","php","c++","test","ui");
SortingParams sortingParams = new SortingParams();
sortingParams.alpha();
sortingParams.desc();

System.out.println(jedis.sort("langues",sortingParams));
2.4.Set的操作
jedis.sadd("students2","a1","b1","c1","b1");		//添加数据
System.out.println(jedis.smembers("students2"));	//获取成员
2.5.Hash操作
jedis.hset("students3","student","zhangsan");			//添加数据
System.out.println(jedis.hget("students3", "student"));	 //获取数据
2.6.事务操作
jedis.set("name","zs");
jedis.set("age",18);

//开启事务
Transaction multi = jedis.multi();
multi.incr("name");
multi.incr("age");
//提交事务
List<Object result = multi.exec();
System.out.println(result);

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

//关闭连接池
pool.destory();

3 springBoot spring data redis

Spring data:spring对数据操作支持规范,但是我们的数据是多种类型,比如rdbms(msyql)、redis(Nosql)、es、mq

Spring data redis :spring操作redis

Spring data jpa :操作关系型数据库

springBoot spring data redis:简化springdata对redis的配置

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>

        <!--导入spring data redis 的jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
spring:
  redis:
    host: localhost
    port: 6379
    password: 123456
    jedis:
      pool:
        max-idle: 8
        max-active: 8
        max-wait: 2000

序列化Redis

    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory){
        System.out.println("创建Bean===>RedisTemplate");
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());//key 使用String序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        System.out.println("创建Bean完成===>"+redisTemplate);
        return redisTemplate;
    }

工具类

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {
  @Resource
  private RedisTemplate redisTemplate;
  /**
   * 测试RedisTemplat Bean是否挂载到Spring容器
   */
  public void sout(){
    System.out.println("RedisTemplat 对象===>"+redisTemplate);
  }
  /**
   * 给一个指定的 key 值附加过期时间
   *
   * @param key
   * @param time
   * @return
   */
  public boolean expire(String key, long time) {
    return redisTemplate.expire(key, time, TimeUnit.SECONDS);
  }

  /**
   * 根据key 获取过期时间
   *
   * @param key
   * @return
   */
  public long getTime(String key) {
    return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  }

  /**
   * 根据key 获取过期时间
   *
   * @param key
   * @return
   */
  public boolean hasKey(String key) {
    return redisTemplate.hasKey(key);
  }

  /**
   * 移除指定key 的过期时间
   *
   * @param key
   * @return
   */
  public boolean persist(String key) {
    return redisTemplate.boundValueOps(key).persist();
  }

  //- - - - - - - - - - - - - - - - - - - - -  String类型 - - - - - - - - - - - - - - - - - - - -

  /**
   * 根据key获取值
   *
   * @param key 键
   * @return 值
   */
  public Object get(String key) {
    return key == null ? null : redisTemplate.opsForValue().get(key);
  }

  /**
   * 将值放入缓存
   *
   * @param key   键
   * @param value 值
   * @return true成功 false 失败
   */
  public void set(String key, String value) {
    redisTemplate.opsForValue().set(key, value);
  }

  /**
   * 将值放入缓存并设置时间
   *
   * @param key   键
   * @param value 值
   * @param time  时间(秒) -1为无期限
   * @return true成功 false 失败
   */
  public void set(String key, String value, long time) {
    if (time > 0) {
      redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    } else {
      redisTemplate.opsForValue().set(key, value);
    }
  }

  /**
   * 批量添加 key (重复的键会覆盖)
   *
   * @param keyAndValue
   */
  public void batchSet(Map<String, String> keyAndValue) {
    redisTemplate.opsForValue().multiSet(keyAndValue);
  }

  /**
   * 批量添加 key-value 只有在键不存在时,才添加
   * map 中只要有一个key存在,则全部不添加
   *
   * @param keyAndValue
   */
  public void batchSetIfAbsent(Map<String, String> keyAndValue) {
    redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);
  }

  /**
   * 对一个 key-value 的值进行加减操作,
   * 如果该 key 不存在 将创建一个key 并赋值该 number
   * 如果 key 存在,但 value 不是长整型 ,将报错
   *
   * @param key
   * @param number
   */
  public Long increment(String key, long number) {
    return redisTemplate.opsForValue().increment(key, number);
  }

  /**
   * 对一个 key-value 的值进行加减操作,
   * 如果该 key 不存在 将创建一个key 并赋值该 number
   * 如果 key 存在,但 value 不是 纯数字 ,将报错
   *
   * @param key
   * @param number
   */
  public Double increment(String key, double number) {
    return redisTemplate.opsForValue().increment(key, number);
  }

  //- - - - - - - - - - - - - - - - - - - - -  set类型 - - - - - - - - - - - - - - - - - - - -

  /**
   * 将数据放入set缓存
   *
   * @param key 键
   * @return
   */
  public void sSet(String key, String value) {
    redisTemplate.opsForSet().add(key, value);
  }

  /**
   * 获取变量中的值
   *
   * @param key 键
   * @return
   */
  public Set<Object> members(String key) {
    return redisTemplate.opsForSet().members(key);
  }

  /**
   * 随机获取变量中指定个数的元素
   *
   * @param key   键
   * @param count 值
   * @return
   */
  public void randomMembers(String key, long count) {
    redisTemplate.opsForSet().randomMembers(key, count);
  }

  /**
   * 随机获取变量中的元素
   *
   * @param key 键
   * @return
   */
  public Object randomMember(String key) {
    return redisTemplate.opsForSet().randomMember(key);
  }

  /**
   * 弹出变量中的元素
   *
   * @param key 键
   * @return
   */
  public Object pop(String key) {
    return redisTemplate.opsForSet().pop("setValue");
  }

  /**
   * 获取变量中值的长度
   *
   * @param key 键
   * @return
   */
  public long size(String key) {
    return redisTemplate.opsForSet().size(key);
  }

  /**
   * 根据value从一个set中查询,是否存在
   *
   * @param key   键
   * @param value 值
   * @return true 存在 false不存在
   */
  public boolean sHasKey(String key, Object value) {
    return redisTemplate.opsForSet().isMember(key, value);
  }

  /**
   * 检查给定的元素是否在变量中。
   *
   * @param key 键
   * @param obj 元素对象
   * @return
   */
  public boolean isMember(String key, Object obj) {
    return redisTemplate.opsForSet().isMember(key, obj);
  }

  /**
   * 转移变量的元素值到目的变量。
   *
   * @param key     键
   * @param value   元素对象
   * @param destKey 元素对象
   * @return
   */
  public boolean move(String key, String value, String destKey) {
    return redisTemplate.opsForSet().move(key, value, destKey);
  }

  /**
   * 批量移除set缓存中元素
   *
   * @param key    键
   * @param values 值
   * @return
   */
  public void remove(String key, Object... values) {
    redisTemplate.opsForSet().remove(key, values);
  }

  /**
   * 通过给定的key求2个set变量的差值
   *
   * @param key     键
   * @param destKey 键
   * @return
   */
  public Set<Set> difference(String key, String destKey) {
    return redisTemplate.opsForSet().difference(key, destKey);
  }


  //- - - - - - - - - - - - - - - - - - - - -  hash类型 - - - - - - - - - - - - - - - - - - - -

  /**
   * 加入缓存
   *
   * @param key 键
   * @param map 键
   * @return
   */
  public void add(String key, Map<String, String> map) {
    redisTemplate.opsForHash().putAll(key, map);
  }

  /**
   * 获取 key 下的 所有  hashkey 和 value
   *
   * @param key 键
   * @return
   */
  public Map<Object, Object> getHashEntries(String key) {
    return redisTemplate.opsForHash().entries(key);
  }

  /**
   * 验证指定 key 下 有没有指定的 hashkey
   *
   * @param key
   * @param hashKey
   * @return
   */
  public boolean hashKey(String key, String hashKey) {
    return redisTemplate.opsForHash().hasKey(key, hashKey);
  }

  /**
   * 获取指定key的值string
   *
   * @param key  键
   * @param key2 键
   * @return
   */
  public String getMapString(String key, String key2) {
    return redisTemplate.opsForHash().get("map1", "key1").toString();
  }

  /**
   * 获取指定的值Int
   *
   * @param key  键
   * @param key2 键
   * @return
   */
  public Integer getMapInt(String key, String key2) {
    return (Integer) redisTemplate.opsForHash().get("map1", "key1");
  }

  /**
   * 弹出元素并删除
   *
   * @param key 键
   * @return
   */
  public String popValue(String key) {
    return redisTemplate.opsForSet().pop(key).toString();
  }

  /**
   * 删除指定 hash 的 HashKey
   *
   * @param key
   * @param hashKeys
   * @return 删除成功的 数量
   */
  public Long delete(String key, String... hashKeys) {
    return redisTemplate.opsForHash().delete(key, hashKeys);
  }

  /**
   * 给指定 hash 的 hashkey 做增减操作
   *
   * @param key
   * @param hashKey
   * @param number
   * @return
   */
  public Long increment(String key, String hashKey, long number) {
    return redisTemplate.opsForHash().increment(key, hashKey, number);
  }

  /**
   * 给指定 hash 的 hashkey 做增减操作
   *
   * @param key
   * @param hashKey
   * @param number
   * @return
   */
  public Double increment(String key, String hashKey, Double number) {
    return redisTemplate.opsForHash().increment(key, hashKey, number);
  }

  /**
   * 获取 key 下的 所有 hashkey 字段
   *
   * @param key
   * @return
   */
  public Set<Object> hashKeys(String key) {
    return redisTemplate.opsForHash().keys(key);
  }

  /**
   * 获取指定 hash 下面的 键值对 数量
   *
   * @param key
   * @return
   */
  public Long hashSize(String key) {
    return redisTemplate.opsForHash().size(key);
  }

  //- - - - - - - - - - - - - - - - - - - - -  list类型 - - - - - - - - - - - - - - - - - - - -

  /**
   * 在变量左边添加元素值
   *
   * @param key
   * @param value
   * @return
   */
  public void leftPush(String key, Object value) {
    redisTemplate.opsForList().leftPush(key, value);
  }

  /**
   * 获取集合指定位置的值。
   *
   * @param key
   * @param index
   * @return
   */
  public Object index(String key, long index) {
    return redisTemplate.opsForList().index("list", 1);
  }

  /**
   * 获取指定区间的值。
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public List<Object> range(String key, long start, long end) {
    return redisTemplate.opsForList().range(key, start, end);
  }

  /**
   * 把最后一个参数值放到指定集合的第一个出现中间参数的前面,
   * 如果中间参数值存在的话。
   *
   * @param key
   * @param pivot
   * @param value
   * @return
   */
  public void leftPush(String key, String pivot, String value) {
    redisTemplate.opsForList().leftPush(key, pivot, value);
  }

  /**
   * 向左边批量添加参数元素。
   *
   * @param key
   * @param values
   * @return
   */
  public void leftPushAll(String key, String... values) {
//        redisTemplate.opsForList().leftPushAll(key,"w","x","y");
    redisTemplate.opsForList().leftPushAll(key, values);
  }

  /**
   * 向集合最右边添加元素。
   *
   * @param key
   * @param value
   * @return
   */
  public void leftPushAll(String key, String value) {
    redisTemplate.opsForList().rightPush(key, value);
  }

  /**
   * 向左边批量添加参数元素。
   *
   * @param key
   * @param values
   * @return
   */
  public void rightPushAll(String key, String... values) {
//        redisTemplate.opsForList().leftPushAll(key,"w","x","y");
    redisTemplate.opsForList().rightPushAll(key, values);
  }

  /**
   * 以集合方式向右边添加元素。
   *
   * @param key
   * @param values
   * @return
   */
  public void rightPushAll(String key, Collection<?> values) {
    redisTemplate.opsForList().rightPushAll(key, values);
  }

  /**
   * 向已存在的集合中添加元素。
   *
   * @param key
   * @param value
   * @return
   */
  public void rightPushIfPresent(String key, Object value) {
    redisTemplate.opsForList().rightPushIfPresent(key, value);
  }

  /**
   * 向已存在的集合中添加元素。
   *
   * @param key
   * @return
   */
  public long listLength(String key) {
    return redisTemplate.opsForList().size(key);
  }

  /**
   * 移除集合中的左边第一个元素。
   *
   * @param key
   * @return
   */
  public void leftPop(String key) {
    redisTemplate.opsForList().leftPop(key);
  }

  /**
   * 移除集合中左边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
   *
   * @param key
   * @return
   */
  public void leftPop(String key, long timeout, TimeUnit unit) {
    redisTemplate.opsForList().leftPop(key, timeout, unit);
  }

  /**
   * 移除集合中右边的元素。
   *
   * @param key
   * @return
   */
  public void rightPop(String key) {
    redisTemplate.opsForList().rightPop(key);
  }

  /**
   * 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
   *
   * @param key
   * @return
   */
  public void rightPop(String key, long timeout, TimeUnit unit) {
    redisTemplate.opsForList().rightPop(key, timeout, unit);
  }

}

五.Redis的持久化配置

1.Redis的持久化概念

1.1.什么是Redis持久化

因为Redis数据基于内存读写,为了防止Redis服务器关闭或者宕机造成数据丢失,我们通常需要对Redis最磁极化,即:把内从中的数据(命令)保存一份到磁盘做一个备份,当Redis服务关闭或者宕机,

在Redis服务器重启的时候会从磁盘重新加载备份的数据,不至于数据丢失。 Redis 提供了两种不同级别的持久化方式:RDB和AOF,可以通过修改redis.conf来进行配置.

开启持久配置后,对Redis进行写操作,在Redis安装目录将会看到持久文件:“appendonly.aof”和“ dump.rdb”。

1.2.Redis如何保存数据

redis为了考虑效率,保存数据在内容中.并且考虑数据安全性,还做数据持久化,如果满足保存策略,就会把内存的数据保存到数据rdb文件,还来不及保存那部分数据存放到aof更新日志中。在加载时,把两个数据做一个并集。

1.3.Redis持久化原理图
  • 保存持久化数据

在这里插入图片描述

  • 启动Redis载入持久化数据

在这里插入图片描述

1.3.Redis持久化-RDB

RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照,默认开启该模式

  • 如何关闭 rdb 模式?修改配置文件注释如下内容
save ""
# save 900 1        #至少在900秒的时间段内至少有一次改变存储同步一次
# save xxx
# save 60 10000
1.4.Redis持久化-AOF

AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集,默认关闭该模式。

  • 如何开启aof模式?修改配置文件如下内容
appendonly yes        #yes 开启,no 关闭

# appendfsync always  #每次有新命令时就执行一次fsync 

appendfsync everysec  #每秒 fsync 一次 ,这里我们启用 everysec

# appendfsync no      #从不fsync(交给操作系统来处理,可能很久才执行一次fsync)
					#其它的参数请大家看redis.conf配置文件详解

2.Redis持久配置

2.1.配置文件
  • 见:redis.windows.conf 或者 redis.conf ,Redis 版本不一样配置文件名字不一样
- bind 					# 指定的ip才能访问
- port					#默认端口
- timeout				#连接超时
- loglevel				#日志的级别
- logfile				#日志文件输出路径
- databases				#数据库数量
- save 900 1			#RDB持久方式,900 秒有一个数据修改进行持久化
- appendonly			#AOF持久方式
2.2.Redis持久化配置

根据业务情况修改配置文件中的持久化配置项目即可

2.3.数据备份与恢复【了解】

虽然配置了持久化Redis会进行自动数据备份,我们也可以通过SAVE 或者 BGSAVE (后台备份)命令创建当前数据库的备份

  • 数据备份执行如下命令
save	#该命令将在 redis 安装目录中创建dump.rdb文件。
  • 恢复数据,将dump.rdb) 移动到 redis 安装目录并启动服务即可。
  • 在Linux上获取 redis 目录可以使用 CONFIG 命令如:
CONFIG GET dir			#返回:"/usr/local/redis/bin"

3.RDB和AOP区别

3.1.RDB

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储 。

RDB优势如下:

1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高

RDB劣势如下:

如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

3.2.AOF

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

AOF优势如下:

1). 该机制可以带来更高的数据安全性,即数据持久性。

2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势如下:

1). 于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。不过生产环境其实更多都是二者结合使用的。

3.3.总结
RDB:记录数据快照
 
   优点:  
       1.产生一个持久化文件,方便文件备份 , 灾难恢复RDB是非常不错的选择
       2.Fork子进程去持久化, 性能比AOF好,文件体积更小,启动时恢复速度快
   缺点:
      1.没办法100%s数据不丢失
      2.数据集大,FORk子进程持久化时会服务器卡顿


AOF: 记录写命令
   优点:  
     1.数据更安全
     2.采用Append,即使持久的时候宕机,也不影响已经持久的数据
     3.日志过大,可以rewrite重写
     4.AOF日志格式清晰,容易理解  
 

   缺点:
      1.AOF文件通常要大于RDB文件
      2.AOF数据恢复比RDB慢
  

最佳实践:二者结合 ,RDB来数据备份,迁移,容灾  。 AOF持久化保证数据不丢失。

六.Redis的淘汰策略

1.淘汰策略基本概念

1.1.为什么要淘汰

Redis的数据读写基于内存,Redis虽然快,但是内存成本还是比较高的,而且基于内存Redis不适合存储太大量的数据。Redis可以使用电脑物理最大内存,当然我们通常会通过设置maxmemory参数现在Redis内存的使用, 为了让有限的内存空间存储更多的有效数据,我们可以设置淘汰策略,让Redis自动淘汰那些老旧的,或者不怎么被使用的数据。

redis 确定驱逐某个键值对后,会删除这个数据,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。

2.2.淘汰策略有哪些
  • volatile-lru :从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据

2.淘汰策略配置

2.1.最大内存配置
  • 修改配置redis.windows.conf,修改maxmemory,放开注释, 根据情况设置大小
maxmemory <bytes
2.2.淘汰策略配置
  • 修改配置redis.windows.conf ,修改maxmemory-policy ,放开注释,按情况修改策略
maxmemory-policy noeviction		#noeviction 为默认的策略,根据情况修改

总结

1.重点

1.1.Redis使用场景 - 4☆

1.2.Redis的特点 - 4☆

1.3.五种数据结构和常用命令 - 5☆

1.4.Java中使用Jedis - 5☆

1.5.集群和分布式相关概念 - 5☆

1.6.Redis三种集群模式 - 5☆

1.7.持久化方案 - 4☆

1.8.淘汰策略 - 4☆

3.面试题

1.项目中那些地方用到Redis,怎么用的?

2.Redis有哪些数据结构?

3.Redis怎么实现栈和队列?

4.Redis数据满了时会怎么办?

5.Redis集群方式有哪些?

6.简单说一下redis-cluster里面选举投票机制?

7.说一下Redis持久化方案?

8.说一下Redis淘汰策略?

4.相关文章

4.1.面试中关于Redis的问题看这篇就够了

https://blog.csdn.net/qq_34337272/article/details/80012284?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

4.2.10个常见的Redis面试题

https://blog.csdn.net/yangzhong0808/article/details/81196472

4.32018最全Redis面试题整理

https://blog.csdn.net/wchengsheng/article/details/79925654

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值