文章目录
下载安装
- 官网:https://redis.io
- 解压直接可以使用:
- redis.windows.conf:配置文件
- redis-cli.exe:redis的客户端
- redis-server.exe:redis服务器端
命令操作
通用命令
- 默认有16个数据库
1. select 1~16 进入指定数据库
2. keys* 查看数据库所有key
3.flushdb 清除当前数据库
4.flushall 清除所有数据库
5. keys * 查看所有key
6. exists name #判断名字是否存在
7. move name 1 #移除 key为name的数据
8. del name #删除key
9. expire name 10 #设置key的过期时间,单位为秒
10. ttl name #查看当前key的剩余时间
11. type name #查看当前key的类型
更多请查看官网:
redis命令
- redis是单线程的,为什么单线程速度还是非常快
核心:redis将所有的数据全部放在内存中,单线程操作效率是最高的,多线程(CPU上下文切换:耗时操作)
redis的数据结构:
- redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构
* value的数据结构:
1) 字符串类型 string
2) 哈希类型 hash : map格式
3) 列表类型 list : linkedlist格式。支持重复元素
4) 集合类型set : 不允许重复元素
5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
字符串类型 string
1. 存储: set key value
2. 获取: get key
3. 删除: del key
4. 追加:append key value
5. 获取长度:strlen key
6. 自增+1:incr key
7. 自增指定步长:incrby key step
8. 自减-1:decr key
9. 截取:getrange key start end #0开始
10. 替换指定位置字符:setrange key
11. setex #set with expire 赋值并设置过期时间
12. setnx #set with not exists 若不存在则设置(在分布式锁中会常常用到)
13. 批量赋值: mset key value ....
14. 批量获取: mget key key ......
15. 先get再set:getset key
列表类型 list:双向链表
- 每一个节点都有指向前一个节点和后一个节点的指针。
- 头节点和尾节点的prev和next指针指向为null,所以链表是无环的。
- 可以添加一个元素到列表的头部(左边)或者尾部(右边)
1. 添加:
1. lpush key value: 将元素加入列表左表
2. rpush key value:将元素加入列表右边
3. lindex key 下标:获取指定下标元素
4. Linsert key BEFORE|AFTER value1 value2:将value2插入到value1之前 | 后
2. 获取:
* lrange key start end :范围获取
3. 删除:
* lpop key: 删除列表最左边的元素,并将元素返回
* rpop key: 删除列表最右边的元素,并将元素返回
* Lrem key count value:删除 num 个值为 value 的元素
4. 修改:
* lset key index value:修改指定位置元素的值,下标越界爆错
6. Llen:获取长度
7. Ltrim key start end:截取指定范围的数组,改变 list(被截断)
8. RpopLpush 源集合 目标集合:源集合右侧弹出一个元素并添加到目标集合
集合类型 set : 不允许重复元素
- 整数集合和字典两种方式来实现,当满足如下两个条件的时候,采用整数集合实现;一旦有一个条件不满足时则采用字典来实现。
- Set 集合中的所有元素都为整数
- Set 集合中的元素个数不大于 512(默认 512,可以通过修改 set-max-intset-entries 配置调整集合大小)
1. 存储:sadd key value
2. 获取:
* smembers key:获取set集合中所有元素
* SrandMember key [count]:随机获取 count 个元素(默认一条)
* sdiff key1 key2 #获取差集
* sinter key1 key2 #获取交集
* sunion key1 key2 #获取并集
3. 删除:
* srem key value:删除set集合中的某个元素
* spop key [count]:随机删除 count 个元素,默认1一条
4. 其它:
* 判断是否包含指定值(返回1 | 0):Sismember key member
* 移动指定元素至其它集合:Smove 源 目标 元素
哈希类型 Hash
相当于 key-map,值为一个map集合
1. 存储:
* hset key field value
* hsetnx key field value #如果不存在则设置反之存在不设置
3. 获取:
* hget key field # 获取指定的field对应的值
* hgetall key #获取所有的field和value
* hkeys key #获取所有 field
* hvals key #获取所有的值
* hlen key #获取hash表的字段数量
4. 删除: hdel key field
5. 其它:
* Hexists key field #判断hash中指定字段是否存在
* HincrBy key field num #将指定值自增 num
有序集合类型 Zset:
- 有序不重复,每个元素关联一个double类型的分数。通过分数进行从小到大的排序。
1.存储:zadd key score value
2. 获取:
* zrange key start end [withscores] # 查询所有 升序排序
* zRevRange key start end # 查询所有 降序排序
* zrangeByScore key min max # 查询 score 在 最小值和最大值之间的数据
* zcard key # 获取有序集合中的个数
* zcount key score1 score2 # 获取分数区间元素的个书
3. 删除:zrem key value
三大特殊类型
geospatial 地理位置
地理位置查询
GEO底层的实现原理就是Zset,可以使用Zset命令操作GEO
redis 1. 添加 * geoAdd key 经度 维度 名称 2. 查看 * geoPos key 名称 #获取指定城市的精确经纬度 * geoDist key 名称1 名称2 单位(mi/km/ft) #获取两城市之间的直线距离 * geoRadius key 经度 维度 num 单位 #查询指定经纬度范围内 key中存在的元素 3. 删除 * zrem key 名称
Hyperloglog 基数统计 不重复
优点:占用的内存是固定的,12KB内存
1. 添加:
* PFadd key 元素 #添加元素到key(不重复)
* PFmerge key1 key2 # 合并 key1 key2 => key1 并集
2. 查看:
* PFcount key # 查看key中元素的个数
3. 删除:
Bitmap 位存储
统计用户信息,活跃,不活跃,登录,未登录,打卡,365打卡
bitmap:位图,数据结构。都是操作二进制来进行记录,只有0和1两个状态
1. 添加
* setbit key 下标 1|0
2. 查看
* getbit key 下标
3. 删除
4. 其它:
* 统计区间1的个数:bitcount key start end
事务
redis 事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,会顺序执行
redis 单条命令式保持原子性的,但是事务不保证原子性
redis 事务中没有隔离级别的概念
- 开启事务(multi)
- 命令入队(… )
- 执行事务(exec )
- 放弃事务(discard)
- 编译型异常( 命令有错 ),事务中所有命令不执行,
- 运行时异常(1/0),其它命令正常执行
示例:127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379> set name zhangsan # 命令入队 QUEUED 127.0.0.1:6379> set gender sex # 命令入队 QUEUED 127.0.0.1:6379> get name # 命令入队 QUEUED 127.0.0.1:6379> get gender # 命令入队 QUEUED 127.0.0.1:6379> exec # 执行事务 1) OK 2) OK 3) "zhangsan" 4) "sex"
redis实现乐观锁
加锁:watch key #监视该key的值,对key进行操作之前,先比较值是否相同,相同则继续,否则失败
解锁:unwatch #解锁
Jedis
- jedis:redis官方推荐的 java连接开发工具,即使用 java 操作 redis
- pom.xml 导入依赖
<!-- jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.6.0</version> </dependency> <!-- fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency>
- 编码测试:
可看到,jedis对象中存在客户端的所有命令
SpringBoot整合redis
说明:springboot2.x之后,原来使用的jedis 被替换为了 lettuce
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池
lettuce:采用netty,实例可以在多个线程中进行共享,不存在进程不安全的情况!可以减少线程数据
- pom.xml导包
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
- 可在 RedisProperties 中查看可以配置的参数
- 使用
@Autowired private RedisTemplate redisTemplate; @Test void contextLoads() { // opsForXXX 操作 XXX // opsForValue() 操作 String // opsForHash() 操作 hash redisTemplate.opsForValue().set("key1","hello"); }
- 序列化
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); // value 序列化配置 Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); template.setDefaultSerializer(objectJackson2JsonRedisSerializer); // key 序列化配置 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); return template; } }
Redis.conf 详解
- 单位
- 包含:可包含其它配置文件
- 网络
* bind 127.0.0.1 # 绑定 ip * protected-mode yes # 保护模式 * port 6379 # 端口
- GENERAL 通用
* daemonized yes # 以守护进程的方式运行,默认 no * pidfile /var/run/redis.pid #如果以后台方式运行,需要指定一个pid文件 * loglevel notice # 日志 * logfile "" # 日志的文件位置名 * databases 16 # 数据库数量,默认16个
- 快照:持久化,规定时间内,执行了多少次操作,则会持久化到文件 .rdb .aof
* save 900 1 # 900秒内,如果至少有 1个 key 进行了修改,则进行持久化 * save 300 10 # 300秒内,如果至少有 10个 key 进行了修改,则进行持久化 * save 60 10000 # 60秒内,如果至少有 10000个 key 进行了修改,则进行持久化 * stop-writes-on-bgsave-error yes # 持久化如果出错,是否继续工作 * rdbcompression yes # 是否压缩 rdb 文件,需要消耗一些 cpu 资源 * rdbchecksum yes # 保存rdb文件时,进行错误的检查校验 * dir ./ # rdb 文件保存的目录
- 复制:REPLICATION
- 安全:SECURITY
* requirepass # 密码
- 限制:LIMITS
* maxclients 10000 # 设置能连接上reids的最大限制 * maxmemory <bytes> # 设置最大的内存容量 * maxmemory-policy noeviction # 内存上限之后的处理策略(六种) 1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 2、allkeys-lru : 删除lru算法的key 3、volatile-random:随机删除即将过期key 4、allkeys-random:随机删除 5、volatile-ttl : 删除即将过期的 6、noeviction : 永不过期,返回错误
- APPEND ONLY MODE:模式,aof配置
* appendonly no # 默认不开启aof模,默认使用rdb方式进行持久化操作 * appendfilename "appendonly.aof" # 持久化文件的名字 # appendfsync always # 每次修改都会 sync,消耗性能 * appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据 # appendfsync no # 不执行 sync,这时候操作系统自己同步数据,速度最快
持久化
- redis持久化机制:指定间隔时间内,检测key的变化,持久化数据
-
RDB:默认
保存的文件:dbfilename dump.rdb- 配置redis.windwos.conf文件
* save 900 1 # 900秒后,至少有一个键被改变 * save 300 10 # 300秒后,至少有10个键被改变 * save 60 10000 # 60秒后,至少有10000个key被改变 * save 50 3 #自定义策略
2. 触发机制 > 1. save规则满足 > 2. 执行 flushall 命令 > 3. 退出redis时 3. 恢复 > 将 rdb 文件放在我们redis启动目录即可,redis启动时自动检查dump.rdb 恢复其中的数据 > 查看存放的位置: ``` 127.0.0.1:6379> config get dir ``` 4. 优点: * 适合大规模的数据恢复 * 对数据的完整性要求不高 5. 缺点 * 需要一定的时间间隔进程操作!若redis意外宕机,则最后一次修改数据消失 * fork 进程时,会占用一定的内存空间
- 配置redis.windwos.conf文件
-
AOF(append only file):将操作过的所有命令(写操作)记录下来,恢复时执行该文件的全部命令
保存的文件:appendonly.aop- 配置 redis.windwos.conf
appendonly yes (开启aof)
- 持久化配置
1. appendfsync always : 每一次操作都进行持久化 2. appendfsync everysec : 每隔一秒进行一次持久化 3. appendfsync no : 不进行持久化
- 修复工具:redis-check-aof
redis-check-aof --fix appendonly.aop
- 优点:
- 每一次修改都同步,文件的完整会更好
- 每秒同步一次,可能丢失一秒的数据
- 从不同步,效率最高
- 缺点:
- 相对于数据文件来说,aof 大于rdb,修复速度也比 rdb 慢
- aof 运行效率比 rdb 慢,所以redis默认配置为 rdb 持久化
- 配置 redis.windwos.conf
Redis 发布订阅
@Configuration
//@EnableCaching // spring缓存需要就开启
public class RedisConfig {
/*序列化*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
/*缓存*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
/**
* redis 监听配置
*
* @param redisConnectionFactory redis 配置
* @return
*/
@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory, MessageListenerAdapter commonListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
container.addMessageListener(commonListenerAdapter, new ChannelTopic(RedisConstant.MSG_CHANNEL));
return container;
}
@Bean
MessageListenerAdapter commonListenerAdapter(RedisReceiver redisReceiver) {
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(redisReceiver, "onMessage");
messageListenerAdapter.setSerializer(jacksonSerializer());
return messageListenerAdapter;
}
private Jackson2JsonRedisSerializer jacksonSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
}
发送消息:redisTemplate.convertAndSend(RedisConstant.MSG_CHANNEL,new JSONObject());
@Component
public class RedisReceiver {
@Autowired
private WebSocket webSocket;
/**
* 接受消息并调用业务逻辑处理器
*/
public void onMessage(JSONObject map) {
System.out.println("收到数据",map);
}
}
Redis 主从复制
-
概念:主从复制,是指将一台redis服务器数据复制到其它的redis服务器上,前者称为主节点(master/leader),后者称为从结点(slave/follower);数据的复制是单向的,只能由主节点到从节点,master以写为主,slave以读为主
-
主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余
- 负载均衡:当主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个结点分担读负载,可以大大提高redis服务器的并发量
- 高可用(集群)基石:除了上述作用意外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是redis高可用的基础
-
一般来说,要将redis运用于工程项目中,只使用一台redis是万万不能的(宕机,一主二从),原因如下:
-
从结构上,单个redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大
-
从容量上,单个redis服务器内存容量有限,就算一台redis服务器内存容量为256G,也不能将所有内存作redis存储内存,一般来说,单个redis最大使用内存不应该超过 20G
-
电商网站的商品,属于 读多写少,推荐使用以下架构
主从复制,读写分离!80%的情况下都是在进行读操作!架构中经常使用!一主二从
-
环境配置
- 查看当前库的信息
- copy三份配置文件,修改端口,rdb文件名,启动
127.0.0.1:6379> info replication # info查看信息 # Replication role:master # 角色master connected_slaves:0 # 0个从机 master_replid:34da02abad8667ca3861112dbebf94d98a524bc0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
- 默认情况下每台redis服务都是主节点;一般情况下只用配置从机
slaveof ip 端口