liudehaos

还历史以真诚,还生命以过程。 ——余秋雨
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Redis_

Posted on 2022-05-14 17:19  liudehaos  阅读(23)  评论(0编辑  收藏  举报

Redis学习

Redis学习(一)

1. NoSQL的引言

NoSQL(Not Only SQL ),意即不仅仅是SQL, 泛指非关系型的数据库。Nosql这个技术门类,早期就有人提出,发展至2009年趋势越发高涨。

2. 为什么是NoSQL

随着互联网网站的兴起,传统的关系数据库在应付动态网站,特别是超大规模和高并发的纯动态网站已经显得力不从心,暴露了很多难以克服的问题。如商城网站中对商品数据频繁查询对热搜商品的排行统计订单超时问题、以及微信朋友圈(音频,视频)存储等相关使用传统的关系型数据库实现就显得非常复杂,虽然能实现相应功能但是在性能上却不是那么乐观。nosql这个技术门类的出现,更好的解决了这些问题,它告诉了世界不仅仅是sql。

3. NoSQL的四大分类

3.1 键值(Key-Value)存储数据库

# 1.说明: 
- 这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。

# 2.特点
- Key/value模型对于IT系统来说的优势在于简单、易部署。  
- 但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。

# 3.相关产品
- Tokyo Cabinet/Tyrant,
- Redis 基于内存的   运行软件--->磁盘--->内存中
- SSDB   基于磁盘的   直接与磁盘做交互--> IO
- Voldemort
- Oracle BDB

3.2 列存储数据库 ----> Hbase

# 1.说明
- 这部分数据库通常是用来应对分布式存储的海量数据。

# 2.特点
- 键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。列簇
- rowkey

# 3.相关产品
- Cassandra、`HBase`、Riak.

3.3 文档型数据库

# 1.说明
- 文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高
{'id':1001,'name':xiaohu}
{'id':1001,'name':'xiaohu2,'address':'anhuihefei','likes':['play','eat'],'study':{'yuyan':java,'ruanjian':'mysql'}}
文档数据库对于单条数据来说,他的事务支持并没有那么强大
目前的mongodb5,支持了单条数据的事务,但是多条不行

# 2.特点
- 以文档形式存储

# 3.相关产品
- `MongoDB`、CouchDB、 MongoDb(4.x). 国内也有文档型数据库SequoiaDB,已经开源。

3.4 图形(Graph)数据库

# 1.说明
- 图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。

# 2.特点
- NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。

# 3.相关产品
- Neo4J、InfoGrid、 Infinite Graph、

4. NoSQL应用场景

  • 数据模型比较简单

  • 需要灵活性更强的IT系统

  • 对数据库性能要求较高

  • 不需要高度的数据一致性(NoSql数据库对事物的支持都不是太好)

5. 什么是Redis

image-20220511203523936

Redis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine.

Redis 开源 遵循BSD 基于内存数据存储 被用于作为 数据库 缓存 消息中间件

  • 总结: redis是一个内存型的数据库

6. Redis特点(面试必问)

  • Redis是一个高性能key/value内存型数据库 在redis中,所有的数据形式都是以键值对的方式来存储的

  • Redis支持丰富的数据类型 string,list,set,sorted set 指的是键值对中的值的类型

  • Redis支持持久化 持久化:将数据落盘,明天会详细说一说redis中的持久化

  • Redis单线程,单进程 由于是单线程和单进程的,所以它的线程是安全的,我们之前说的java中的多线程安全在分布式中不起作用,当时只针对一个JVM中是有效的。


7. Redis安装

# 0.准备环境
- vmware 12.x+
- centos7.x+

# 1.下载redis源码包
- https://redis.io/

image-20220511205543918

# 2.下载完整源码包
- redis-7.0.10.tar.gz

image-20220511205921207

# 3.将下载redis资料包上传到Linux中

image-20220511210105941

# 4.解压缩文件
[root@localhost ~]# tar -zxvf redis-7.0.10.tar.gz
[root@localhost ~]# ll

image-20220511210144370

redis底层是由C语言编写的
# 5.安装gcc  
- yum install -y gcc

# 6.进入解压缩目录执行如下命令
# 注意。一定要在Makefile文件的同目录下使用下面的命令对redis做编译
- make MALLOC=libc

# 7.编译完成后执行如下命令
- make install PREFIX=/usr/redis

# 8.进入/usr/redis目录启动redis服务 
- ./redis-server

image-20220511210737950

# 9.Redis服务端口默认是 6379

# 10.进入bin目录执行客户端连接操作
- ./redis-cli -h localhost –p 6379
# 如果是在一台机器上,可以省略后面的
- ./redis-cli

image-20220511210929504

# 11.连接成功出现上面界面连接成功

简单测试成功,给一个键值对,根据键获取值,获取所有的键

image-20220511211227714

8. Redis数据库相关指令

注意:(说这个之前,需要带学生写一个配置文件的修改和使用,库的细节)

8.1 数据库操作指令

# 1.Redis中库说明
- 使用redis的默认配置器动redis服务后,默认会存在16个库,编号从0-15 配置问价中有个database相关的
- 可以使用select 库的编号 来选择一个redis的库

# 2.Redis中操作库的指令
- 清空当前的库  FLUSHDB
- 清空全部的库  FLUSHALL

# 3.redis客户端显示中文
-	./redis-cli  -p 7000 --raw

8.2 操作key相关指令(分别演示案例)

# 1.DEL指令
- 语法 :  DEL key [key ...] 
- 作用 :  删除给定的一个或多个key 。不存在的key 会被忽略。多个key之间使用空格隔开
- 可用版本: >= 1.0.0
- 返回值: 被删除key 的数量。 

# 2.EXISTS指令
- 语法:  EXISTS key
- 作用:  检查给定key 是否存在。多个key之间使用空格隔开,只要有一个key存在,返回值就是1
- 可用版本: >= 1.0.0
- 返回值: 若key 存在,返回1 ,否则返回0。 

# 3.EXPIRE
- 语法:  EXPIRE key seconds
- 作用:  为给定key 设置生存时间,当key 过期时(生存时间为0 ),它会被自动删除。
- 可用版本: >= 1.0.0
- 时间复杂度: O(1)
- 返回值:设置成功返回1 。

# 4.KEYS
- 语法 :  KEYS pattern
- 作用 :  查找所有符合给定模式pattern 的key 。
- 语法:
	KEYS * 匹配数据库中所有key 。
	KEYS h?llo 匹配hello ,hallo 和hxllo 等。
	KEYS h*llo 匹配hllo 和heeeeello 等。
	KEYS h[ae]llo 匹配hello 和hallo ,但不匹配hillo 。特殊符号用 "\" 隔开
- 可用版本: >= 1.0.0
- 返回值: 符合给定模式的key 列表。

# 5.MOVE
- 语法 :  MOVE key db  (move name 1----将name键移动到1号库)
- 作用 :  将当前数据库的key 移动到给定的数据库db 当中。
- 可用版本: >= 1.0.0
- 返回值: 移动成功返回1 ,失败则返回0 。

# 6.PEXPIRE
- 语法 :  PEXPIRE key milliseconds
- 作用 :  这个命令和EXPIRE 命令的作用类似,但是它以毫秒为单位设置key 的生存时间,而不像EXPIRE 命令那样,以秒为单位。
- 可用版本: >= 2.6.0
- 时间复杂度: O(1)
- 返回值:设置成功,返回1  key 不存在或设置失败,返回0

# 7.PEXPIREAT
- 语法 :  PEXPIREAT key milliseconds-timestamp
- 作用 :  这个命令和EXPIREAT 命令类似,但它以毫秒为单位设置key 的过期unix 时间戳,而不是像EXPIREAT那样,以秒为单位。
- 可用版本: >= 2.6.0
- 返回值:如果生存时间设置成功,返回1 。当key 不存在或没办法设置生存时间时,返回0 。(查看EXPIRE 命令获取更多信息)

# 8.TTL
- 语法 :   TTL key
- 作用 :   以秒为单位,返回给定key 的剩余生存时间(TTL, time to live)。
- 可用版本: >= 1.0.0
- 返回值:
	当key 不存在时,返回-2 。
	当key 存在但没有设置剩余生存时间时,返回-1 。
	否则,以秒为单位,返回key 的剩余生存时间。
- Note : 在Redis 2.8 以前,当key 不存在,或者key 没有设置剩余生存时间时,命令都返回-1 。

# 9.PTTL
- 语法 :  PTTL key
- 作用 :  这个命令类似于TTL 命令,但它以毫秒为单位返回key 的剩余生存时间,而不是像TTL 命令那样,以秒为单位。
- 可用版本: >= 2.6.0
- 返回值: 当key 不存在时,返回-2 。当key 存在但没有设置剩余生存时间时,返回-1 。
- 否则,以毫秒为单位,返回key 的剩余生存时间。
- 注意 : 在Redis 2.8 以前,当key 不存在,或者key 没有设置剩余生存时间时,命令都返回-1 。

# 10.RANDOMKEY
- 语法 :  RANDOMKEY
- 作用 :  从当前数据库中随机返回(不删除) 一个key 。
- 可用版本: >= 1.0.0
- 返回值:当数据库不为空时,返回一个key 。当数据库为空时,返回nil 。

# 11.RENAME
- 语法 :  RENAME key newkey
- 作用 :  将key 改名为newkey 。当key 和newkey 相同,或者key 不存在时,返回一个错误。当newkey 已经存在时,RENAME 命令将覆盖旧值。
- 可用版本: >= 1.0.0
- 返回值: 改名成功时提示OK ,失败时候返回一个错误。

# 12.TYPE
- 语法 :  TYPE key
- 作用 :  返回key 所储存的值的类型。
- 可用版本: >= 1.0.0
- 返回值:
	none (key 不存在)
	string (字符串)
	list (列表)
	set (集合)
	zset (有序集)
	hash (哈希表)

8.3 String类型(分别演示)

1. 内存存储模型

image-20200623132104399

2. 常用操作命令

命令说明
set 设置一个key/value
get 根据key获得对应的value
mset 一次设置多个key value
mget 一次获得多个key的value
getset 获得原始key的值,同时设置新值
strlen 获得对应key存储value的长度
append 为对应key的value追加内容
getrange 索引0开始 截取value的内容 到末尾-1
setex 设置一个key存活的有效期(秒)
psetex 设置一个key存活的有效期(毫秒)
setnx 存在不做任何操作,不存在添加
msetnx原子操作(只要有一个存在不做任何操作) 可以同时设置多个key,只有有一个存在都不保存
decr 进行数值类型的-1操作
decrby 根据提供的数据进行减法操作
Incr 进行数值类型的+1操作
incrby 根据提供的数据进行加法操作
Incrbyfloat 根据提供的数据加入浮点数(不是四舍五入)

8.4 List类型

list 列表 相当于java中list 集合 特点 元素有序 且 可以重复,key还是一个字符串,值是一个list

1.内存存储模型

image-20200623161114380

2.常用操作指令

命令说明
lpush 将某个值加入到一个key列表头部 lpush list xiaohu xiaohei xiaoming 当列表不存在的时候会进行创建
lpushx 同lpush,但是必须要保证这个key存在 必须在列表进行存在的情况下从左插入
rpush 将某个值加入到一个key列表末尾
rpushx 同rpush,但是必须要保证这个key存在
lpop 返回和移除列表左边的第一个元素
rpop 返回和移除列表右边的第一个元素
lrange 获取某一个下标区间内的元素 lrange list 0 -1
llen 获取列表元素个数
lset 设置某一个指定索引的值(索引必须存在)
lindex 获取某一个指定索引位置的元素
lrem 删除重复元素
ltrim 保留列表中特定区间内的元素
linsert 在某一个元素之前,之后插入新元素

8.5 Set类型

特点: Set类型 Set集合 元素无序 不可以重复

1.内存存储模型

image-20200623193634316

2.常用命令

命令说明
sadd 为集合添加元素
smembers 显示集合中所有元素 无序
scard 返回集合中元素的个数
spop 随机返回一个元素 并将元素在集合中删除
smove 从一个集合中向另一个集合移动元素 必须是同一种类型
srem 从集合中删除一个元素
sismember 判断一个集合中是否含有这个元素
srandmember 随机返回元素 后面可以加数字 表示每次返回的个数
sdiff 去掉第一个集合中其它集合含有的相同元素
sinter 求交集
sunion 求和集

8.6 ZSet类型

特点: 可排序的set集合 排序 不可重复

ZSET 官方 可排序SET sortSet

1.内存模型

image-20200623194903967

2.常用命令

命令说明
zadd 添加一个有序集合元素 zadd zset 2 xiaohu 3 xiaohu2
zcard 返回集合的元素个数
zrange 升序 zrevrange 降序 返回一个范围内的元素 如果想看看分数 withscores
zrangebyscore 按照分数查找一个范围内的元素 zrangebyscore zset 0 20 withscores limit 0 2
zrank 返回排名
zrevrank 倒序排名
zscore 显示某一个元素的分数
zrem 移除某一个元素
zincrby 给某个特定元素加分

8.7 hash类型

特点: value 是一个map结构 存在key value key 无序的

redis key(String) value(map)

Map<String,Map<String,value>> map

举例:map name zhangsan

1.内存模型

image-20220511234124908

2.常用命令

命令说明
hset 设置一个key/value对
hget 获得一个key对应的value
hgetall 获得所有的key/value对
hdel 删除某一个key/value对
hexists 判断一个key是否存在
hkeys 获得所有的key
hvals 获得所有的value
hmset 设置多个key/value
hmget 获得多个key的value
hsetnx 设置一个不存在的key的值
hincrby 为value进行加法运算
hincrbyfloat 为value加入浮点值

第一天说完上面知识点后,介绍可视化工具的安装使用(引入修改配置文件,使得远程连接)mac电脑的叫做medis https://getmedis.com/

下载官网:https://redisdesktop.com/download

注意问题:

1、如果还是连接不上,查看防火墙

2、端口被占用,可能是redis服务起多了 ps aux|grep redis

Redis学习(二)

1. 持久化机制

client redis[内存] -----> 内存数据- 数据持久化-->磁盘

Redis官方提供了两种不同的持久化方法来将内存的数据存储到硬盘里面分别是:

  • 快照(Snapshot)

  • AOF (Append Only File) 只追加日志文件

1.1 快照(Snapshot)

1. 特点

这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认开启持久化方式,保存的文件是以.rdb形式结尾的文件因此这种方式也称之为RDB方式。

官方说法叫快照持久化

image-20220512214029142

2.快照生成方式

  • 客户端方式: BGSAVE 和 SAVE指令

  • 服务器配置自动触发

# 1.客户端方式之BGSAVE
- a.客户端可以使用BGSAVE命令来创建一个快照,当接收到客户端的BGSAVE命令时,redis会调用fork¹来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令请求。
	
	`名词解释: fork当一个进程创建子进程的时候,底层的操作系统会创建该进程的一个副本,在类似于unix系统中创建子进程的操作会进行优化:在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了写之后,对被写入的内存的共享才会结束服务`

image-20220512214729500

# 2.客户端方式之SAVE
- b.客户端还可以使用SAVE命令来创建一个快照,接收到SAVE命令的redis服务器在快照创建完毕之前将不再响应任何其他的命令

image-20220512214914633

  • 注意: SAVE命令并不常用,使用SAVE命令在快照创建完毕之前,redis处于阻塞状态,无法对外服务

# 3.服务器配置方式之满足配置自动触发
- 如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后自动触发一次BGSAVE命令,如果设置多个save配置选项,当任意一个save配置选项条件满足,redis也会触发一次BGSAVE命令

image-20220512215313342

# 4.服务器接收客户端shutdown指令
- 当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器

3.配置生成快照名称和位置

#1.修改生成快照名称
- dbfilename dump.rdb

# 2.修改生成位置
- dir ./

image-20220512220504189

演示断电操作,这个持久化并不是太好,可能会造成数据丢失的问题(刚刚做完一次快照,又来了写数据请求断电)


1.2 AOF 只追加日志文件

1.特点

这种方式可以将所有客户端执行的写命令记录到日志文件中,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行一次AOF文件所包含的所有写命令,就可以恢复AOF文件的记录的数据集.

image-20220512222000591

2.开启AOF持久化

在redis的默认配置中AOF持久化机制是没有开启的,需要在配置中开启

# 1.开启AOF持久化
- a.修改 appendonly yes 开启持久化
- b.修改 appendfilename "appendonly.aof" 指定生成文件名称

image-20220512222213906

image-20220512222231426

3.日志追加频率

# 1.always 【谨慎使用】
- 说明: 每个redis写命令都要同步写入硬盘,严重降低redis速度
- 解释: 如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制;
- 注意: 转盘式硬盘在这种频率下200左右个命令/s ; 固态硬盘(SSD) 几百万个命令/s;
- 警告: 使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的`写入放大`问题,导致将固态硬盘的寿命从原来的几年降低为几个月。

# 2.everysec 【推荐默认】
- 说明: 每秒执行一次同步显式的将多个写命令同步到磁盘
- 解释: 为了兼顾数据安全和写入性能,用户可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis可以保证,即使系统崩溃,用户最多丢失一秒之内产生的数据。 

# 3.no	【不推荐】
- 说明: 由操作系统决定何时同步 
- 解释:最后使用no选项,将完全有操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响但是系统崩溃时,会丢失不定数量的数据,甚至丢失全部数据,另外如果用户硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘数据填满时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢。

4.修改同步频率

# 1.修改日志同步频率
- 修改appendfsync everysec|always|no 指定

image-20220512223201218


1.3 AOF文件的重写(面试必问)

1. AOF带来的问题

AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了AOF重写(ReWriter)机制。

2. AOF重写

用来在一定程度上减小AOF文件的体积,并且还能保证数据不丢失

3. 触发重写方式

# 1.客户端方式触发重写
- 执行BGREWRITEAOF命令  不会阻塞redis的服务

# 2.服务器配置方式自动触发
- 配置redis.conf中的auto-aof-rewrite-percentage选项 参加下图↓↓↓
- 如果设置auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%)时,会自动触发,如果重写过于频繁,用户可以考虑将auto-aof-rewrite-percentage设置为更大

image-20220512225431013

4. 重写原理

从 Redis 7.0.0 开始,Redis 使用了多部分 AOF 机制。也就是将原来的单个AOF文件拆分为基础文件(最多一个)和增量文件(可能不止一个)。基本文件表示重写AOF 时存在的数据的初始(RDB 或 AOF 格式)快照。增量文件包含自创建最后一个基本 AOF 文件以来的增量更改。所有这些文件都放在一个单独的目录中,并由清单文件跟踪。

从 Redis 7.0.0 开始,在调度 AOF 重写时,Redis 父进程会打开一个新的增量 AOF 文件继续写入。子进程执行重写逻辑并生成新的基础 AOF。Redis 将使用一个临时清单文件来跟踪新生成的基础文件和增量文件。当它们准备好后,Redis 会执行原子替换操作,使这个临时清单文件生效。为了避免在 AOF 重写重复失败和重试的情况下创建大量增量文件的问题,Redis 引入了 AOF 重写限制机制,以确保失败的 AOF 重写以越来越慢的速度重试。

日志重写

注意:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,替换原有的文件这点和快照有点类似。

# 重写流程
- 1. redis调用fork ,现在有父子两个进程 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 2. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 3. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

Redis7.0.0之前:

image-20220512225149515

Redis7.0.0之后:

image-20220513120013948


1.4 持久化总结

两种持久化方案既可以同时使用(aof),又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定。

无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方)。

2、位图

零存零取,整存零取,整存整存

1.bitmap介绍

位图不是真正的数据类型,它是定义在字符串类型中,一个字符串类型的值最多能存储512M字节的内容

位上限:2^(9(512)+10(1024)+10(1024)+3(8b=1B))=2^32b

2.setbit设置某一位上的值

语法:SETBIT key offset value (offset位偏移量,从0开始)

127.0.0.1:7000> flushall
OK
127.0.0.1:7000> setbit k1 1 1
0
127.0.0.1:7000> get k1
@
127.0.0.1:7000> setbit k1 7 1
0
127.0.0.1:7000> get k1
A
127.0.0.1:7000> setbit k1 7 2
ERR bit is not an integer or out of range

image-20220512235948384

127.0.0.1:7000> setbit k1 9 1
0

127.0.0.1:7000> get k1
A@

image-20220512235956599

image-20220513000005724

 

3.getbit 获取某一位上的值

语法:GETBIT key offset

127.0.0.1:7000> getbit k1 7
1
127.0.0.1:7000> getbit k1 8
0
127.0.0.1:7000> getbit k1 1
1

image-20220513000108744

 

4.bitpos返回指定值0或者1在指定区间上首次出现的下标

 

语法:BITPOS key bit [start] [end](字节索引,0表示第一个字节)

summary: Find first bit set or clear in a string since: 2.8.7 group: string

不指定查找范围,表示从全部内容中查找:BITPOS key bit

127.0.0.1:7000> keys *
k1
127.0.0.1:7000> bitpos k1 1
1
127.0.0.1:7000> setbit k1 1 0
1
127.0.0.1:7000> bitpos k1 1
7
127.0.0.1:7000> setbit k1 7 0
1
127.0.0.1:7000> bitpos k1 1
9

image-20220513000116192

 

指定查找范围:

BITPOS key bit start :从start+1个字节开始查找,直到尾部 BITPOS key bit start end:从start+1字节开始到end+1字节之间查找

然后将数据还原:

127.0.0.1:7000> setbit k1 1 1
0
127.0.0.1:7000> setbit k1 7 1
0

查找演示:

127.0.0.1:7000> bitpos k1 1 0 0
1 #在第一个字节中查找1首次出现的下标
127.0.0.1:7000> bitpos k1 1 0
1 #从第一个字节到值得最后一个字节查找1首次出现的下标
127.0.0.1:7000> setbit k1 1 0
1 #将指定下标的值改为0
127.0.0.1:7000> bitpos k1 1 0 0
7 #
127.0.0.1:7000> bitpos k1 1 0
7
127.0.0.1:7000> setbit k1 7 0
1
127.0.0.1:7000> bitpos k1 1 0 0
-1  #在第一个字节中没有找到1,则返回-1
127.0.0.1:7000> bitpos k1 1 0
9  #从第一个字节到值得最后一个字节查找
127.0.0.1:7000> bitpos k1 1 0 1
9 #在第1和第2个字节总找1首次出现的位置
127.0.0.1:7000> bitpos k1 1 0 2
9 #在第1到第3个字节查找1首次出现的位置,但数据总共2(小于end对应的3)个字节,不会抛错。

5.bitop位操作

语法:BITOP operation destkey key [key ...]

summary: Perform bitwise operations between strings since: 2.6.0 group: string

对一个或多个保存二进制位的字符串 key 进行位操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种

BITOP AND destkey key [key ...] ,对一个或多个 key 求逻与,并将结果保存到 destkey

BITOP OR destkey key [key ...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey

BITOP XOR destkey key [key ...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey

BITOP NOT destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey

除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入,当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0,空的 key 也被看作是包含 0 的字符串序列

BITOP AND destkey key [key ...]演示:

127.0.0.1:7000> flushall
OK
127.0.0.1:7000> keys *
(empty list or set)
127.0.0.1:7000> setbit k1 1 1
(integer) 0
127.0.0.1:7000> setbit k2 7 1
(integer) 0
127.0.0.1:7000> bitop and k3 k1 k2
(integer) 1
127.0.0.1:7000> get k3
"\x00"

image-20220513000132424

 

BITOP OR destkey key [key ...]演示

127.0.0.1:7000> bitop or k4 k1 k2
(integer) 1
127.0.0.1:7000> get k4
"A"

 

image-20220513000216186

BITOP XOR destkey key [key ...]

127.0.0.1:7000> bitop xor k5 k1 k2
(integer) 1
127.0.0.1:7000> get k5
"A"

image-20220513000225357

127.0.0.1:7000> bitop not k6 k1
(integer) 1
127.0.0.1:7000> get k6
"\xbf"

image-20220513000236086

6.bitcount

统计指定位区间上值为1的个数

  1. BITCOUNT key [start] [end] start end 字节的索引 正方向

从左向右从0开始,注意官方start、end是位,测试后是字节

127.0.0.1:7000> get k1
"@"
127.0.0.1:7000> bitcount k1
(integer) 1
127.0.0.1:7000> setbit k1 7 1
(integer) 0
127.0.0.1:7000> bitcount k1
(integer) 2
127.0.0.1:7000> setbit k1 9 1
(integer) 0
127.0.0.1:7000> bitcount k1
(integer) 3 #统计全部的1的总数
127.0.0.1:7000> bitcount k1 0 0
(integer) 2 #统计第一个字节中1出现的总数
127.0.0.1:7000> bitcount k1 0 1
(integer) 3 #统计第0+1到第1+1字节中1出现的总数
  1. BITCOUNT key [start] [end] start end 字节的索引 负方向

从右向左从-1开始,注意官方start、end是位,测试后是字节

127.0.0.1:7000>BITCOUNT k1  0  -1 #等同于BITCOUNT k1
 (integer) 3

最常用的就是 BITCOUNT k1

7.Redis的二进制位

127.0.0.1:7000> set k7 ab
OK
127.0.0.1:7000> get k7
"ab"
127.0.0.1:7000> bitcount k7
(integer) 6
127.0.0.1:7000> bitcount k7 0 0
(integer) 3
127.0.0.1:7000> bitcount k7 1 1
(integer) 3

image-20220513000248093

127.0.0.1:7000> set k8 中
OK
127.0.0.1:7000> bitcount k8
(integer) 13
127.0.0.1:7000> get k8
"\xe4\xb8\xad"

image-20220513000312304

 

8.Bitmap应用场景

网站用户签到的天数统计

用户ID为key,天作为offset,上线置为1 366> 000000000000000

366 /8=46Byte ID为18的用户,今年的第1天签到、第30天签到

127.0.0.1:7000[2]> setbit u18 1 1
(integer) 0
127.0.0.1:7000[2]> setbit u18 30 1
(integer) 0
127.0.0.1:7000[2]> bitcount u18 #统计id为18的用户签到总次数
(integer) 2
127.0.0.1:7000[2]> keys u*
1) "u18"

按天统计网站活跃用户

天作为key,用户ID为offset,上线置为1

求一段时间内活跃用户数 5000 0000 / 8366= 6.3MB=366 (五千万活跃用户1年才产生2GB左右的数据)

127.0.0.1:7000>SETBIT 20190601 5  1    #0000 0100
127.0.0.1:7000>SETBIT 20190602 7  1    #0000 0001 
127.0.0.1:7000>SETBIT 20190603 7  1    #0000 0001
求6月1日到6月10日的活跃用户数
127.0.0.1:7000>BITOP OR users 20190601 20190602 20190603 ... 20190610
127.0.0.1:7000>BITCOUNT users    #目标key为users
结果为2

用户在线状态、在线人数统计

127.0.0.1:7000> SETBIT online 5  1 #0000 0100 上线为1
(integer) 0
127.0.0.1:7000> SETBIT online 7  1 #0000 0101
(integer) 0
127.0.0.1:7000> bitcount online #当前在线人数
(integer) 2 
127.0.0.1:7000> SETBIT online 7  0
(integer) 1
127.0.0.1:7000> bitcount online #当前在线人数
(integer) 1

3. java操作Redis

10.1 环境准备

1. 引入依赖

<!--引入jedis连接依赖-->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>4.3.2</version>
</dependency>

2.创建jedis对象

 public static void main(String[] args) {
   //1.创建jedis对象
   Jedis jedis = new Jedis("192.168.40.110", 7000);//1.redis服务必须关闭防火墙  2.redis服务必须开启远程连接
   jedis.select(0);//选择操作的库默认0号库
   //2.执行相关操作
   //....
   //3.释放资源
   jedis.close();
 }

10.2 操作key相关API

    //测试与key相关的操作
    @Test
    public void testKey(){
        jedis.set("name","xiaohu");
        jedis.set("age","18");
        jedis.set("age1","19");
        jedis.set("age2","20");
        jedis.set("age3","21");
        //删除一个键
        long l = jedis.del("age1");
        if(l==1){
            System.out.println("成功删除age1键");
        }
        //删除多个键
        long l2 = jedis.del("age2", "age3");
        if(l2>1){
            System.out.println("成功删除多个键");
        }

        //判断是否存在
        boolean name = jedis.exists("age1");
        if(name==true){
            System.out.println("键存在");
        }

        jedis.expire("age",10L);


    }

 

10.3操作String相关API

 @Test
    public  void testString(){
        jedis.decrBy("age",255l);
        jedis.incr("age");



    }

 

10.4操作List相关API

@Test
    public  void testList  (){
        jedis.lpush("l1","name","age","xingbie","address");
        jedis.lrange("l1",0,-1);
        jedis.type("l1");
        jedis.llen("l1");
        jedis.lpop("l1",1);
        jedis.lrem("l1",1,"lisi");
        jedis.linsert("l1", ListPosition.valueOf("before"), "name", "class");
        jedis.linsert("l1","");


    }

 

10.5操作Set的相关API

@Test
    public  void testSet(){
        jedis.sadd("set1","name","xiaoming" ,"xiaobai", "xiaohu" ,"xiaohong", "xiaocai" ,"xiaoshui");
        jedis.keys("*");
        jedis.smembers("set1");
        jedis.sadd("set2","wangwu" ,"zhaoliu" , "wangermazi", "minli");
        jedis.smembers("set2");
        jedis.scard("set1");
        jedis.spop("set2");
        jedis.smove("set1","set2","name");
        jedis.sismember("set1","xiaoming");
        jedis.srandmember("set1",1);
        jedis.sadd("set1", "java" ,"hoodoop" ,"spark" ,"python");
        jedis.sadd("set1", "java" ,"hoodoop" ,"spark" ,"python","mysql" ,"redis", "maven");
        jedis.sdiff("set1","set2");
        jedis.sinter("set1","set2");
        jedis.sunion("set1","set2");



    }

 

10.6 操作ZSet相关API

@Test
    public void TestZset() {
jedis.zadd("zset1", 20l, "xiaoming");
        jedis.zadd("zset1", 2l, "xiaoxin");
       jedis.zadd("zset1", 5l, "xiaofang");
        jedis.zrange("zset1",0,-1);
        jedis.zrangeByScoreWithScores("zset1",0,10);
        jedis.zrangeWithScores("zset1",0,-1);
        jedis.zrank("zset1","xiaoming");
        jedis.zscore("zset1","xiaoxin");
        jedis.zincrby("zset1",25l,"xiaoming");

        jedis.select(1);
    }

 

10.7 操作Hash相关API

 @Test
    public  void testHash(){
        jedis.hset("map1","age","25");
        jedis.hset("map1","name","xiaohua");
        jedis.hset("map1","sex","nv");
        jedis.hgetAll("map1");
        jedis.mset("map2", "name", "wangyouhu" ,"age", "18");
        jedis.hgetAll("map2");
        jedis.hsetnx("map3", "name"," xiaofang");
        jedis.hgetAll ("map3");
        long l = jedis.hincrBy("map1","age",15l);
        jedis.hincrByFloat("map1","age",32.12f);
        jedis.hgetAll("map1");

    }

 


Redis学习(三 )

1.SpringBoot整合Redis

Spring Boot Data(数据) Redis 中提供了RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。

注意: 使用RedisTemplate默认是将对象序列化到Redis中,所以放入的对象必须实现对象序列化接口

1.1 环境准备

1.引入依赖 (spring2.0以后不适用jedis了,使用lettuce)

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

2.配置application.propertie

spring.redis.host=192.168.40.110
spring.redis.port=7000
spring.redis.database=0

1.2 使用StringRedisTemplate和RedisTemplate

2.连接池

2.1引入依赖

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