Redis的学习

Redis的学习

本教程基于【狂神说Java】Redis最新超详细版教程通俗易懂2020版,哔哩哔哩链接:https://www.bilibili.com/video/BV1S54y1R7SB?spm_id_from=333.999.0.0

一、NoSQL(非关系型数据库)概述

1.单机MySQL的年代

image-20210209183559508

2.memcached(缓存)+MySQL+垂直拆分(读写分离)

image-20210209183812057

3.分库分表+水平拆分+MySQL集群

m:主服务器、s:从服务器

image-20210209184255713

4.当今时代

image-20210209184719744

5.为什么要用NoSQL

image-20210209184844783

6.什么是NoSQL

image-20210209185010894

7.NoSQL特点

image-20210209185122627

image-20210209185145845

8.3V和3高

image-20210209185318260

9.NoSQL的四大分类

image-20210209185615347

image-20210209185638639

image-20210209185707096

四者对比

image-20210209185821283

二、Redis概述

image-20210201110302230

image-20210201110344789

redis中文网:http://www.redis.cn/

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

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings)散列(hashes)列表(lists)集合(sets)有序集合(sorted sets) 与范围查询, bitmapshyperloglogs地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication)LUA脚本(Lua scripting)LRU驱动事件(LRU eviction)事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

1.用Linux安装redis6

我们使用Xftp6进行连接Linux虚拟机,方便传输文件的操作,使用xshell 6连接虚拟机进行命令的操作

没有安装Linux虚拟机的可以先去看我的其他博客,点击前往

1.我们进入redis官网https://redis.io/下载redis版本,此处下载redis6版本

image-20210131193729013

2.我们新建一个文件夹usr/src/tang,将其安装包放到其目录下,注意不能建文件夹的话直接切换为root用户运行即可,之后解压redis,

新建:	mkdir 文件夹
重命名/移动文件: mv 文件名 要移动的文件夹/(重命名的文件名)
解压:		tar -zxvf 压缩包 ,z不写也可以
切换到root用户 	su 

删除文件夹实例:
rm -rf /var/log/httpd/access
将会删除/var/log/httpd/access目录以及其下所有文件、文件夹

删除文件使用实例:
rm -f /var/log/httpd/access.log
将会强制删除/var/log/httpd/access.log这个文件

进入解压后的redis目录,可以看到redis的配置文件redis.conf

image-20210131195226546

3.之后我们安装 gcc-c++ ,如果已安装则不用再安装,

查看gcc安装:	gcc -v
安装gcc:	yum install gcc-c++

之后在解压的文件夹下输入 make ,之后输入make install 进行安装redis(或者直接make && make install ),此处redis6安装会出错,因为

安装6.0以上版本需要升级gcc到5.3及以上,如下:升级到gcc 9.3:
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。
如果要长期使用gcc 9.3的话:
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
这样退出shell重新打开就是新版的gcc了

image-20210131200647258

4.redis默认安装路径在 usr/local/bin下,我们在此目录下新建一个文件夹,并将之前解压redis的配置文件redis.conf放到其下,我们之后就用这个配置文件进行启动

复制某个文件到某个文件夹	cp 某个文件 某个文件夹	

image-20210131201752880

5.redis默认不是后台启动的,我们要修改配置文件,进入redis.conf配置文件,将daemonize从no改为yes

image-20210131202338469

6.启动redis服务

显示工作目录	pwd
通过指定的配置文件启动服务:redis-server config/redis.conf  

image-20210131202709923

7.之后我们用redis-cli(redis客户端)连接进行使用redis,

redis-cli -h 127.0.0.1 -p 6379	注意:-h 默认是本机,可以省略不写

image-20210131203530570

8.查看redis进程是否开启,我们可以在用xshell连接我们的虚拟机进行查看,虚拟机ip地址查看命令:ifconfig

查看redis是否开启	ps -ef | grep redis

image-20210131204037328

9.关闭redis

输入shutdown会提示是否保存设置的key

image-20210131204220447

2.用Windows安装redis

目前windows版的最新redis版本为3.2.100(所以我们并不推荐使用windows版本的redis),下载地址:https://github.com/MSOpenTech/redis/releases

image-20210201105109037

1.解压下载的redis

image-20210201105353121

redis.conf —reids配置文件

redis-benchmark.exe —reids压测工具

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

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

redis-cli.exe —reids客户端,用来连接redis

redis-server.exe —reids服务端,用来启动redis服务

2.启动redis,双击redis.-server.exe即可,之后用redis-cli连接redis即可使用

redis.-server.exe启动界面:

image-20210201110108002

redis-cli连接界面

image-20210201105825742

3.redis-benchmark性能测试

redis-benchmark是一个压力测试工具,官方自带,在安装目录文件夹bin下,命令参数如下:

image-20210201110739909

接下来我们进行简单的测试,测试100个并发连接,每个并发发送100000个请求

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

image-20210201111437965

我们再使用xshell开一个会话,连接这个虚拟机,用来输入命令,方便操作,

image-20210201112613812

测压结果分析:对100000个请求进行写入测试耗时0.66s,每次请求100个并行的客户端,每次只写入三个字符串,保持连接的数量只有一台服务器进行连接测试,1s中处理97.73%,2s中处理99.89%,3s中处理99.98%,所有请求不到4s就处理完成了,每秒处理151515.14个请求

4.redis的基本知识

redis默认有16个数据库,我们查看redis配置文件即可知道,

image-20210201125433668

默认使用的是第 0 个(0-15),我们可以用命令进行切换数据库,如

select 5 表示切换到第5个数据库

DBSIZE   可以查看这个数据库的大小,如果没有存key值(数据),则所占大小默认为0,

keys *   查看当前数据库所有的key

flushdb  清除当前数据库数据

FLUSHALL 清除所有数据库的数据

image-20210201160257386

image-20210201161011099

image-20210201161239814

redis6.0之后是多线程的了!!!

退出redis:输入命令 SHUTDOWN 关闭连接,再输入exit 即可退出

三、五大数据类型

Redis-Key命令

关于Redis-Key的一些基本命令,注意redis-key不是数据类型,是命令

检查某个key是否存在			EXISTS name

移动某个key到某个数据库,1表示移动到第一个数据库		move name 1

设置某个key多久后过期,10表示10s后过期				 EXPIRE name 10

查看某个key还有多久过期						    ttl name

查看某个key是什么数据类型的						   type name

查看更多可以前往官网的“命令”查看

image-20210201163138741

1.String(字符串)

set、get、keys、exists、strlen、append

image-20210201164539207

注意:APPEND name 'tang' ,如果name不存在,就会自动创建一个key叫name,值为tang

redis不区分大小写命令

incr、decr、incrby、decrby

image-20210201165121956

image-20210201165212639

getrange、setrange

image-20210201170514713

setex、ttl、setnx

image-20210201170951489

mset、mget、msetnx

image-20210201171322911

对象set user:1 {name:tang,age:18}

image-20210201171618130

getset

image-20210201172027953

2.List(列表)

image-20210201193733432

lpush、lrange、rpush

image-20210201194031516

lpop、rpop

image-20210201194301401

lindex

image-20210201195156197

llen

image-20210201195303464

lrem

image-20210201195601747

ltrim

image-20210201195841339

rpoplpush

image-20210201200113274

lset

image-20210201200506153

linsert

image-20210201200826511

小结

image-20210201201055570

3.Set(集合)

set中的值是不能重复的,set的命令都是以s开头的

sadd、smembers、sismember

image-20210201201411118

scard、srem

image-20210201201635791

srandmember

image-20210201201853099

spop

image-20210201202046518

smove

image-20210201202241163

sdiff、sinter、sunion

image-20210201202651917

4.Hash(哈希)

hset、hget、hmset、hmget、hgetall、hdel

以h开头,

image-20210201215220361

hlen

image-20210201215451476

hexists

image-20210201215542382

hkeys、hvals

image-20210201215746343

incr、decr

image-20210201220038516

5.Zset(有序集合)

zadd、zrange

image-20210201220635427

zrangebyscore、-inf、+inf、withscores

image-20210201220850789

image-20210201221639379

image-20210201221030798

zrem、zcard

image-20210201221450999

zcount

image-20210201222238358

排行榜应用实现,取Top N 测试

四、三种特殊数据类型

1.geospatial(地理位置)

geoadd

image-20210204135516777

geopos

image-20210204135745972

geodist

image-20210204140034475

georadius

image-20210204140610450

image-20210204140820319

image-20210204140918979

georadiusbymember

image-20210204141105473

image-20210204141128437

geohash

image-20210204141527032

2.hyperloglog(基数统计)

pfadd、pfcount、pfmerge

image-20210204142349402

image-20210204142641771

3.bitmaps(位图场景)

image-20210204143405377

setbit、getbit、bitcount

image-20210204143524943

image-20210204143657882

五、Redis基本的事务操作

image-20210204150931413

multi、exec、discard

image-20210204151017211

image-20210204151321458

image-20210204151625233

image-20210204151947388

redis实现乐观锁

image-20210204153058911

image-20210204153151961

image-20210204153305517

image-20210204153328054

以下模拟另一个线程修改我们的值(在执行之前exec):我们再开一个线程连接这台虚拟机并修改值

image-20210204153519389

六、通过Jedis操作Redis

1.创建maven项目,导入依赖

image-20210204155936362

2.编码测试

image-20210204160131600

常用的api

即我们上面所学的各种命令,它在jedis中就是一个个的方法

key的操作命令
package com.kuang.base;

import redis.clients.jedis.Jedis;

import java.util.Set;

public class TestKey {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        System.out.println("清空数据:"+jedis.flushDB());
        System.out.println("判断某个键是否存在:"+jedis.exists("username"));
        System.out.println("新增<'username','kuangshen'>的键值对:"+jedis.set("username", "kuangshen"));
        System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "password"));
        System.out.print("系统中所有的键如下:");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除键password:"+jedis.del("password"));
        System.out.println("判断键password是否存在:"+jedis.exists("password"));
        System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));
        System.out.println("随机返回key空间的一个:"+jedis.randomKey());
        System.out.println("重命名key:"+jedis.rename("username","name"));
        System.out.println("取出改后的name:"+jedis.get("name"));
        System.out.println("按索引查询:"+jedis.select(0));
        System.out.println("删除当前选择数据库中的所有key:"+jedis.flushDB());
        System.out.println("返回当前数据库中key的数目:"+jedis.dbSize());
        System.out.println("删除所有数据库中的所有key:"+jedis.flushAll());
    }
}

结果:

清空数据:OK
判断某个键是否存在:false
新增<'username','kuangshen'>的键值对:OK
新增<'password','password'>的键值对:OK
系统中所有的键如下:[password, username]
删除键password:1
判断键password是否存在:false
查看键username所存储的值的类型:string
随机返回key空间的一个:username
重命名key:OK
取出改后的name:kuangshen
按索引查询:OK
删除当前选择数据库中的所有key:OK
返回当前数据库中key的数目:0
删除所有数据库中的所有key:OK
对string的操作命令
package com.kuang.base;

import redis.clients.jedis.Jedis;

import java.util.concurrent.TimeUnit;

public class TestString {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        jedis.flushDB();
        System.out.println("===========增加数据===========");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3", "value3"));
        System.out.println("删除键key2:"+jedis.del("key2"));
        System.out.println("获取键key2:"+jedis.get("key2"));
        System.out.println("修改key1:"+jedis.set("key1", "value1Changed"));
        System.out.println("获取key1的值:"+jedis.get("key1"));
        System.out.println("在key3后面加入值:"+jedis.append("key3", "End"));
        System.out.println("key3的值:"+jedis.get("key3"));
        System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03","key04"));
        System.out.println("删除多个键值对:"+jedis.del("key01","key02"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));

        jedis.flushDB();
        System.out.println("===========新增键值对防止覆盖原先值==============");
        System.out.println(jedis.setnx("key1", "value1"));
        System.out.println(jedis.setnx("key2", "value2"));
        System.out.println(jedis.setnx("key2", "value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("===========新增键值对并设置有效时间=============");
        System.out.println(jedis.setex("key3", 2, "value3"));
        System.out.println(jedis.get("key3"));
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));

        System.out.println("===========获取原值,更新为新值==========");
        System.out.println(jedis.getSet("key2", "key2GetSet"));
        System.out.println(jedis.get("key2"));

        System.out.println("获得key2的值的字串:"+jedis.getrange("key2", 2, 4));
    }
}

结果:

===========增加数据===========
OK
OK
OK
删除键key2:1
获取键key2:null
修改key1:OK
获取key1的值:value1Changed
在key3后面加入值:9
key3的值:value3End
增加多个键值对:OK
获取多个键值对:[value01, value02, value03]
获取多个键值对:[value01, value02, value03, null]
删除多个键值对:2
获取多个键值对:[null, null, value03]
===========新增键值对防止覆盖原先值==============
1
1
0
value1
value2
===========新增键值对并设置有效时间=============
OK
value3
null
===========获取原值,更新为新值==========
value2
key2GetSet
获得key2的值的字串:y2G
对list的操作命令
package com.kuang.base;

import redis.clients.jedis.Jedis;

public class TestList {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("===========添加一个list===========");
        jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
        jedis.lpush("collections", "HashSet");
        jedis.lpush("collections", "TreeSet");
        jedis.lpush("collections", "TreeMap");
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));//-1代表倒数第一个元素,-2代表倒数第二个元素,end为-1表示查询全部
        System.out.println("collections区间0-3的元素:"+jedis.lrange("collections",0,3));
        System.out.println("===============================");
        // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈
        System.out.println("删除指定元素个数:"+jedis.lrem("collections", 2, "HashMap"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("删除下表0-3区间之外的元素:"+jedis.ltrim("collections", 0, 3));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections列表出栈(左端):"+jedis.lpop("collections"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections添加元素,从列表右端,与lpush相对应:"+jedis.rpush("collections", "EnumMap"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections列表出栈(右端):"+jedis.rpop("collections"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("修改collections指定下标1的内容:"+jedis.lset("collections", 1, "LinkedArrayList"));
        System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
        System.out.println("===============================");
        System.out.println("collections的长度:"+jedis.llen("collections"));
        System.out.println("获取collections下标为2的元素:"+jedis.lindex("collections", 2));
        System.out.println("===============================");
        jedis.lpush("sortedList", "3","6","2","0","7","4");
        System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
        System.out.println(jedis.sort("sortedList"));
        System.out.println("sortedList排序后:"+jedis.lrange("sortedList", 0, -1));
    }
}

结果:

===========添加一个list===========
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, HashMap, Stack, Vector, ArrayList]
collections区间0-3的元素:[TreeMap, TreeSet, HashSet, LinkedHashMap]
===============================
删除指定元素个数:1
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, Stack, Vector, ArrayList]
删除下表0-3区间之外的元素:OK
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap]
collections列表出栈(左端):TreeMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
collections添加元素,从列表右端,与lpush相对应:4
collections的内容:[TreeSet, HashSet, LinkedHashMap, EnumMap]
collections列表出栈(右端):EnumMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
修改collections指定下标1的内容:OK
collections的内容:[TreeSet, LinkedArrayList, LinkedHashMap]
===============================
collections的长度:3
获取collections下标为2的元素:LinkedHashMap
===============================
sortedList排序前:[4, 7, 0, 2, 6, 3]
[0, 2, 3, 4, 6, 7]
sortedList排序后:[4, 7, 0, 2, 6, 3]
对set的操作命令
package com.kuang.base;

import redis.clients.jedis.Jedis;

public class TestSet {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("============向集合中添加元素(不重复)============");
        System.out.println(jedis.sadd("eleSet", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("删除一个元素e0:"+jedis.srem("eleSet", "e0"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("删除两个元素e7和e6:"+jedis.srem("eleSet", "e7","e6"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("eleSet中包含元素的个数:"+jedis.scard("eleSet"));
        System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet", "e3"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e1"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e5"));
        System.out.println("=================================");
        System.out.println(jedis.sadd("eleSet1", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8"));
        System.out.println("将eleSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));//移到集合元素
        System.out.println("将eleSet1中删除e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2"));
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
        System.out.println("============集合运算=================");
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
        System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中没有
        jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集并将交集保存到dstkey的集合
        System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
    }
}

结果:

============向集合中添加元素(不重复)============
8
1
0
eleSet的所有元素为:[e8, e5, e1, e7, e2, e0, e4, e3, e6]
删除一个元素e0:1
eleSet的所有元素为:[e5, e1, e7, e2, e4, e3, e6, e8]
删除两个元素e7和e6:2
eleSet的所有元素为:[e2, e4, e3, e8, e5, e1]
随机的移除集合中的一个元素:e2
随机的移除集合中的一个元素:e1
eleSet的所有元素为:[e3, e4, e8, e5]
eleSet中包含元素的个数:4
e3是否在eleSet中:true
e1是否在eleSet中:false
e1是否在eleSet中:true
=================================
8
6
将eleSet1中删除e1并存入eleSet3中:1
将eleSet1中删除e2并存入eleSet3中:1
eleSet1中的元素:[e3, e8, e5, e7, e0, e4]
eleSet3中的元素:[e1, e2]
============集合运算=================
eleSet1中的元素:[e3, e8, e5, e7, e0, e4]
eleSet2中的元素:[e2, e4, e3, e8, e1, e0]
eleSet1和eleSet2的交集:[e4, e3, e8, e0]
eleSet1和eleSet2的并集:[e3, e8, e5, e1, e7, e2, e0, e4]
eleSet1和eleSet2的差集:[e7, e5]
eleSet4中的元素:[e3, e8, e0, e4]
对hash的操作命令
package com.kuang.base;

import redis.clients.jedis.Jedis;

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

public class TestHash {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        Map<String,String> map = new HashMap<String,String>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        //添加名称为hash(key)的hash元素
        jedis.hmset("hash",map);
        //向名称为hash的hash中添加key为key5,value为value5元素
        jedis.hset("hash", "key5", "value5");
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));//return Map<String,String>
        System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));//return Set<String>
        System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));//return List<String>
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 6));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 3));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash", "key2"));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("散列hash中键值对的个数:"+jedis.hlen("hash"));
        System.out.println("判断hash中是否存在key2:"+jedis.hexists("hash","key2"));
        System.out.println("判断hash中是否存在key3:"+jedis.hexists("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key4"));
    }
}

结果:

散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
散列hash的所有键为:[key1, key2, key5, key3, key4]
散列hash的所有值为:[value1, value2, value3, value4, value5]
将key6保存的值加上一个整数,如果key6不存在则添加key6:6
散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key6=6, key3=value3, key4=value4}
将key6保存的值加上一个整数,如果key6不存在则添加key6:9
散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key6=9, key3=value3, key4=value4}
删除一个或者多个键值对:1
散列hash的所有键值对为:{key1=value1, key5=value5, key6=9, key3=value3, key4=value4}
散列hash中键值对的个数:5
判断hash中是否存在key2:false
判断hash中是否存在key3:true
获取hash中的值:[value3]
获取hash中的值:[value3, value4]
对zset的操作命令
通过Jedis再次理解事务
package com.kuang.multi;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TestMulti {
    public static void main(String[] args) {
        //创建客户端连接服务端,redis服务端需要被开启
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "java");
        //开启事务
        Transaction multi = jedis.multi();
        //强转为string
        String result = jsonObject.toJSONString();
        try{
            //向redis存入一条数据
            multi.set("json", result);
            //再存入一条数据
            multi.set("json2", result);
            //这里引发了异常,用0作为被除数
            int i = 100/0;
            //如果没有引发异常,执行进入队列的命令,执行事务
            multi.exec();
        }catch(Exception e){
            e.printStackTrace();
            //如果出现异常,回滚,放弃事务
            multi.discard();
        }finally{
            System.out.println(jedis.get("json"));
            System.out.println(jedis.get("json2"));
            //最终关闭客户端
            jedis.close();
        }
    }
}

七、SpringBoot整合Redis

image-20210204201606003

源码分析:

image-20210204201642630

配置:

image-20210204201809195

3.编写测试代码

image-20210204201850762

以上这样在redis中输出会乱码,没有序列化,如下:

image-20210204202451375

我们要自定义一个配置类,用来处理乱码问题:

关于对象的保存:

image-20210204202722853

配置文件如下:

package com.kuang.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public final class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * 指定缓存失效时间
     * @param key  键
     * @param time 时间(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================String=============================

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

    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @param key   键
     * @param delta 要增加几(大于0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * @param key   键
     * @param delta 要减少几(小于0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================

    /**
     * HashGet
     * @param key  键 不能为null
     * @param item 项 不能为null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     * @param key 键
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

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

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */

    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

八、Redis配置文件详解

单位

image-20210204203115222

注意:配置文件对大小写不敏感

包含

image-20210204203332185

网络

image-20210204203658486

通用 GENERAL

image-20210204203732104

LOGO即我们运行redis服务时显示的logo

快照

image-20210204204154188

replication 复制

后面的主从复制在进行讲解

security 安全

第一种方法:

image-20210209142857915

第二种方法:

image-20210209142954573

解除密码:config set requirepass ''

设置后保存输入 save 命令即可

cllients 限制

image-20210209143301356

append only 模式 aof配置

aof:append only file

image-20210209143611028

九、持久化

持久化之rdb操作

rdb(Redis DataBase),在主从复制中,rdb为备用,从机上面

image-20210209145039427

image-20210209145138087

image-20210209145241570

之后我们设置5个key,会生成一个dump.rdb文件,在usr/local/bin目录下,rm -rf 文件名 可删除文件

image-20210209145753494

image-20210209145918663

持久化之aof操作

aof(append only file),将我们所有的命令记录下来,恢复的时候就把这个文件全部再执行一遍

image-20210209150225211

image-20210209150520350

append

image-20210209150552752

image-20210209150616052

redis-check-aof

image-20210209151133975

这个工具在usr/local/bin下,ls -l 命令:显示这个目录文件的详细信息

aof优点和缺点

image-20210209152810192

重写规则说明

image-20210209151849557

表示aof文件大于64mb时,redis会fork一个新的进程来将我们的文件进行重写来将我们变小,如将一些一样的命令进行合并

两种持久化对比

image-20210209152357859

image-20210209152459095

十、Redis发布订阅

image-20210209153441393

image-20210209153501349

命令

image-20210209153630746

测试:

两个虚拟机都连接上

image-20210209154030256

原理

image-20210209154652890

image-20210209154710378

image-20210209154619836

十一、Redis集群环境搭建

redis主从复制

image-20210209155100562

image-20210209155213204

image-20210209155245540

image-20210209155336602

环境配置

image-20210209160047773

复制命令:cp 要复制的文件名 新复制的文件名,我们这里复制三份,端口分别为6379、6380、6381,之后分别修改复制的三份配置文件,如下:

image-20210209160732293

image-20210209160336357

在三台服务器中分别输入命令,通过配置文件启动redis:进入usr/local/bin目录输入 redis-server config/redis.conf

一主二从

默认情况下,每天redis服务器都是主节点,我们一般只用配置从机即可,我们让6379这个端口的redis服务器为主机,6380、6381这两个端口的为从机,分别在要指定为从机的服务器上输入命令 :

image-20210209163044580

主机中查看:

image-20210209163217883

真实的主从配置应该在配置文件中进行配置,这样才是永久的,如果只是命令的形式,那他是暂时的,redis重启后不生效

配置文件的形式:

image-20210209163408432

注意事项

image-20210209163756190

image-20210209163859107

主机断开连接,从机还是认这个老大的,主机重启后能得到原来的key

通过命令的形式,从机断开连接,重启后变为主机,不再认以前的老大

宕机后手动配置主机

层层链路

image-20210209165000026

以上两种方法通常我们都不会使用

image-20210209165702916

哨兵模式

image-20210209170027362

image-20210209170120243

image-20210209170218842

image-20210209170351641

测试:

image-20210209171239267

image-20210209171404385

哨兵启动日志:

image-20210209171547623

哨兵选出新的主节点:

image-20210209171656049

哨兵投票的日志:

image-20210209171812788

注意:如果主机宕机后回来了,只能归并到新选出的主节点下,变为从机,这就是哨兵模式的规则

哨兵模式优缺点

image-20210209172138223

哨兵模式的全部配置

image-20210209172249253

image-20210209172336053

image-20210209172412664

image-20210209172441214

image-20210209172518444

十二、缓存穿透和雪崩

image-20210209180626350

image-20210209180917432

缓存穿透(查不到)

image-20210209180515327

image-20210209181034669

缓存空对象

image-20210209181121956

image-20210209180534577

缓存击穿(查到但量太大,缓存过期)

如微博热搜

image-20210209181400427

image-20210209181722123

缓存雪崩

image-20210209181841218

image-20210209182106775

image-20210209182320568

喜欢请关注我

至此,我们的Redis的学习就告一段落了,喜欢我的话可以关注我的微信公众号 我爱学习呀嘻嘻 ,不定期分享各类资源哦。

image-20211108230322493

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值