redis的常用命令 启动redis // 启动redis ./redis-server // 启动并加载配置文件 ./redis-server /usr/local/redis/bin/redis.conf // 设置后台启动并指定端口号 ./redis-server /usr/local/redis/bin/redis.conf --daemonize yes --port 1111 查看是否启动成功 // 查看进程 ps -ef |grep redis // 查看端口号 lsof -i:6379 停止redis // 没密码停止 ./redis-cli shutdown // 有密码停止 ./redis-cli -a 123456 shutdown
客户端密码登录 ./redis-cli auth 123456
查看redis的配置信息 // 登录redis客户端 ./redis-cli auth 123456 // 输入info命令 info 查看密码 //登录客户端后,再获取密码 redis 127.0.0.1:6379> config get requirepass
redis的基本数据类型 字符串类型(String) set aa 123 // 存值 set aa 123 ex 10 // 设置有效期10秒 get aa // 取值 del aa // 删除 keys * // 获取所有 列表类型(List) lpush list aa // 存值 lpush list aa // 存值 lrange list 0 10 // 按下标取值 无序集合(Set) 无序,且唯一 sadd aa java // 存值 sadd aa php // 存值 smembers aa // 取值 有序集合(sorted set) 有序,且唯一 zadd cc 1 java // 存值 zadd cc 2 php // 存值 zrange cc 0 10 withscores // 取值 哈希(Hash) hmset person name "admin" age 18 phone "13111111111" // 存值 hgetall person // 取值 命令文档参考: https://www.runoob.com/redis/redis-strings.html
Redis默认的情况下分为16个库
db0-db15 默认使用db0库
springboot集成redis 创建springboot项目在pom.xml中引入依赖 <!-- 引入redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>在application.properties配置redis ###Redis配置 spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password=123456 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接超时时间(毫秒) spring.redis.timeout=5000
创建RedisService.java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * Create by wangxb * 2019-05-27 17:40 */ @Service public class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate redisTemplate; // 存String public void saveString(String key, String value){ stringRedisTemplate.opsForValue().set(key, value); } // 获取String public String getString(String key){ return stringRedisTemplate.opsForValue().get(key); } // 删除 public void delByKey(String key){ stringRedisTemplate.delete(key); } // 存String设置时间 public void saveStringAndTime(String key, String value, Long timeOut){ stringRedisTemplate.opsForValue().set(key, value, timeOut , TimeUnit.SECONDS); } }
mybatis如何使用redis作为缓存 使用@Cacheable注解 // 数据库查询接口 @Mapper public interface EmpMapper { @Select("select * from emp") List<EmpEntity> findAll(); } // controller @RequestMapping("/findAll") @Cacheable(cacheNames = "emp", key = "'findAll'") public List<EmpEntity> findAll() { return empMapper.findAll(); } @Cacheable(cacheNames = "emp", key = "'findAll'") 将从数据库查询的数据保存到redis中,后面都是从redis中查询。 emp表示文件夹 findAll表示 key, 如下图:
redis过期事件通知 修改服务器redis配置 // 修改配置文件 vi redis.conf // 修改配置文件内容,默认注释掉的 将 # notify-keyspace-events Ex 改成 notify-keyspace-events Ex 开启事件通知, 重启redis创建spingboot项目并整合redis 创建redis监听类 RedisListenerConfig.java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.listener.RedisMessageListenerContainer; @Configuration public class RedisListenerConfig { @Bean RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); return container; } } RedisKeyExpirationListener.java import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; @Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } /** * Redis的key过期通知事件 */ @Override public void onMessage(Message message, byte[] pattern) { String key = message.toString(); System.out.println("该key: "+key+" 到期失效了!!!"); } } 启动项目测试 提前保存数据并设置时间,查看控制台信息: 原理: 底层是基于发布订阅实现。可以通过配置在集群环境、多个消费者情况下: 不会产生重复监听失效的问题。
redis实现分布式锁 思路 通过redis的setnx命令实现 // 如何当前key没被使用返回1 127.0.0.1:6379> setnx name admin (integer) 1 // 如何当前key已被使用返回0 127.0.0.1:6379> setnx name admin (integer) 0 127.0.0.1:6379> setnx name ben (integer) 0 redis如何存java对象 使用json实现序列化和反序列化 使用StringRedisTemplate.java // 保存 String str = JSONObject.toJSONString(userEntity); redisService.saveString(key, str); // 获取 String str = redisService.getString(key); UserEntity userEntity = JSONObject.parseObject(str, UserEntity.class); 保存的是json字符串将对象实现Serializable.java进行序列化和反序列化 // 使用 RedisTemplate<k, v> // 将UserEntity对象实现Serializable接口 保存二进制文件Resi的主从复制 什么是redis的主从复制 就是多个Redis数据库之间的数据同步优点 实现数据库的读写分离提高服务器的负载能力,主数据库主要进行写操作,而从数据库负责读操作。图解 如图所示: 136作为主库,具有读写功能,从数据库137、138只能读不能写。 当136数据库写入数据时,会同步更新到从数据库137、138中。 配置主从复制 在从数据库137、138的redis.conf配置信息如下: // 编辑137、138服务器的redis.conf slaveof 192.168.33.136 6379 // 设置136为主数据库 masterauth 123456 // 主数据库如果要密码,这里就需要配置主数据库的密码
配置完成后,可以在136、137、138的redis.cli中输入: info 命令,查看配置的信息配置成功:我们测试在136主数据库,存值时,137、138从数据库去取值 redis的哨兵机制 什么是哨兵机制如图: 当哨兵检测到Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将其中一个Slave升级为新的Master(并更新你的哨兵配置文件), 并让其他的Slave数据库改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。 可以在从redis中选择一个服务器作为哨兵。建议配置多台哨兵。哨兵不能实现数据的同步如何配置哨兵机制,我们将137从数据库配置哨兵 // 编辑redis的sentinel.conf配置文件。 vi sentinel.conf ##########修改配置文件的内容, 如下: ############ // 哨兵启动模式为后台启动 默认no daemonize yes // 设置监听主redis的ip、端口 1: 表示多台哨兵的话,只要1台确认主redis宕机则就重新选举 sentinel monitor mymaster 192.168.33.136 6379 1 // 设置主redis密码 sentinel auth-pass mymaster 123456 // 设置等待的超时时间 该改为5000(5秒) sentinel down-after-milliseconds mymaster 30000 // 最多多少个从节点 sentinel parallel-syncs mymaster 2 保存启动redis // 启动redis ./redis-server redis.conf // 启动哨兵 ./redis-sentinel sentinel.conf
redis事务 Redis 事务可以一次执行多个命令redis事务相关命令
序号
命令及描述
1
DISCARD 取消事务,放弃执行事务块内的所有命令。
2
EXEC 执行所有事务块内的命令。 提交事务
3
MULTI 标记一个事务块的开始。 开始事务
4
UNWATCH 取消 WATCH 命令对所有 key 的监视。
5
WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
redis的持久化 什么是持久化 就是将内存数据保存到硬盘。 Redis 持久化存储 (AOF 与 RDB 两种模式) RDB: 以数据文件的形式进行保存备份。 redis默认使用RDBAOF: 以日志文件的形式进行保存备份。 RDB持久化 配置RDB持久化 ################以下为redis.conf的默认配置###################### #持久化数据保存到本地的文件名 dbfilename dump.rdb #持久化数据保存到本地的文件路径 ./表示当前路径 dir ./ #如下为900秒内,有1次变更操作,就会生成rdb数据文件。 #如下为300秒内,有10次变更操作,就会生成rdb数据文件 save 900 1 save 300 10 save 60 10000 #当出现错误时,是否阻塞客户端的操作 默认: yes stop-writes-on-bgsave-error yes #是否启用压缩文件形式保存rdb文件,默认: yes rdbcompression yes如上: RDB是以时间进行周期性全量保存dump.rdb数据文件。 默认时间为: 900秒、300秒 、60秒 redis每次启动时,都会自动加载dump.rdb数据文件。 缺点:安全性不高,如果操作了9次且时间还未到,redis挂了,那么之前9次的数据就不会保存。
AOF AOF即: Append-only file 将每次的操作命令和数据保存到日志文件(不包括查询命令),redis在还原数据时,就会读取该日志文件的所有操作命令和数据在内部重新执行一次。 就和mysql中bin.log类似虽然AOF会造成磁盘 IO 的负荷加重,但是相对可靠安全,所以一般都是使用aof,配置AOF持久化 ##开启aof功能, 默认no appendonly yes ##指定aof文件名称 appendfilename appendonly.aof ##指定aof的同步策略,有三种:always everysec no, 默认为everysec appendfsync everysecAOF的同步策略 : appendfsync always #表示每次有数据修改时就写入AOF文件,能够保证数据不丢失,但是效率低。 appendfsync everysec #表示每秒钟同步一次,可能会丢失1s内的数据,但是效率非常高。推荐 appendfsync no #从不同步
AOF与RDBAOF 更加安全,可以将数据更加及时的同步到文件中,但是 AOF 需要较多的磁盘 IO 开支,AOF 文件尺寸较大,文件内容恢复数度相对较慢。RDB安全性较差,它是“正常时期”数据备份以及 master-slave 数据同步的最佳手段,文件尺寸较小,恢复数度较快。 redis的淘汰策略 应用场景 redis在使用内存不足的情况下,就会采用策略清除部分数据,释放部分内存。 redis的六种淘汰策略 noeviction:当内存使用达到阈值的时候,执行命令直接报错 allkeys-lru:在所有的key中,优先移除最近未使用的key。(推荐) volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。 allkeys-random:在所有的key中,随机移除某个key。 volatile-random:在设置了过期时间的键空间中,随机移除某个key。 volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。 配置redis的淘汰策略 默认没有配置,是根据redis申请到的物理空间大小来决定 // 1、编辑redis.conf vi redis.conf // 在配置文件中修改内容 将 # maxmemory <bytes> 比如修改成 maxmemory 100mb 将 # maxmemory-policy noeviction 修改成 maxmemory-policy allkeys-lru redis如何解决缓存穿透问题 什么是缓存穿透 比如订单查询功能。用一个不存在的订单Id,在redis和数据库中都不存在,这样缓存就是去了意义。 如果用大量不存在的id并发访问,导致缓存无法命中,数据库也查询不,但是每次请求还是要穿透到后端数据库,这样就会造成你的数据库 压力。 如何解决缓存穿透 a. 接口限流 b. 使用布隆过滤器 布隆过滤器原理 采用的是bit(0/1)数组和哈希函数。通过key生成3个hash值,再将hash值锁对应的bit位置改为1。 代码实现 依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency> 伪代码 import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import java.util.ArrayList; import java.util.List; /** * @author xiaobo * @Description Bloom * @createTime 2020-03-16 20:58 */ public class Bloom { BloomFilter<Integer> bloomFilter = null; // 获取所有订单id private List<Integer> getAllOrderId(){ List<Integer> orderList = new ArrayList<Integer>(); for (int i = 0; i< 1000000; i++) { orderList.add(i); } return orderList; } public void initBloomFilter(){ // 提前获取所有订单id List<Integer> orderIds = getAllOrderId(); // 将订单id存在布隆过滤器容器中 0.01数据越小,数组越大,误判概率也就越小 bloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01); for (Integer orderId : orderIds) { bloomFilter.put(orderId); } } public void findOrder(Integer id) { // 布隆过滤器判断容器内是否有这个订单id if (!bloomFilter.mightContain(id)) { System.out.println("伪造的订单id...."+id); return ; } System.out.println("开始从redis、mysql查询订单......"+id); } } 测试 public static void main(String[] args) { Bloom bloom = new Bloom(); bloom.initBloomFilter(); bloom.findOrder(1); bloom.findOrder(10000000); } 代码思路: 1) 提前将数据库所有的订单号存在布隆过滤器容器内,这里我是伪造的订单: 1~1000000 2) 在查询时,先判断容器内是否存在,如果不存在,则判断为伪造订单号。否则执行查询..... 布隆过滤器产生的问题 因为布隆过滤器底层采用的时二进制[0 1]数据,会根据key计算hash值的出存放的位置,这样就会导致新的key计算出的hash值与容器内已存放的元素hash产生冲突,这样就会产生误判可能存在。 如何解决这个问题:二进制数组长度设置比较大,可以减少布隆误判的概率。 Redis如何解决缓存雪崩问题 产生原因 如果我们缓存在redis数据过期时间是相同的。这就就会导致在这段时间内,这些缓存同时过期失效,全部请求到数据库中。增加数据库压力。 解决思路 对不同类型的数据,设置不同的有效期。 Redis如何解决缓存击穿问题 产生原因 缓存击穿,就是说某个 key对应的数据访问非常频繁,属于高并发式访问,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,造成数据库访问压力大。解决思路 可以将热点数据设置为永不过期 使用分布式锁,当缓存失效时,只允许一个线程去访问,在缓存到redis中。 Redis Cluster集群 RedisCluster集群的原理 Redis哨兵机制在同步中,要求每个redis节点服务器都要保存master节点的全量数据,冗余的数据比较多; Redis3.0开始官方推出了集群模式 RedisCluster,采用分片集群模式,可以减少冗余数据。 采用hash槽的原理,预先设定16384个卡槽,并将卡槽分配各个master服务器;通过key进行crc16(key)384 获取余数,余数就是对应的卡槽的位置,然后将数据存放在卡槽对应的服务器上。一个卡槽可以存放多个值,从而将读或者写转发到该卡槽的服务的节点。 缺点: 就是构建该集群模式需要服务器多、成本高。 优点:动态实现服务器的扩容、缩容。 redis的发布订阅 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。例子:有A、B数据库:启动A的redis-cli输入:SUBSCRIBE redisChat // 表示A订阅了redisChat这个频道的消息启动B的redis-cli输入:PUBLISH redisChat "message1" // 表示B向redisChat这个频道的发送消息 A可以实时获取往redisChat这个频道发送的消息发布订阅相关方法序号
命令及描述
1
PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。
2
PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。
3
PUBLISH channel message 将信息发送到指定的频道。
4
PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。
5
SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。
6
UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。
redis的卸载 查看redis服务器 ps aux | grep redis停止进程a kill -9 PID删除相关文件 rm -rf /usr/local/redis // 删除安装目录 rm -rf /usr/bin/redis-* // 删除所有redis相关的命令脚本