当前位置:   article > 正文

redis知识集锦_lpush lpop 是队列还是栈

lpush lpop 是队列还是栈

redis知识集锦

1、nosql

1、背景

历程:单机mysql(x) -->x(读写分离) -->x + memcache缓存(发展历程:优化数据结构和索引–>文件缓存(IO)–>memcache缓存)–>mysql集群

原因:mysql关系型数据库很难存放一些特殊数据,比如个人信息、社交网络、地理位置等,这些数据不需要固定的格式来存储。

2、定义

什么是nosql?
nosql = not only sql(不仅仅sql)。泛指非关系型数据库,随着web 2.0的到来,传统的关系型数据库很难支撑下去,尤其出现超大规模的高并发的社区论坛!nosql在当今大数据环境下发展迅速,redis是发展最快的,是当下我们必须要掌握的技术!

3、特点

1、方便扩展,数据之间没有关系。

2、大数据量高性能,比如redis一秒可以写8w次,读11w次。nosql的缓存记录级,是一种细粒度的缓存,性能会比较高。

3、数据类型是多样性的(不需要事先设计数据库)。

4、传统RDBMS和nosql。

传统的rdbms(关系型数据库管理系统)

1、结构化组织
2、sql
3、数据和关系都存在单独的表中
4、严格的一致性
5、基础的事务

nosql

1、不仅仅是数据
2、没有固定的查询语言
3、键值对存储、列存储、文档存储、图形数据库(社交关系)
4、最终一致性
5、CAD定理和BASE
6、高性能、高可用、高可扩

4、3V3高

3V:多样(Variety)、海量(Volume)、实时(Velocity)
3高:高性能、高并发、高可扩

2、nosql四大分类

1、KV键值对

redis、memcache、tair

2、列存储

HBase、分布式文件系统

3、文档型数据库

MongoDB、ConthDB

4、图形数据库

存的是关系,而不是图形。
Neo4j、inoGrid

3、redis概述

1、什么是redis?

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

2、redis用来干嘛?

1、内存存储、持久化(aof、rdb)
2、高速缓存、效率高
3、发布订阅
4、地图信息分析
5、计时器、计数器

3、特性

1、多样的数据类型
2、持久化
3、集群
4、事务

4、redis安装

1、window环境安装

下载链接:redis安装包下载

在这里插入图片描述

1、双击redis-server.exe打开redis服务端,最小化

2、双击redis-cli.exe打开redis客户端,输入ping回车,显示pong即连接成功。

在这里插入图片描述
key-value键值对存储,存值set key value,取值get key

2、linux环境安装

1、下载压缩包linux版压缩包下载

2、使用xftp传输到linux(我是使用阿里云的服务器)
在这里插入图片描述
3、程序一般放在/opt,移动过去并解压压缩包

移动过去 mv redis-7.0.8.tar.gz /opt

切换目录cd /opt

解压 tar -zxvf redis-7.0.8.tar.gz

4、进到redis目录,安装基本环境 yum install gcc-c++

5、在当前目录 make , 完毕后可以再确认一下 make install(可有可无)

6、安装的redis在/usr/local/bin,如图所示:

在这里插入图片描述
7、为了安全,拷贝一份redis.conf,用备用的redis.conf,出问题了能复原

在这里插入图片描述
8、redis默认不是后台运行,修改配置文件,让他可以在后台运行,找到daemonize no,把他改成yes

在这里插入图片描述

9、启动redis-server,通过刚刚修改的配置文件启动 redis-server pconfig/redis.conf

10、启动redis-cli,测试能不能连通 redis-cli -p 6379

在这里插入图片描述
11、查看redis进程

开另一个终端 ps -ef|grep redis

在这里插入图片描述

12、关闭redis shutdownexit回到命令行

3、性能测试

在这里插入图片描述

5、redis基础命令知识

1、redis默认有16个数据库,默认选择第一个

在这里插入图片描述

2、select 用来切换数据库

在这里插入图片描述

3、keys * 用來查看当前数据库的所有key

在这里插入图片描述

4、DBSIZE 用来查看当前数据库的大小

在这里插入图片描述

5、flushdb 用来清空当前数据库

在这里插入图片描述

6、flushall 用来清空所有数据库

在这里插入图片描述

7、exists 用来判断key是否存在

在这里插入图片描述

8、move 用来移除key

在这里插入图片描述
后面的1代表当前数据库

9、expire 用来设置key的过期时间

在这里插入图片描述

10、ttl 用来查询key的剩余时间

如上图

11、type 用来查看key的类型

在这里插入图片描述

12、官网命令查询,不会的来搜索

在这里插入图片描述

6、redis数据类型

摘要:官网介绍的redis
在这里插入图片描述
翻译:Redis是一个开源(BSD许可)的内存数据结构存储,用作数据库缓存消息代理流引擎。Redis提供了字符串(string)、哈希(hash)、列表(list)、集合(set)、带范围查询的排序集合(zset)、位图、超日志、地理空间索引和流等数据结构。Redis具有内置复制、Lua脚本、LRU逐出、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供高可用性。

1、String

1、append 给字符串加长

如果key不存在,就新建一个
在这里插入图片描述

2、strlen 用来计算字符串的长度

在这里插入图片描述

3、incr 加1,decr 减1, incrby 按步长加, decrby 按步长减

在这里插入图片描述

4、getrange 用来截取字符串,和java的substring一样

在这里插入图片描述
当end是-1时,返回整个字符串

5、setrange 用来替换指定区间字符串,和java的replace一样

在这里插入图片描述

6、setex 创建一个过期时间的值, setnx 当key不存在时创建,否则不创建

在这里插入图片描述

7、mset 批量写, mget 批量读

在这里插入图片描述

8、msetnx 有原子性,同时成功或同时失败

在这里插入图片描述

9、 mset加: 写json对象, mget 读json对象

在这里插入图片描述

10、getset 先读再写

在这里插入图片描述

2、List(有序可重复,命令l开头)

1、lpush 从左边添加, rpush 从右边添加,index从左到右

在这里插入图片描述

2、lrange 读list的值

如上图,从index0开始读,0到-1表示读全部(和getrange一样)

3、lpop 从左边移出, rpop 从右边移出(命令 key count)

count 表示数量
在这里插入图片描述

4、llen 计算list的长度

在这里插入图片描述

5、lindex 读指定index的值

在这里插入图片描述

6、lrem 移出指定个数的value

在这里插入图片描述

7、ltrim 截取指定区间的值

在这里插入图片描述

意思是只要index 3到4的值

8、rpoplpush 弹出最右边的一个元素,加到一个新的list中

在这里插入图片描述

9、lset 替换指定index的值(前提要存在,否则报错)

在这里插入图片描述

10、exists 判断list是否存在(和string一样)

在这里插入图片描述

11、linsert 从list的某个元素(value)的前面或者后面插入值

温馨提示:图中before和after后面的2是value,而不是index。

在这里插入图片描述

12、将list的值全部移除,相当于key也不存在了

在这里插入图片描述

13、list相当于链表,lpush rpop是队列, lpush lpop是栈

可以用作消息队列、栈等。

3、Set(无序不重复集合,命令s开头)

1、sadd 加一个元素(不是原子性操作)

在这里插入图片描述

2、smembers 查看set的所有元素

如上图

3、sismember 判断set是否包含指定元素

在这里插入图片描述

4、srem 删除指定一个或多个元素

在这里插入图片描述

5、scard 返回set的元素个数

在这里插入图片描述

6、srandmember 抽取set的指定个数的元素

在这里插入图片描述

7、spop 随机删除一个或多个元素

在这里插入图片描述

8、smove 将一个元素从一个set移动到另一个set

在这里插入图片描述

9、sdiff 多个集合的差集,以第一个对比

在这里插入图片描述

10 sinter 集合间的交集

在这里插入图片描述

11、 sunion 集合间的并集

在这里插入图片描述

4、Hash(key-map本质和string没啥区别)

1、hset 设置key的值,可以同时设置多个field

在这里插入图片描述

2、hget 读取key的一个field的值

在这里插入图片描述

3、hmset 同时设置key的多个field,hmget 读取多个field值

在这里插入图片描述

4、hgetall 读取key的所有field-value

在这里插入图片描述

5、hexists 判断key的field是否存在

在这里插入图片描述

6、hdel 删除key的若干个field的field-value

在这里插入图片描述

7、hlen 返回key的field-value的对数

在这里插入图片描述

8、hkeys 返回key的所有field值

在这里插入图片描述

9、hvals 返回key的所有value值

在这里插入图片描述

10、hincrby 加指定值(可以是正数,也可以是负数)

在这里插入图片描述

11、hsetnx 不存在就写,存在就不写

在这里插入图片描述

12、Hash经常用于存储对象,string经常用于存字符串

在这里插入图片描述

5、Zset(有序集合)

1、zadd value前加个score,标记以便排序

在这里插入图片描述

2、zrangebyscore 对zset按区间内score升序,加withscores表示同时读取score

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

3、zrevrange 降序,withscores表示是否包含score

在这里插入图片描述

4、zcard 返回zset的元素个数

在这里插入图片描述

5、zrem 移除指定元素

在这里插入图片描述

6、zcount 返回区间的元素个数(按score值)

在这里插入图片描述

7、redis特殊数据类型

1、Geospatial(地理位置)

1、geoadd key 经度 纬度member(添加经纬度)

在这里插入图片描述

温馨提示:有效的经度从-180度到180度。

有效的纬度从-85.05112878度到85.05112878度。

2、geopos key member(返回元素的经纬度)

在这里插入图片描述

3、geodist 返回两个给定位置之间的距离

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。

GEODIST 默认使用米作为单位。

在这里插入图片描述

4、georadius key 经纬度 半径 单位【可选withdist 距离、withchrood 经纬度、count 数量、asc/desc 按距离排序】

在这里插入图片描述

5、georadiusbymember 找出指定元素周围的元素

在这里插入图片描述

6、geohash 返回元素的11位字符串,hash表示

在这里插入图片描述

7、底层实际为 Zset, 可以用zset命令操作geo

在这里插入图片描述

2、Hyperloglog(有大概0.81%的错误)

1、pfadd 添加元素(不重复)

在这里插入图片描述

2、pfcount 计算数量

在这里插入图片描述

3、pfmerge 返回并集

在这里插入图片描述

4、hyperloglog省内存,允许容错的首选。

3、Bitmaps(两个状态的都可以使用)

1、setbit 写入某人一周的登录状态

在这里插入图片描述

2、getbit 查看某人星期四有没有登录

在这里插入图片描述

3、bitcount 计算登录的天数【可选(index区间)】

在这里插入图片描述

8、redis事务操作

1、redis事务的概念

redis单条命令具有原子性,redis事务不保证原子性

redis事务不具有隔离性的概念

redis事务具有一次性、顺序性、排他性

一次性:事务执行的时候命令一次性被执行

排他性:事务在执行时不能被其他事务干扰

顺序性:事务中的命令被序列化,按照入队顺序执行

2、redis事务的执行流程及命令

redis事务流程

1、开启事务(multi)

2、命令入队

3、执行事务(exec)

在这里插入图片描述

3、编译型错误(redis事务所有命令都不会执行)

在这里插入图片描述

4、运行时错误(redis事务会执行没错的命令)

在这里插入图片描述

5、事务放弃(discard)

在这里插入图片描述

9、redis实现乐观锁

乐观锁:认为任何时候都不会出问题,不加锁。更新数据的时候判断下是否被修改过。(redis中的watch)

悲观锁:认为任何时候都会出问题,加锁,性能十分低。

如图所示,money没有被修改过,事务能正常执行。

在这里插入图片描述


启动另一个redis客户端2,在客户端1事务还没执行时。修改money的值,客户端1事务提交不成功。客户端1会解锁,拿到最新的值后再次加锁,执行事务。直至事务执行成功!

客户端1

在这里插入图片描述
客户端2
在这里插入图片描述

10、jedis操作redis

1、导入依赖

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>2.0.3</version>
    </dependency>
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>4.3.0</version>
    </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2、测试连接

运行本机redis-server

在这里插入图片描述

3、jedis操作redis命令

1、String

        System.out.println("包含k1吗?---" + jedis.exists("k1"));
        System.out.println("set键值对(k1, v1)---" + jedis.set("k1", "v1"));
        System.out.println("k1的值=" + jedis.get("k1"));
        System.out.println("批量创建键值对(k2, v2; k3, v3)" + jedis.mset("k2", "v2", "k3", "v3"));
        System.out.println("读取k1、k2、k3的值:" + jedis.mget("k1", "k2", "k3"));
        System.out.println("查看k1,index:0-1:" + jedis.getrange("k1", 0, 1));
        System.out.println("查看整个k1:" + jedis.getrange("k1", 0, -1));
        System.out.println("更新k1的值为v6---" + jedis.setrange("k1", 0, "v6"));
        System.out.println("读取k5,更新为v5---" + jedis.getSet("k5", "v5"));
        System.out.println("读取k5,更新为v6---" + jedis.getSet("k5", "v6"));
        System.out.println("写入k8,过期时间为10s---" + jedis.setex("k8", 10, "v8"));
        System.out.println("查看k8的剩余时间:" + jedis.ttl("k8"));
        System.out.println("设置k1的过期时间---" + jedis.expire("k1", 10));
        System.out.println("如果k9不存在,写入v9---" + jedis.setnx("k9", "v9"));
        System.out.println("如果k9不存在,写入v9---" + jedis.setnx("k9", "v9"));
        System.out.println("设置age:20---" + jedis.set("age", "20"));
        System.out.println("age+1:---" + jedis.incr("age"));
        System.out.println("age+10:" + jedis.incrBy("age", 10));
        System.out.println("age-1:" + jedis.decr("age"));
        System.out.println("age-5:" + jedis.decrBy("age", 5));
        System.out.println("" + jedis.append("k5", ",hello"));
        System.out.println("k5的长度---" + jedis.strlen("k5"));
        System.out.println("删除k1、k2---" + jedis.del("k1", "k2"));
        System.out.println("查看所有的key:" + jedis.keys("*"));
        System.out.println("清空数据库---" + jedis.flushDB());
        System.out.println("查看所有的key:" + jedis.keys("*"));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在这里插入图片描述

2、list

        System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("从左边加:" + jedis.lpush("list1","5", "8", "1", "8", "1"));
        System.out.println("从右边加:" + jedis.rpush("list1", "10", "4"));
        System.out.println("list1是否存在:" + jedis.exists("list1"));
        System.out.println("查看list1的元素:" + jedis.lrange("list1", 0, -1));
        System.out.println("从左边移出1个:" + jedis.lpop("list1"));
        System.out.println("从右边移出1个(redis6.2以下版本不支持取多个):" + jedis.rpop("list1"));
        System.out.println("将最右边一个移出,添加到list2:" + jedis.rpoplpush("list1", "list2"));
        System.out.println("查看list1的元素个数:" + jedis.llen("list1"));
        System.out.println("查看list2的元素:" + jedis.lrange("list2", 0, -1));
        System.out.println("读取list1的index=2的值:" + jedis.lindex("list1", 2));
        System.out.println("移出指定个数的元素值:" + jedis.lrem("list1", 2, "1"));
        System.out.println("截取1 - 2的元素:" + jedis.ltrim("list1", 1, 2));
        System.out.println("将index值为1的元素值替换成10:" + jedis.lset("list1", 1, "10"));
        System.out.println("将15插在index 0的前面:()" + jedis.linsert("list1", ListPosition.BEFORE, "0", "15"));
        System.out.println("将51插在index 0的后面:" + jedis.linsert("list1", ListPosition.AFTER, "0", "51"));
        System.out.println("查看list1的元素:" + jedis.lrange("list1", 0, -1));
        System.out.println("查看所有的key:" + jedis.keys("*"));
        System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("查看所有的key:" + jedis.keys("*"));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

3、set

        System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("添加值:" + jedis.sadd("set1", "1", "2", "3", "4", "5"));
        System.out.println("移出值:" + jedis.srem("set1", "2"));
        System.out.println("查看set1:" + jedis.smembers("set1"));
        System.out.println("返回2个随机成员:" + jedis.srandmember("set1", 2));
        System.out.println("返回set1的元素个数:" + jedis.scard("set1"));
        System.out.println("判断5在不在set1:" + jedis.sismember("set1", "5"));
        System.out.println("随机删除2个成员:" + jedis.spop("set1", 2));
        System.out.println("将一个member从set1移到set2:" + jedis.smove("set1", "set2", "3"));
        System.out.println("查看set2:" + jedis.smembers("set2"));
        System.out.println("添加值到set3:" + jedis.sadd("set3", "3", "4", "5", "6", "7"));
        System.out.println("求set1和set3的差集:" + jedis.sdiff("set1", "set3"));
        System.out.println("查看set1:" + jedis.smembers("set1"));
        System.out.println("求set1和set3的交集:" + jedis.sinter("set1", "set3"));
        System.out.println("求set1和set3的并集:" + jedis.sunion("set1", "set3"));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

4、hash

        System.out.println("清空数据库:" + jedis.flushDB());
        HashMap<String, String> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        System.out.println("添加值:" + jedis.hset("hash1", "name", "zhangsan"));
        System.out.println("添加值:" + jedis.hset("hash1", "age", "24"));
        System.out.println("读取值:" + jedis.hget("hash1", "age"));
        System.out.println("读取值:" + jedis.hmset("hash1", map));
        System.out.println("查看所有的key-value:" + jedis.hgetAll("hash1"));
        System.out.println("判断field是否存在:" + jedis.hexists("hash1", "name"));
        System.out.println("查看hash的键值对数:" + jedis.hlen("hash1"));
        System.out.println("查看hash的field的长度:" + jedis.hstrlen("hash1", "name"));
        System.out.println("删除key的若干个field的field-value:" + jedis.hdel("hash1", "name", "sex"));
        System.out.println("查看所有的key-value:" + jedis.hgetAll("hash1"));
        System.out.println("返回key的所有field值:" + jedis.hkeys("hash1"));
        System.out.println("返回key的所有value值:" + jedis.hvals("hash1"));
        System.out.println("加指定值(可以是正数,也可以是负数):" + jedis.hincrBy("hash1", "age", 10));
        System.out.println("不存在就写,存在就不写(密码):" + jedis.hsetnx("hash1", "pwd", "123"));
        System.out.println("不存在就写,存在就不写(密码):" + jedis.hsetnx("hash1", "pwd", "123"));
        System.out.println("查看所有的key-value:" + jedis.hgetAll("hash1"));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

5、zset

        System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("添加值:" + jedis.zadd("zset1", 500, "z1"));
        System.out.println("添加值:" + jedis.zadd("zset1", 1000, "z2"));
        System.out.println("添加值:" + jedis.zadd("zset1", 700, "z3"));
        System.out.println("添加值:" + jedis.zadd("zset1", 2000, "z4"));
        System.out.println("添加值:" + jedis.zadd("zset1", 1500, "z5"));
        System.out.println("添加值:" + jedis.zadd("zset1", 800, "z6"));
        System.out.println("查看所有的值:" + jedis.zrange("zset1", 0, -1));
        System.out.println("移出值:" + jedis.zrem("zset1", "z1", "z4"));
        System.out.println("查看所有的值:" + jedis.zrange("zset1", 0, -1));
        System.out.println("降序:" + jedis.zrevrangeWithScores("zset1", 0, -1));
        System.out.println("返回zset1的元素个数:" + jedis.zcard("zset1"));
        System.out.println("升序(含scores):" + jedis.zrangeByScoreWithScores("zset1", "-inf", "+inf", 0, 5));
        System.out.println("升序:" + jedis.zrangeByScore("zset1", "-inf", "+inf", 0, 5));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

6、geospatial

        System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("添加值:" + jedis.geoadd("geo1", 110.55, 23.55, "beijing"));
        System.out.println("添加值:" + jedis.geoadd("geo1", 130.55, 28.55, "xian"));
        System.out.println("添加值:" + jedis.geoadd("geo1", 115.55, 25.16, "guangzhou"));
        System.out.println("查看值:" + jedis.geopos("geo1",  "guangzhou"));
        System.out.println("两个位置的距离:" + jedis.geodist("geo1", "beijing", "guangzhou", GeoUnit.KM));
        System.out.println("以guangzhou为圆心,求与其他位置的距离:" + jedis.georadiusByMember("geo1", "guangzhou", 1000, GeoUnit.KM));
        System.out.println("以某个位置为圆心,求与其他位置的距离:" + jedis.georadius("geo1", 105.0, 25, 1000, GeoUnit.KM));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

7、hyperloglog

		System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("添加数据:" + jedis.pfadd("key1", "1", "2", "3", "4"));
        System.out.println("添加数据:" + jedis.pfadd("key2", "3", "4", "5", "6"));
        System.out.println("key1、key2的元素个数是:(不重复)" + jedis.pfcount("key1", "key2"));
        System.out.println("key1和key2的并集是:" + jedis.pfmerge("key3", "key1", "key2"));
        System.out.println("key3的元素个数是:" + jedis.pfcount("key3"));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

8、bitmap

 System.out.println("清空数据库:" + jedis.flushDB());
        System.out.println("星期一:" + jedis.setbit("key1", 0, true));
        System.out.println("星期二:" + jedis.setbit("key1", 1, false));
        System.out.println("星期三:" + jedis.setbit("key1", 2, true));
        System.out.println("星期四:" + jedis.setbit("key1", 3, true));
        System.out.println("星期五:" + jedis.setbit("key1", 4, false));
        System.out.println("星期六:" + jedis.setbit("key1", 5, true));
        System.out.println("星期日:" + jedis.setbit("key1", 6, true));
        System.out.println("星期四:" + jedis.getbit("key1", 3));
        System.out.println("这星期登录了多少天:" + jedis.bitcount("key1"));
        System.out.println("清空数据库:" + jedis.flushDB());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

4、断开连接

jedis.close();
  • 1

11、springboot集成redis

1、springboot的小知识

springboot操作数据:spring-data redis jpa mongodb jdbc

springboot 2.x及以上版本已经将lettuce替换成jedis

jedis:采用直连,多线程下不安全,使用 jedis pool连接池又会造成性能低的问题。

lettuce:采用 netty,实例可被多个线程共享,线程安全, 性能高。
在这里插入图片描述

2、学会看源码

springboot配置类都会有一个自动配置类,而自动配置类都会绑定

一个.properties文件。找到文件,了解底层是如何实现的,方便自

定义template。

在这里插入图片描述

在这里插入图片描述

如下图所示:3.0.2的springboot版本已经不推荐以前的写法了。

在这里插入图片描述

正确写法如下图:许多配置自己查看并测试

在这里插入图片描述

3、整合测试

1、导入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

2、配置连接

spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
  • 1
  • 2

3、测试

//        redisTemplate.opsForValue();操作字符串
//        redisTemplate.opsForList();操作list
//        redisTemplate.opsForSet();操作set
//        redisTemplate.opsForHash();操作hash
//        redisTemplate.opsForZSet();操作zset
//        redisTemplate.opsForGeo();操作geo
//        redisTemplate.opsForHyperLogLog();操作hyperloglog
        
redisTemplate.opsForValue().set("name", "zhangsan");
System.out.println(redisTemplate.opsForValue().get("name"));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

测试完成
在这里插入图片描述

4、自定义redisTemplate

1、为什么要自定义?
1、原因1

在这里插入图片描述
使用redis存储java对象时,如果java对象没有序列化,会报错,如

下图。

在这里插入图片描述

虽然可以在User类实现序列化接口, 也可以用fastjson进行序列化

,但是这种方式不推荐,每个对象都要实现接口或者使用jackson序

列化。推荐自定义template。

2、原因2

redis默认使用jdk序列化

在这里插入图片描述

在这里插入图片描述

如图所示:key需要转义。

2、编写自定义template

1、自己写一个config类,并注解为配置类。

@Bean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        //jackson的序列化配置
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        //String的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //String的key采用string序列化
        template.setKeySerializer(stringRedisSerializer);
        //Hash的key采用string序列化
        template.setHashKeySerializer(stringRedisSerializer);
        //String的value采用jackson序列化
        template.setValueSerializer(serializer);
        //Hash的value采用jackson序列化
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();

        return template;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2、注入redisTemplate时注意导入这个自定义template。

3、测试自定义template

在这里插入图片描述

在这里插入图片描述

5、编写redisUtil

代码太长,百度就有。

12、了解redis.conf

1k => 1000 bytes
1kb => 1024 bytes
1m => 1000000 bytes
1mb => 10241024 bytes
1g => 1000000000 bytes
1gb => 1024
1024*1024 bytes
units are case insensitive so 1GB 1Gb 1gB are all the same.

redis对单位不敏感,即不区分大小写。

include /path/to/local.conf
include /path/to/other.conf
include /path/to/fragments/*.conf

可以包含上述配置文件

1、bind 127.0.0.1 -::1 绑定本机网卡ip

2、protected-mode yes 保护模式 开启

3、port 6379 连接端口, 默认6379

4、daemonize yes 守护进程方式运行,即后台运行

5、pidfile /var/run/redis_6379.pid 和守护进程关联,进程文件

6、loglevel notice 日志等级

7、logfile "" 日志文件, “”标志性输出

8、databases 16 默认16个数据库

9、always-show-logo no logo总是显示

10、save 3600 1 300 100 60 10000 持久化规则

redis是内存数据库, 内存是断电即逝的。需要持久化。

3600s内发生至少1个修改,写入到文件。

300s内发生至少100个修改,写入到文件。

60s内发生至少10000个修改,写入到文件。

11、stop-writes-on-bgsave-error yes 发送错误是否继续写入

12、rdbcompression yes rdb文件是否压缩

13、rdbchecksum yes rdb是否检查校验

14、dbfilename dump.rdb rdb文件名字

15、maxclients 10000 默认最大10000个客户端连接

16、maxmemory-policy noeviction 内存不足的应对策略(5种)

17、requirepass magebyte 设置密码,默认没密码

18、appendonly no 是否开启aof

19、appendfilename "appendonly.aof" aof文件名字

20、appendfsync everysec 每一秒写入aof文件,并完成磁盘同步

21、appendfsync always 总是写入aof文件,并完成磁盘同步

22、appendfsync no 写入aof文件,不等待磁盘同步

23、no-appendfsync-on-rewrite aof重写

24、sentinel monitor host port 1 监控master主节点

13、redis持久化

1、RDB(redis database)

在这里插入图片描述

在指定的时间间隔内,将内存的数据集快照写入磁盘rdb文件,恢复时将rdb文件读到内存。

rdb文件位置:在这里插入图片描述

rdb文件名默认dump.rdb, 可以在 redis.conf 配置文件内修改。

快照触发机制:

1、save time count, 当time内修改了count次,就会生成一个rdb文件。

2、执行 flushall 也会生成一个rdb文件。

3、退出redis也会生成一个rdb文件。

优点:1、适合大规模数据的恢复!

2、数据完整性不高的可以使用,高效率。

缺点:1、需要间隔一定时间操作,redis突然宕机就会失去最后一次修改的数据。

2、folk 进程需要占用内存空间。

2、AOF(append only file)

在这里插入图片描述

以日志的记录每个 写指令 ,将redis所有的指令记录下来,读操作不记录,只许追加文件,不许改写文件。redis启动时会读取该文件,重构数据。(aof的文件是默认是appendonly.aof, 可以改配

置文件)修改配置文件appendonly yes, 重启redis即可生效。

当aof文件有埙坏,redis就跑不起来。此时需要使用redis提供的修复文件, redis-check-aof --fix ,执行该指令即可修复,redis就可以跑起来了,但错误的数据被丢弃了!

优点: 1、数据的完成性高一些,可以设置redis每个修改都执行写入aof文件!

2、设置不同步aof,效率非常高!

缺点:1、aof数据文件远远大于rdb文件,修复速度也远远低于rdb文件!

2、aof运行效率要比rdb文件慢, 所有redis默认使用rdb进行持久化!

3、将同步到aof文件配置成 everysec ,可能会丢失1s的数据!

4、持续io,降低效率!

aof重写规则

在这里插入图片描述
当aof文件大于64mb, 就会再folk一个进程来重写!

14、redis实现订阅分布

1、介绍

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 发布订阅(pub/sub)实现了消息系统,发送者(在redis术语中称为发布者)在接收者(订阅者)接收消息时发送消息。传送消息的链路称为信道。在Redis中,客户端可以订阅任意数量的信道。

在这里插入图片描述

1、基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。

2、 消息发布者,即publish客户端,无需独占链接,你可以在publish消息的同时,使用同一个redis-client链接进行其他操作(例如:INCR等)

3、 消息订阅者,即subscribe客户端,需要独占链接,即进行subscribe期间,redis-client无法穿插其他操作,此时client以阻塞的方式等待“publish端”的消息;这一点很好理解,因此subscribe端需要使用单独的链接,甚至需要在额外的线程中使用。

2、命令

PUBLISH channel message #将信息发送到指定的频道。

SUBSCRIBE channel [channel …] #订阅给定的一个或多个频道的

信息。

UNSUBSCRIBE [channel [channel …]] #退订给定的频道。

PSUBSCRIBE pattern [pattern …] #订阅一个或多个符合给定模式

的频道。根据模式来订阅,可以订阅许多频道。

PUNSUBSCRIBE [pattern [pattern …]] #退订所有给定模式的频道。

PUBSUB subcommand [argument [argument …]] #查看订阅与发布系统状态。

3、测试

发送端:

在这里插入图片描述

接收端:

在这里插入图片描述

4、适用场景

1、实时消息系统。

2、实时聊天。

3、订阅、关注…

15、redis主从复制

1、介绍

在这里插入图片描述
1、Redis使用异步复制,2.8版本开始,从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。

2、一个主服务器可以有多个从服务器。

3、不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器,多个从服务器之间可以构成一个图状结构。

4、复制功能不会阻塞主服务器:即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。

5、复制功能也不会阻塞从服务器:只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。

6、在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,连接请求会被阻塞。

7、还可以配置从服务器,让它在与主服务器之间的连接断开时,向客户端发送一个错误。

8、复制功能可以单纯地用于数据冗余(data redundancy),也可以通过让多个从服务器处理只读命令请求来提升扩展(scalability): 比如说,繁重的SORT命令可以交给附属节点去运行。

9)可以通过复制功能来让主服务器免于执行持久化操作:只要关闭主服务器的持久化功能,然后由从服务器去执行持久化操作即可。

2、作用

1、数据冗余:实现数据的热备份,是持久化之外的另一种方式。

2、故障恢复:当主节点(master)出现问题时,可以由从节点(slave)来提供服务,实现了快速恢复故障,也就是服务冗余。

3、读写分离:master主要是写,slave主要是读,可以提高服务器的负载能力。同时可以根据需求的变化,添加从节点的数量。

4、负载均衡:配合读写分离,有主节点提供写服务,从节点提供读服务,分担服务器负载,尤其在写少读多的情况下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量和负载。

5、高可用的基石:主从复制是哨兵和集群能够实施的基础,因此我们可以说主从复制是高可用的基石。

3、测试

1、复制3份redis.conf,区分命名

在这里插入图片描述

2、修改配置文件

在这里插入图片描述

修改端口port、pid文件、rdb文件、log文件

3、启动3个服务并连接客户端

在这里插入图片描述

查看主从信息(命令:info replication

在这里插入图片描述

4、将6380、6381作6379的从机

6380主从信息

在这里插入图片描述

6379主从信息

在这里插入图片描述

5、在主机写入

主机写入k1 v1

在这里插入图片描述
从机读取k1成功

在这里插入图片描述

从机不能写入

在这里插入图片描述
1、将主机shutdown,从机虽然写不了数据,但仍然能读到数据。

2、将从机shutdown,再连接,从机成为了主机,因为是用命令设

置主从关系的。重启就会恢复。通常是在配置文件设置主从关系。

3、从机重新认6379作主机后,扔能访问到k1,是全量复制

4、主机此时写入k5 v5, 从机仍能读到k5, 是增量复制

6、扩展

1、6380认6379作master, 而6381认6380作master。此时6380仍然是从节点,依旧不能执行写操作。

2、此时6379宕机了, 从节点可以自己当master,命令slaveof no one,此时就算之前的主节点恢复了也没有了从节点,需要重新手动配置。

16、redis哨兵模式

1、介绍

主从切换技术:当主机宕机后,需要手动把一台从(slave)服务器切换为主服务器,这就需要人工干预,费时费力,还回造成一段时间内服务不可用,所以推荐哨兵架构(Sentinel)来解决这个问题。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

在这里插入图片描述

哨兵模式有两个作用:

1、通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器当哨兵监测到Redis主机宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他服务器,修改配置文件,让他们换主机。

2、当一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此可以使用哨兵进行监控, 各个哨兵之间还会进行监控,这就形成了多哨兵模式。

在这里插入图片描述

假设主服务器宕机,哨兵1先检测到结果,但是系统并不会马上进行failover过程,仅仅是哨兵1主观认为主服务器不可以用,这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,并且数量达到一定时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。操作转移成功后。就会发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这一过程称为客观下线

2、测试

1、配置哨兵

在这里插入图片描述

sentinel monitor mysentinel 127.0.0.1 6379 1

sentinel monitor (被监控的名称) host port 1 (1是监测到主机宕机后,会投出1票)

2、启动哨兵

在这里插入图片描述

将6379关掉

在这里插入图片描述
如上图所示:选举了6380为master。

当主机重新连接时,只能被抓去当6380的从机了。。。

3、优缺点

优点:

1.哨兵集群,基于主从复制模式,所以有主从配置的所有优点。
2.主从可以切换,故障可以转移,系统可用性就更好。
3.哨兵模式是主从模式的升级,手动到自动,更完善。

缺点:
1.Redis不好在线扩容,集群容量一旦达到上限,扩容麻烦。
2.实现配置麻烦。

17、缓存穿透及解决办法

1、定义

在这里插入图片描述

通常流程是:一个请求过来,先查询是否在缓存当中,如果缓存中存在,则直接返回。如果缓存中不存在对应的数据,则检索数据库,如果数据库中存在对应的数据,则更新缓存并返回结果。如果数据库中也不存在对应的数据,则返回空或错误。

缓存穿透(cache penetration)是用户访问的数据既不在缓存当中,也不在数据库中。出于容错的考虑,如果从底层数据库查询不到数据,则不写入缓存。这就导致每次请求都会到底层数据库进行查询,缓存也失去了意义。当高并发或有人利用不存在的Key频繁攻击时,数据库的压力骤增,甚至崩溃,这就是缓存穿透问题。

2、解决办法

1、缓存空值(null)或 默认值

分析业务请求,如果是正常业务请求时发生缓存穿透现象,可针对相应的业务数据,在数据库查询不存在时,将其缓存为空值(null)或 默认值。需要注意的是,针对空值的缓存失效时间不宜过长,一般设置为5分钟之内。当数据库被写入或更新该key的新数据时,缓存必须同时被刷新,避免数据不一致

2、业务逻辑前置校验

在业务请求的入口处进行数据合法性校验,检查请求参数是否合理、是否包含非法值、是否恶意请求等,提前有效阻断非法请求。比如,根据年龄查询时,请求的年龄为-10岁,这显然是不合法的请求参数,直接在参数校验时进行判断返回。

3、使用布隆过滤器请求白名单

在写入数据时,使用布隆过滤器进行标记(相当于设置白名单),业务请求发现缓存中无对应数据时,可先通过查询布隆过滤器判断数据是否在白名单内,如果不在白名单内,则直接返回空或失败。

4、用户黑名单限制

当发生异常情况时,实时监控访问的对象和数据,分析用户行为,针对故意请求、爬虫或攻击者,进行特定用户的限制;

18、缓存击穿及解决办法

1、定义

在这里插入图片描述

缓存击穿和缓存雪崩很类似,只不过是缓存击穿是一个热点key失效,而缓存雪崩是大量热点key失效。因此,可以将缓存击穿看作是缓存雪崩的一个子集。

2、解决办法

1、缓存执行完毕,重新从缓存中获取数据。单机通过synchronized或lock来处理,分布式环境采用分布式锁。

2、热点数据不设置过期时间,后台异步更新缓存,适用于不严格要求缓存一致性的场景。

19、缓存雪崩及解决办法

1、定义

在这里插入图片描述

在使用缓存时,通常会对缓存设置过期时间,一方面目的是保持缓存与数据库数据的一致性,另一方面是减少冷缓存占用过多的内存空间。但当缓存中大量热点缓存采用了相同的实效时间,就会导致缓存在某一个时刻同时实效,请求全部转发到数据库,从而导致数据库压力骤增,甚至宕机。从而形成一系列的连锁反应,造成系统崩溃等情况,这就是缓存雪崩(Cache Avalanche)。
除了缓存过期,由于某些原因导致缓存服务宕机、挂掉或不响应也会发生缓存雪崩。

2、解决办法

1、通常的解决方案是将key的过期时间后面加上一个随机数(比如随机1-5分钟),让key均匀的失效。

2、考虑用队列或者锁的方式,保证缓存单线程写,但这种方案可能会影响并发量。

3、热点数据可以考虑不失效,后台异步更新缓存,适用于不严格要求缓存一致性的场景。

4、数据预热,对于一些可能高访问的数据,先查询放到缓存里。

5、构建缓存高可用集群(针对缓存服务故障情况)。

6、当缓存雪崩发生时,服务熔断、限流、降级等措施保障。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/518305
推荐阅读
相关标签
  

闽ICP备14008679号