1.redis基础
redis有16个数据库 默认使用第0个 默认端口6379 redis基于内存 瓶颈是机器内存和网络带宽 单线程的 为什么redis单线程效率高? redis所有数据在内存上 多线程cpu会上下文切换 耗时操作 对于内存系统来说 多次读写在一个cpu上效率最高 基本命令(记住): select 3 //切换到第3号数据库 dbsize //查看该数据库存有多少个数据 keys * //查看数据库所有的key flushdb //清空该数据库数据 flushall //清空所有数据库的数据 exists name //是否存在name这个key 存在1 不存在0 move name 1 //将name这个key移除 1表示当前库 expire name 1 //name这个key 10秒后过期 ttl name //查看key剩余过期时间 type name //查看key的类型
2.String字符串
基本命令 append key "hello" //往key追加字符串hello key若不存在就新增 strlen key //获取字符串长度 incr key // 自增1 相当于i++ incrby key 8 //自增8 相当于i+=8 decr key //自减1 decrby key 8 //自减8 getrange key 0 3 //截取字符串 相当于substring setrange key 1 xx //替换字符串 相当于replace //重点(常用) setex key 30 "hb" //添加值并设置过期时间 setnx key "hb" //不存在key添加 存在key添加失败(分布式锁中常用到) mset k1 v1 k2 v2 //批量添加 mget k1 k2 //批量获取 msetnx k2 v2 k3 v3 //同上是批量添加 若k2或k3只要有已存在key就添加失败 getset key v1 //先取得key的值并设置新的值
3.List
redis的list就是栈 链表 后进先出 push进 pop出 但可以从左或从右添加元素 list命令基本是l开头的 基本命令 lpush key one //从左边添加元素 rpush key one //从右边添加元素 lrange key 0 1 //获取起始到终止位置的元素 0 -1是所有的元素 lpop key //从左边移除第一个元素 rpop key //从右边移除第一个元素 lindex key 0 //获取下标0的元素 llen key //获取list的长度 lrem key 1 one //移除list中1个value为one的值 ltrim key 1 2 //只保留list中下标1到2的值 其它值被删除 rpoplpush key key1 //把key最右边元素剪切到key1中 exists key //是否存在key lset key 0 value1 //更新操作 将下标0的值替换为value1 下标0无值报错 linsert key before value1 insvalue //list中value1的值前加上一个元素 这个元素的值为insvalue. 其中before/after往前/后插入
4.Set
set命令基本s开头 基本命令 sadd myset value1 //添加元素 smembers myset //获取set中所有值 sismember myset value1 //set中是否包含value1这个元素 scard myset //获取set元素个数 srem myset value1 //移除set中指定的元素 srandmember myset 2 //随机抽选set集合指定个数的元素 spop myset //随机删除set中的元素 smove myset1 myset2 value1 //set集合myset1中的value1剪切移动到myset2中 sdiff myset myset1 //取第一个集合与第二个集合的差集(1集合有2集合没有) sinter myset myset1 //取第一个集合与第二个集合的交集(2个集合都有的元素 共同好友这个做) sunion myset myset1 //取并集(2个集合整合)
5.hash
hash命令基本h开头 hset myhash key1 value1 //添加hashmap hget myhash key1 //获取hashmap hmset myhash k1 v1 k2 v2 //批量添加hashmap hmget myhash k1 k2 //批量获取hashmap hgetall myhash //获取hash所有值(kv都显示) hdel myhash key1 //删除 hlen myhash //获取hashmap个数 hexists myhash k1 //hashmap中指定字段是否存在 hkeys myhash //获取所有key hvals myhash //获取所有values hincrby myhash k1 1 //自增1 hsetnx myhash k1 v1 //不存在创建 存对象如用户对象 用string: set user:1:name hb 用hash: hset user:1 name hb 用hash更好一点
6.zset(有序集合)
就把它看成linkhashset 基础语法 基本是z开头 zadd myzset 1 v1 2 v2 //添加元素(可以批量添加) zrange myzset 0 -1 //正序显示所有元素(zrevrange是反序) zrangebyscore myzset -inf +inf [withscores] //按-无穷到+无穷排序显示(inf是无穷的意思 []可选 withscores也打出下标 ) zrem myzset two //移除元素 zcard myzset //获取集合元素个数 zcount myzset 1 3 //获取指定区间的元素个数 与set命令的api基本一致 应用范围: 排行榜//数量存下标 能排列显示排行信息
7.geospatial
地理位置 可以推算位置信息和距离 命令geo开头 //南北极无法导入 一般情况下直接下载地理位置信息导入到redis geoadd mygeo 116.40 39.90 beijin //添加地理位置 geoadd mygeo 121.47 31.23 shanghai 121.47 31.23 shanghai1 //也可以批量添加 geopos mygeo shanghai //获取指定城市的经纬度 geodist mygeo beijin shanghai km [withcoord] //两地间距离(m米 km千米 mi英里 ft英尺子) georadius mygeo 120.3 30.2 1000 km [withcoord] [withdist] [count 1] //获取以指定经纬度为中心半径1000km范围内的元素 //withcoord显示元素的坐标 withdist显示到中心的距离 count允许显示元素的个数 georadiusbymember mygeo shanghai 100 km //同上 中心坐标改为指定的元素 //geo底层其实就是zset 因此可以用zet命令处理geo zrange mygeo 0 -1 //显示地图所有元素 zrem mygeo shanghai //移除指定元素
8.Hyperloglog 基数
基数(不重复的元素) 如{1,3,5,7,8,7}基数是{1,3,5,7,8} 应用场景:如网站访问统计人数,同一人多次访问算一人 传统方式:set保存用户id 存大量用户id比较麻烦 因为目的只是为了计数 Hyperloglog:基数统计算法 且占用内存固定 只需12kb 但有0.81%错误率 基本语法 pf开头 pfadd mypfkey a b c d e d //添加 pfcount mypfkey //统计基数数量 5 pfmerge mypfkey3 mypfkey2 mypfkey1 //mypfkey3的值为mypfkey1和2的并集 注:Hyperloglog有容错率0.81% 不允许容错的话就不能用
8.Bitmaps
位存储 操作二进制 只有0和1两种状态 基本命令 setbit mybit 0 1 //在第一位置添加1 getbit mybit 0 //获取第一位的值 bitcount mybit //统计值为1的数量
9.事务
原子性:要么同时成功 要么同时失败 一致性:事务的执行使得数据库从一种正确状态转换成另外一种正确状态 隔离性:在事务正确提交之前,它可能的结果不应该显示给其他事务 持久性:事务正确提交之后,其结果将永远保存在数据库之中 redis事务不保证原子性 保证一致性 顺序性 排他性 redis事务流程: 1.开启事务 2.依次输入redis命令 3.执行事务 基本命令 multi //开启事务 exec //事务结束 开始执行 discard //取消事务 事务队列中的命令都不会执行
10.乐观锁
悲观锁:无论什么情况都会加锁 乐观锁:不会加锁 只有在更新字段的时候会去判断期间是否有人改过数据 用watch监控 mysql的乐观锁是version监控 如: 乐观锁付款事务正常流程: set money 100 set out 0 watch money //监控money 若money变了 下面事务exec执行失败 multi decrby money 20 incrby out 20 exec //事务成功会自动解锁 //如果事务提交失败应该是money被改了 执行下面失败的流程 unwatch //解锁 watch money //再加锁 锁定money的最新值 ... //继续把事务走一遍
11.jedis
添加依赖: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency> 测试: Jedis jedis = new Jedis("127.0.0.1",6379); jedis.get("key"); ------------------------------------------------------------------- springboot整合 springboot2.x后jedis被替换为lettuce 1.添加依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2.yml中添加配置: spring.redis.host spring.redis.port等 3.注入: @Autowired private RedisTemplate redisTemplate; 操作: redisTemplate.opsForValue().get("");//操作string opsForList(). //操作list 等 测试:redisTemplate.opsForValue().set("name1","springboottest"); 到redis服务器发现插入的key乱码 其实不是乱码 是因为RedisTemplate默认了使用jdk序列化
用代码获取都是正常的 只是用命令在服务器上看不太方便 //4 5都是企业级开发需要的方式 4.配置一个自己的RedisTemplate配置类处理序列化问题 所有key采用string序列化 value采用json 具体详情看项目中的测试类RedisConfig 再测试: redisTemplate.opsForValue().set("name2","springboottest"); 到redis服务器发现插入的key是name2了 value是"springboottest" 5.使用原生redisTemplate的api不太方便 可以自己封装一个工具类 详细的看测试类中utils中的RedisUtil jedis采用直连 多线程不安全 还需使用jedis pool连接池 BIO模式 lettuce采用netty 实例多线程中共享 不存在线程不安全情况 可以不使用线程池减少线程数量 NIO模式
12. redis.conf配置文件详解
学习看redis.conf 打开redis.conf 1.# units are case insensitive so 1GB 1Gb 1gB are all the same. redis对大小写不敏感 2.# include /path/to/local.conf # include /path/to/other.conf 类似spring的import include 能包含其它的配置文件 3.NETWORK bind //允许访问的ip protected-mode yes //保护模式 默认开启 若关闭则允许所有外部访问 开启则允许bind的ip访问 port //端口设置 4.GENERAL 通用配置 daemonize yes //守护进程 默认no yes允许退出继续在后台运行 logfile /usr/local/bin/config/redislog.log //日志文件路径 databases 16 //数据库数量 always-show-logo yes //启动是否显示redis logo 5.SNAPSHOTTING快照 持久化 在规定时间内执行多少次操作能持久化到文件 .rdb .aof redis是内存数据库 没有持久化数据断电丢失 save 900 1 //900秒内至少1个key修改 就进行持久化操作 save 300 10 save 60 10000 stop-writes-on-bgsave-error no //若持久化失败是否停止工作 rdbcompression yes //是否压缩rdb文件 压缩会消耗cpu资源 rdbchecksum yes //保存rdb文件时是否校验 dir ./ //rdb文件保存目录 6.REPLICATION 主从复制 7.SECURITY 安全 # requirepass foobared 登录redis密码 默认没有密码 设置登录密码2种方式 1.在配置文件加requirepass 1234 2.redis-cli命令输入 config get requirepass //查密码 config set requirepass "1234" //设置密码 //设置密码后发现所有命令没权限了 auth 1234//登录 8.CLIENTS 限制 # maxclients 10000 //允许连接redis最大客户端的数量 # maxmemory <bytes> //redis配置最大内存容量 # maxmemory-policy noeviction //内存到上限处理策略 9.APPEND ONLY MODE aof模式 appendonly no //默认不开启aof模式(用rdb模式) appendfilename "appendonly.aof" //aof持久化文件名称 appendfsync everysec //每秒同步一次(可能丢失1秒数据) # appendfsync always //每次修改都同步(消耗性能) # appendfsync no //不同步
13.Redis持久化
rdb:
rdb保存的文件名是dump.rdb 1.触发生成rdb备份文件的规则: 1.配置文件中配置的规则满足触发 如save 900 1(900秒内有改过1次key) 2.flushall 3.shutdown退出redis时 2.恢复rdb文件数据 把rdb文件放到redis启动目录下自动恢复rdb中数据 优点: 1.适合大规模数据恢复 2.对数据完整性要求不高 缺点: 1.需要一定的时间 若意外宕机 最后一次修改数据没有了 2.子进程去生成rdb 会暂用一定的内存
aof:
aof保存的文件名是appendonly.aof aof将所有的命令都记录下来 可以看成记录命令的日志文件 恢复的时候将全部命令执行一遍重新构建 redis.conf 中appendonly yes开启aof aof文件生成规则: # appendfsync always //每次修改同步生成 appendfsync everysec //每秒同步生成 # appendfsync no //不生成 注:若aof文件错误 redis启动会报错 修复aof文件方式:redis-check-aof --fix appendonly.aof 优点: 1.appendfsync always //每次修改同步生成 数据完整性好 appendfsync everysec //每秒同步生成 数据完整性较好 但可能会丢失最后1秒数据 缺点: 1.aof远大于rdb 修复速度比rdb慢 2.aof运行效率比rdb慢
扩展: 1.redis只做缓存的话 不用弄持久化 2.同时开启rdb和aof 优先载入aof 因为aof数据更完整
14.redis发布订阅
SUBSCRIBE runoobChat //订阅 PUBLISH runoobChat "Redis PUBLISH test" //发布 只能做一些简单的发布订阅 复杂的用消息中间件mq做
15.redis主从复制
主从复制:将一台redis服务器的数据复制到其它redis服务器 主节点(master/leader) 从节点(slave/follower) 数据只能由主节点到从节点 主节点写 从节点读(读写分离) 80%的压力是读取 把读取的压力放到从节点 主从复制主要作用 1.数据冗余:主从复制实现了数据热备份 是持久化外的一种数据冗余方式 2.故障恢复:主节点出问题 可以由从节点提供服务 以快速恢复。是一种服务冗余 3.负载均衡:主从复制配合读写分离 主节点写入 从节点读取 写少读多的情况下分担负载 提高性能 环境配置 redis-cli查看主从复制的信息: info replication 显示role就是这台redis是主机(master)或从机(slave) -------------------------------------------------------------------- 配置主从机方式(假设配置一主二从): 1.配置主从机需要改的配置(redis.conf)(就是把主从机的配置信息和其它主从机区分开): //3个redis.conf都需要配置 port //redis端口区分开 pidfile //pid命名区分开 logfile //区分log命名 dbfilename //rdb文件命名区分 2.配置好后3台默认都是主机 那么选2台当从机 从机需要选定一台当它的主机 //重点 在从机中的redis-cli中输入slaveof 127.0.0.1 6379 //即让自己由主机变从机 它主机的地址是127.0.0.1 6379 slaveof no one //本机作为从机失效 恢复为主机 3.步骤2是通过命令配置主从 不是永久的 在redis.conf中配置 # replicaof <masterip> <masterport> 写入对应的ip 端口就好了 如果主机有密码则在下面配置 # masterauth <master-password> 注:1.主机只负责写 从机只负责读 2.主机写入的从机都会同步数据 3.主从复制虽然用slaveof命令的方式每次重启都需要重新配置 但还是比 在配置文件中配置更好一点 因为若出现某一台宕机 可以用命令快速的 切换主从
16.哨兵模式
主从模式中如果主机宕机 得手动切换主从(把其中一台从机换成主机) 人工处理耗时 所以优先考虑哨兵模式 哨兵是一个独立的进程 其原理是哨兵发送命令监控多个redis实例 监控是否宕机 redis-sentinel 一个哨兵可能有问题 可以用多个哨兵对多个redis服务监控 哨兵间也互相监控 (用哨兵模式建议6个服务器 1主2从3哨兵) 配置哨兵方式: 1.建一个哨兵配置文件sentinel.conf #sentinel monitor 被监控名称 host ip 1//1是多少个哨兵认为主机宕机时开始投票选一台从机当主机 host ip是主节点的redis sentinel monitor myredis 127.0.0.1 6379 1 2.启动哨兵 redis-sentinel sentinel.conf 再次梳理流程: 新建哨兵配置文件 配置哨兵监听的主节点主机 主节点宕机后被至少1个哨兵监听到时进行投票(被多少个哨兵监听到宕机是上述配置文件中定义的) 投票算法会选择一台从节点作为新的主节点 若宕机的那台主机恢复了 哨兵会自动把宕机的主机变更为从机 优点: 1.哨兵基于主从复制 所有主从复制的优点都有 2.故障可以转移 高可用性 3.哨兵模式就是主从复制的升级版 手动到自动 缺点: 1.不容易在线扩容 2.哨兵模式全局配置内容比较多
17.redis缓存穿透和雪崩
缓存穿透(查不到导致):用户查一个数据 缓存没有导致查数据库 数据库也没有导致重复查询 查询很多时导致给数据库造成很大压力 这就是缓存穿透 解决方式: 1.数据库没有的数据redis缓存设空对象 减轻数据库压力 2.布隆过滤器 //布隆过滤器是一个数据结构 对所有可能的查询的参数以hash存储 在控制层进行校验 不符的丢弃以缓解redis及数据库的压力 缓存击穿(查量太大缓存过期导致):是比缓存穿透更严重一级 一个热词用户搜索量非常大 但redis的key过期 就在过期的这一瞬间高并发都到数据库 服务器这一瞬间没抗住 解决方式: 1.key设不过期 2.分布式锁 每个key只用一个线程去查询数据库 其余的等待 缓存雪崩:某一段时间 缓存集体失效或redis宕机 解决方式: 1.停掉一些服务保证主要服务可用 如双11退单服务停用 2.数据预热 预先将可能大部分访问的数据访问一遍加载到缓存中 3.redis高可用 就是多设几台redis 4.限流降级 缓存失效的话通过加锁或队列控制读写访问数据库的线程 如对某一key只允许一个线程查询