# Redis笔记
**Repository Path**: azin-cn/redis-notes
## Basic Information
- **Project Name**: Redis笔记
- **Description**: Redis笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-03-06
- **Last Updated**: 2022-03-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
> 互联网技术中,终极解决方案:加一层
# 一. Java开发中技术分析
从开发角度的技术选型来说,只需要功能性的技术即可完成业务的开发,但是仅仅只有功能性的技术不足以满足快速开发或者可维护的需求,所以需要引入扩展性的技术和解决性能类型的技术。
# 二. 为什么添加NOSQL数据库
### (1). 负载均衡时,session对象的共享问题
做负载均衡时,如果session不能共享,那么会导致在一台服务器上有数据,另外一台服务器上没有数据,显然这是不允许的。
### (2). session共享引发的不安全、大量IO操作问题
如果直接在每台服务器上做session复制,每一台都储存session对象会造成空间的大量浪费,如果储存到数据库中判断是否已经登录(是否有session),那么又会造成大量的IO操作,硬盘的速度限制了访问的速度。
# 三. Redis数据库 - 内存中的数据库(高读写,低延迟,做缓存)
## 1. Redis的简介
> Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis就像是一个HashMap,不过不是在JVM中运行,而是以一个独立进程的形式运行。
## 2. Redis的安装
参考链接🔗 [Redis安装](https://www.cnblogs.com/ifme/p/13903017.html)
## 3. Redis的结构
## 4. Redis的启动
> 端口:6379
### 1). Redis的前台启动
### 2). Redis的后台启动
`redis-server`
## 5. Redis的技术策略
单线程+多路IO复用的效率更好,类似线程中分出的协程思想,复用线程。
- 单线程IO复用的举例
这里省下的就是线程切换时的成本,如果时多线程,即使线程的切换成本比进程低了很多,但线程的切换消耗在今天高并发的角度来看,还是很高的。
线程的切换成本简单来说是:切换线程时,CPU需要保存一个线程状态和加载另外一个线程花费的时间和耗费的能量(上下文切换)。
以前只需要考虑线程比进程轻量,切换线程即可,但是对于高并发的今天,线程也显得是重量级的过程,所以也出现了更轻量的协程(用户态的线程)。
## 6. Redis的使用
> Redis是单线程操作,所以不需要考虑多线程的数据锁。
>
> 给定的指令不一样,Redis形成的数据结构就不一样,不需要显式的声明数据类型
>
> (set,lpush,sadd)
### 1. 五大基本数据类型
- String(字符串)string 类型是 Redis 最基本的数据类型,可以包含任何数据,最大能存储 512MB
- Hash(哈希)Redis hash 是一个键值对集合,string类型的key,hash 特别适合用于存储对象
- List(列表)Redis 列表是简单的字符串列表,按照插入顺序排序,按照左右进行区分
- Set(集合)Redis 的 Set 是 string 类型的无序集合,通过哈希实现,添加,删除,查找的复杂度都是 O(1)
- Zset(sorted set:有序集合) Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员,有序树
### 2. 常用的命令
不需要记住所有的命令,但是最起码得了解数据类型有什么方法,适合做什么。
常用命令查找:链接🔗 [Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)](http://redis.cn/commands.html)
避免中文乱码:`redis-cli --raw`
#### (1)String字符串数据常用命令
String在Redis中采用的是动态字符串,相当于Java中的ArrayList结构,有容量和实际大小。当数据大小小于1M时,每次双倍扩容,大于1M时,每次扩容只扩大1M,最大只能到512M。
| 命令(忽略大小写)增删改查 | 描述 |
| ---------------------------------- | --------------------------------------------------------- |
| `SET` key value | 设置key=value |
| `SETRANGE` key start value | 从指定位置开始,插入value |
| `SETNX` key value | 只有在 key 不存在时设置 key 的值 |
| `MSET` k1 v1 k2 v2 ... | 设置多个键和多个值 |
| `MSETNX` k1 v1 k2 v2 ... | 只要有一个失败,那么所有的都失败 |
| `SETEX` key seconds value | 指定的 key 设置值及其过期时间,存在则覆盖 |
| | |
| `DEL` key | 如果存在则删除键,同步删除,删除后再响应 |
| `UNLINK` key | 如果存在则删除键,异步删除,直接响应删除,后续再删 |
| `FLUSHDB` / `FLUSHALL` | 清空当前库/清空所有库 |
| | |
| **一些修改方法五大数据通用** | |
| `APPEND` key value | 如果 key 已经存在并且是一个字符串,拼接上原字符串 |
| `INCR` key | 将 key 中储存的数字类型的value增加1 |
| `INCRBY` key increment | 将 key 中储存的数字类型的value增加给定大小 |
| `INCRBYFLOAT` key increment | 将 key 中储存的数字类型的value增加给定大小浮点数值 |
| `DECR` key | 将 key 中储存的数字类型的value减去1 |
| `DECRBY` key decrement | 将 key 中储存的数字类型的value减去给定大小 |
| `EXPIRE` key seconds | 指定键的过期时间,秒为单位 |
| `EXPIREAT` key timestamp | 指定的键过期时间expire at。在这里,时间是在Unix时间戳格式 |
| `PEXPIRE` key milliseconds | 指定键的过期时间,毫秒为单位 |
| `TTL` key | 查询key还剩多少时间过期,-1永不过期,-2已过期 |
| | |
| **一些查询方法为无大数据通用方法** | |
| `GET` key | 获得键key对应的值 |
| `MGET` k1 k2 ... | 得到所有的给定键的值 |
| `GETRANGE` key start end | 得到value为字符串的子字符串,字符串索引从0开始,左闭右闭 |
| `GETSET` key value | 返回原来的值后,设置新的值 |
| `STRLEN` key | 返回给定的key储存的字符串类型value的长度 |
| `EXISTS` key | 判断给定的key是否存在 |
| `TYPE` key | 返回键对应的value数据类型,五大数据类型 |
| `KEYS` * | 查询所有存在的key |
#### (2)List列表常用命令
双向链表实现实现的栈,头插法和尾插法。命令方法中分出了左和右 `lpush,rpush,lpop,rpop`,注意实现的是双向的栈,左边插入和右边插入不一样,`lpop`和`rpop`的顺序不一样
Redis中,`list`使用的是`quicklist`,即单个块使用的是`压缩链表ziplist(连续的块)`,数据量变大时,多个`ziplist`前后相连形成了`quicklist`。这样避免了每一个节点都储存下一节点的指针冗余。
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | [BLPOP key1 [key2 ] timeout](https://www.runoob.com/redis/lists-blpop.html) 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
| 2 | [BRPOP key1 [key2 ] timeout](https://www.runoob.com/redis/lists-brpop.html) 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
| 3 | [BRPOPLPUSH source destination timeout](https://www.runoob.com/redis/lists-brpoplpush.html) 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
| 4 | [LINDEX key index](https://www.runoob.com/redis/lists-lindex.html) 通过索引获取列表中的元素 |
| 5 | [LINSERT key BEFORE\|AFTER pivot value](https://www.runoob.com/redis/lists-linsert.html) 在列表的元素前或者后插入元素 |
| 6 | [LLEN key](https://www.runoob.com/redis/lists-llen.html) 获取列表长度 |
| 7 | [LPOP key](https://www.runoob.com/redis/lists-lpop.html) 移出并获取列表的第一个元素 |
| 8 | [LPUSH key value1 [value2]](https://www.runoob.com/redis/lists-lpush.html) 将一个或多个值插入到列表头部 |
| 9 | [LPUSHX key value](https://www.runoob.com/redis/lists-lpushx.html) 将一个值插入到已存在的列表头部 |
| 10 | [LRANGE key start stop](https://www.runoob.com/redis/lists-lrange.html) 获取列表指定范围内的元素,0,-1表示所有的值 |
| 11 | [LREM key count value](https://www.runoob.com/redis/lists-lrem.html) 移除列表元素,从索引0开始删除 |
| 12 | [LSET key index value](https://www.runoob.com/redis/lists-lset.html) 通过索引设置列表元素的值,修改原有的value |
| 13 | [LTRIM key start stop](https://www.runoob.com/redis/lists-ltrim.html) 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
| 14 | [RPOP key](https://www.runoob.com/redis/lists-rpop.html) 移除列表的最后一个元素,返回值为移除的元素。 |
| 15 | [RPOPLPUSH source destination](https://www.runoob.com/redis/lists-rpoplpush.html) 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
| 16 | [RPUSH key value1 [value2]](https://www.runoob.com/redis/lists-rpush.html) 在列表中添加一个或多个值 |
| 17 | [RPUSHX key value](https://www.runoob.com/redis/lists-rpushx.html) 为已存在的列表添加值 |
#### (3)Hash哈希字典常用命令
底层就是一个哈希表,快速定位到需要查找的元素。
> 常用的方式是:
>
> key存给定的索引(地址),value存对象的(地址),对象也是一个Map结构,也有对象对应的字段 \ 属性的key和value,这样索引比较快。 索引(哪个对象) + 字段名(对象的哪个字段 \ 属性)
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | [ HDEL key field1 [field2]](https://www.runoob.com/redis/hashes-hdel.html) 删除一个或多个哈希表字段 |
| 2 | [HEXISTS key field](https://www.runoob.com/redis/hashes-hexists.html) 查看哈希表 key 中,指定的字段是否存在。 |
| 3 | [HGET key field](https://www.runoob.com/redis/hashes-hget.html) 获取存储在哈希表中指定字段的值。 |
| 4 | [HGETALL key](https://www.runoob.com/redis/hashes-hgetall.html) 获取在哈希表中指定 key 的所有字段和值 |
| 5 | [HINCRBY key field increment](https://www.runoob.com/redis/hashes-hincrby.html) 为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
| 6 | [HINCRBYFLOAT key field increment](https://www.runoob.com/redis/hashes-hincrbyfloat.html) 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 |
| 7 | [HKEYS key](https://www.runoob.com/redis/hashes-hkeys.html) 获取所有哈希表中的字段 |
| 8 | [HLEN key](https://www.runoob.com/redis/hashes-hlen.html) 获取哈希表中字段的数量 |
| 9 | [HMGET key field1 [field2]](https://www.runoob.com/redis/hashes-hmget.html) 获取所有给定字段的值 |
| 10 | [HMSET key field1 value1 [field2 value2 ]](https://www.runoob.com/redis/hashes-hmset.html) 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
| 11 | [HSET key field value](https://www.runoob.com/redis/hashes-hset.html) 将哈希表 key 中的字段 field 的值设为 value 。
格式:hset user:100 id 1 name lisi age 20 语意:存储了一个对象为100,有属性id,name,age
hset 和 hmset基本一样 |
| 12 | [HSETNX key field value](https://www.runoob.com/redis/hashes-hsetnx.html) 只有在字段 field 不存在时,设置哈希表字段的值。 |
| 13 | [HVALS key](https://www.runoob.com/redis/hashes-hvals.html) 获取哈希表中所有值。 |
| 14 | [HSCAN key cursor [MATCH pattern\] [COUNT count]](https://www.runoob.com/redis/hashes-hscan.html) 迭代哈希表中的键值对。 |
#### (4) Redis 集合(Set)
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | [SADD key member1 [member2]](https://www.runoob.com/redis/sets-sadd.html) 向集合添加一个或多个成员 |
| 2 | [SCARD key](https://www.runoob.com/redis/sets-scard.html) 获取集合的成员数 |
| 3 | [SDIFF key1 [key2]](https://www.runoob.com/redis/sets-sdiff.html) 返回第一个集合与其他集合之间的差异。 |
| 4 | [SDIFFSTORE destination key1 [key2]](https://www.runoob.com/redis/sets-sdiffstore.html) 返回给定所有集合的差集并存储在 destination 中 |
| 5 | [SINTER key1 [key2]](https://www.runoob.com/redis/sets-sinter.html) 返回给定所有集合的交集 |
| 6 | [SINTERSTORE destination key1 [key2]](https://www.runoob.com/redis/sets-sinterstore.html) 返回给定所有集合的交集并存储在 destination 中 |
| 7 | [SISMEMBER key member](https://www.runoob.com/redis/sets-sismember.html) 判断 member 元素是否是集合 key 的成员 |
| 8 | [SMEMBERS key](https://www.runoob.com/redis/sets-smembers.html) 返回集合中的所有成员 |
| 9 | [SMOVE source destination member](https://www.runoob.com/redis/sets-smove.html) 将 member 元素从 source 集合移动到 destination 集合 |
| 10 | [SPOP key](https://www.runoob.com/redis/sets-spop.html) 移除并返回集合中的一个随机元素,随机的一个元素,并删除 |
| 11 | [SRANDMEMBER key [count]](https://www.runoob.com/redis/sets-srandmember.html) 返回集合中一个或多个随机数,返回随机的一个元素,但不删除 |
| 12 | [SREM key member1 [member2]](https://www.runoob.com/redis/sets-srem.html) 移除集合中一个或多个成员 |
| 13 | [SUNION key1 [key2]](https://www.runoob.com/redis/sets-sunion.html) 返回所有给定集合的并集,与差集等类似 |
| 14 | [SUNIONSTORE destination key1 [key2]](https://www.runoob.com/redis/sets-sunionstore.html) 所有给定集合的并集存储在 destination 集合中 |
| 15 | [SSCAN key cursor [MATCH pattern\] [COUNT count]](https://www.runoob.com/redis/sets-sscan.html) 迭代集合中的元素 |
#### (5)Redis 有序集合(sorted set)常用命令
了解:
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | [ZADD key score1 member1 [score2 member2]](https://www.runoob.com/redis/sorted-sets-zadd.html) 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
| 2 | [ZCARD key](https://www.runoob.com/redis/sorted-sets-zcard.html) 获取有序集合的成员数 |
| 3 | [ZCOUNT key min max](https://www.runoob.com/redis/sorted-sets-zcount.html) 计算在有序集合中指定区间分数的成员数 |
| 4 | [ZINCRBY key increment member](https://www.runoob.com/redis/sorted-sets-zincrby.html) 有序集合中对指定成员的分数加上增量 increment |
| 5 | [ZINTERSTORE destination numkeys key [key ...]](https://www.runoob.com/redis/sorted-sets-zinterstore.html) 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中 |
| 6 | [ZLEXCOUNT key min max](https://www.runoob.com/redis/sorted-sets-zlexcount.html) 在有序集合中计算指定字典区间内成员数量 |
| 7 | [ZRANGE key start stop [WITHSCORES]](https://www.runoob.com/redis/sorted-sets-zrange.html) 通过索引区间返回有序集合指定区间内的成员 |
| 8 | [ZRANGEBYLEX key min max [LIMIT offset count]](https://www.runoob.com/redis/sorted-sets-zrangebylex.html) 通过字典区间返回有序集合的成员 |
| 9 | [ZRANGEBYSCORE key min max [WITHSCORES\] [LIMIT]](https://www.runoob.com/redis/sorted-sets-zrangebyscore.html) 通过分数返回有序集合指定区间内的成员 |
| 10 | [ZRANK key member](https://www.runoob.com/redis/sorted-sets-zrank.html) 返回有序集合中指定成员的索引 |
| 11 | [ZREM key member [member ...]](https://www.runoob.com/redis/sorted-sets-zrem.html) 移除有序集合中的一个或多个成员 |
| 12 | [ZREMRANGEBYLEX key min max](https://www.runoob.com/redis/sorted-sets-zremrangebylex.html) 移除有序集合中给定的字典区间的所有成员 |
| 13 | [ZREMRANGEBYRANK key start stop](https://www.runoob.com/redis/sorted-sets-zremrangebyrank.html) 移除有序集合中给定的排名区间的所有成员 |
| 14 | [ZREMRANGEBYSCORE key min max](https://www.runoob.com/redis/sorted-sets-zremrangebyscore.html) 移除有序集合中给定的分数区间的所有成员 |
| 15 | [ZREVRANGE key start stop [WITHSCORES]](https://www.runoob.com/redis/sorted-sets-zrevrange.html) 返回有序集中指定区间内的成员,通过索引,分数从高到低 |
| 16 | [ZREVRANGEBYSCORE key max min [WITHSCORES]](https://www.runoob.com/redis/sorted-sets-zrevrangebyscore.html) 返回有序集中指定分数区间内的成员,分数从高到低排序 |
| 17 | [ZREVRANK key member](https://www.runoob.com/redis/sorted-sets-zrevrank.html) 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
| 18 | [ZSCORE key member](https://www.runoob.com/redis/sorted-sets-zscore.html) 返回有序集中,成员的分数值 |
| 19 | [ZUNIONSTORE destination numkeys key [key ...]](https://www.runoob.com/redis/sorted-sets-zunionstore.html) 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
| 20 | [ZSCAN key cursor [MATCH pattern\] [COUNT count]](https://www.runoob.com/redis/sorted-sets-zscan.html) 迭代有序集合中的元素(包括元素成员和元素分值) |
### 3. 统计数量(菜鸟教程)
#### Redis HyperLogLog
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
#### 什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | [PFADD key element [element ...]](https://www.runoob.com/redis/hyperloglog-pfadd.html) 添加指定元素到 HyperLogLog 中。 |
| 2 | [PFCOUNT key [key ...]](https://www.runoob.com/redis/hyperloglog-pfcount.html) 返回给定 HyperLogLog 的基数估算值。 |
| 3 | [PFMERGE destkey sourcekey [sourcekey ...]](https://www.runoob.com/redis/hyperloglog-pfmerge.html) 将多个 HyperLogLog 合并为一个 HyperLogLog |
# 四. Java操作Redis Jedis
### 准备工作
linux中打开防火墙
```shell
打开端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
检查端口
firewall-cmd --zone=public --query-port=6379/tcp
重启防火墙
systemctl restart firewalld.service
```
### 普通的maven工程操作redis
1. `pom`导入依赖
```xml
redis.clients
jedis
3.2.0
```
2. 测试是否连接成功
```java
public class TestRedis {
public static void main(String[] args) {
//ip 端口
Jedis jedis = new Jedis("你的ip", 6379);
//密码
jedis.auth("mypassword");
//测试连接
String ping = jedis.ping();
System.out.println(ping); // 返回的值是pong
}
}
```
### Jedis使用
Jedis提供的API和Redis的原生方法基本一致。
如Redis中的`set`方法,在Jedis中,直接是`jedis.set()`
# 五 Java操作Redis SpringBoot
说明: 在 SpringBoot2.x 之后, 原来使用的 Jedis 被替换成了 lettuce。
如果不是很高的性能要求,可以采用Jedis,否则需要编写Utils工具类实现序列化。
> jedis: 采用直连, 多个线程操作的话, 是不安全的, 如果想要避免不安全, 使用 jedis pool 连接池 它更像BIO
> lettuce: 采用netty 实例可以多个线程中进行共享, 不存在线程不安全的情况, 可以减少线程数据 它更像NIO
### 1.pom导入依赖
Jedis版本
```xml
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.6.0
```
Lettuce版本
```xml
org.springframework.boot
spring-boot-starter-data-redis
```
### 2. 配置连接信息
```properties
# SpringBoot 所有的配置类, 都有一个自动配置类 RedisAutoConfiguration
# 自动配置类都会绑定一个 peoperties 配置文件 RedisProperties
# 正常使用即可,更多的配置详情redis.cn
spring.redis.host=127.0.0.1
spring.redis.port=6379
```
### 3. Jedis 与 Lettuce
如果采用 `Jedis+Jedis Pool` 可以不用写utils工具类,jedis已经封装完成,如果采用的是 `Lettuce`,仍需要编写utils工具类实现序列化,否则会出现错误。
采用Lettuce版本
#### 自定义Template
```java
@Bean
@SuppressWarnings("all")
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 自定义 String Object
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// Json 序列化配置
Jackson2JsonRedisSerializer