redis常见问题(一)
主流应用架构
redis和memcache的区别
memcache
- 支持简单数据类型
- 不支持数据持久化存储
- 不支持主从
- 不支持分片
Redis
- 数据类型丰富
- 支持数据磁盘持久化存储
- 支持主从
- 支持分片
redis为什么这么快?——10万+QPS(query per second,每秒查询次数)
- 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高
- 数据结构简单,对数据操作也简单
- 采用单线程,单线程也能处理高并发请求,想多核也可以启动多实例
- 使用多路I/O复用模型,非阻塞IO
redis采用的I/O多路复用函数:epoll/kqueue/evport/select?
- 因地制宜
- 优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现
- 以时间复杂度为O(n)的select作为保底
- 基于react线程模型监听I/O事件
redis的数据类型
- String:最基本的数据类型,二进制安全
- Hash:String元素组成的字典,适合用于存储对象
- List: 列表,按照String元素插入顺序排序(实现最新消息排行榜)
- Set:String元素组成的无序集合,通过哈希表实现,不允许重复
- Sorted Set(ZSet):通过分数来为集合中的成员进行从小到大的排序
- 用于计数的HyperLogLog,用于支持存储地理位置信息的Geo
从海量Key里查询出某一固定前缀的Key
KEYS pattern:查找所有符合给定模式pattern的key
- KEYS指令一次性返回所有匹配的key
- 健的数量过大会使服务器卡顿
SCAN cursor [MATCH pattern] [COUNT count]
- 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
- 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历
- 不保证每次执行都返回某个给定数量的元素,支持模糊查询
- 一次返回的数量不可控,只能是大概率符合count参数
模拟海量数据查询,往redis中灌入2千万的数据
通过编写Shell脚本来注入数据:
批量生成redis测试数据
- Linux Bash下面执行 for((i=1;i<=20000000;i++)); do echo "set k$i v$i" >> /tmp/redisTest.txt ;done; 生成2千万条redis批量设置kv的语句(key=kn,value=vn)写入到/tmp目录下的redisTest.txt文件中
- 用vim去掉行尾的^M符号,使用方式如下:: vim /tmp/redisTest.txt :set fileformat=dos #设置文件的格式,通过这句话去掉每行结尾的^M符号 ::wq #保存退出
- 通过redis提供的管道--pipe形式,去跑redis,传入文件的指令批量灌数据,需要花10分钟左右 cat /tmp/redisTest.txt | 路径/redis-5.0.0/src/redis-cli -h 主机ip -p 端口号 --pipe
scan指令每次游标迭代出的结果可能会重复,所以需要代码进行筛选去重(利用set)
- redis实现分布式锁
分布式锁需要解决的问题:
互斥性:任意时刻只能有一个客户节点获得锁
安全性:只能由获得锁的节点释放锁
死锁:获得锁的节点由于某些原因突然宕机,其他节点永远都拿不到锁,从而导致死锁
容错:部分节点宕机,客户节点仍能获得锁和释放锁
不好的方案:SETNX key value:如果key不存在,则创建并赋值
如何解决SETNX长期有效的问题:EXPIRE key seconds
- 设置key的生存时间,当key过期时,会被自动删除
- 原子性得不到满足
Jedis jedisPool = new JedisPool(config,"localhost",6379,2000); long status = jedisPool.setnx(key,"1"); if(status == 1){ jedisPool.expire(key,expire); //执行独占资源逻辑 doSomething(); }
上述伪代码是有问题的,因为如果执行到setnx操作后该节点宕机,还没有设置expire时间,将会导致其他节点再也拿不到锁,导致死锁。(不具有原子性)
好的解决方案:SET key value [EX seconds] [px milliseconds] [NX|XX]
Jedis jedisPool = new JedisPool(config,"localhost",6379,2000); String result = jedisPool.set(lockKey,requestId,SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME,expireTime); if("OK".equals(result)){ //执行独占资源逻辑 doSomething(); }
大量Key同时过期,由于清除大量的Key很耗时,会出现短暂的卡顿现象,如何解决?
- 解决方案:在设置key的过期时间时,给每个key加上一个随机值