redis


前言

背景: c语言编写,可承受十万次的QPS ,redis的指令式原子性的


一、为何快

  • 完全基于内存,绝大不分请求是存粹内存操作的,执行效率高。
  • 数据结构简单,对数据操作比较简单。没有数据关联
  • 单线程,(一般使用多线程,将io线程和业务线程分开,用线程池避免线程的频繁创建和销毁)
  • redis使用的是io多路复用函数 文件处理器

二、数据模型

  • String:最基本最常用(key,val),最大存储512M,二进制安全(意思可以包含任何文件,比如jpg图片,序列化的对象 )。
	保存字符串的对象结构:
 		struct sdshdr {
            unsigned int len;//已占空间长度
            unsigned int free;//剩余可用空间长度
            char buf[];//数据空间
        };

常用命令:get、set、incr、decr、mget等。
使用场景:

1、计数器(incr)记录访问量 粉丝数等
2、序列化的对象,或二进制文件
3、等等。。。。

  • Hash:
    常用命令:hget,hset,hgetall 等。
    使用场景:

1、数据模型:在这里插入图片描述
2、根据数据模型来看,hash很适合存储对象类型,hash 的结构可以任意添加或删除‘字段名’,更加高效灵活

  • List:有序列表
    常用命令:lpush,rpush,lpop,rpop,lrange等。
    使用场景:

1、消息队列
2、查询两端附近的数据性能非常好,所以可以做最新消息获取

  • Set:无序集合,不允许重复
    常用命令:sadd,spop,smembers,sunion 等。
    使用场景:

1、社交类应用中,获取两个人或多个人的共同好友,两个人或多个人共同关注的微博这样类似的功能,用 MySQL 的话操作很复杂,可以把每个人的好友 id 存到集合中,获取共同好友的操作就可以简单到一个取交集的命令就搞定

  • Sortd Set(zset):有序不重复集合,通过分数从小到大排序
    常用命令:zadd,zrange,zrem,zcard等
    使用场景:

在集合类型的场景上加入排序就是有序集合的应用场景了。比如根据好友的“亲密度”排序显示好友列表。

三、从海量数据中查询某个固定前缀的key

回答这个问题前,一定要先问清楚数据量。数量小可以用keys
1)使用keys (尽量不要使用,一般安装redis会被禁用此指令)
指令:keys pattern .pattern - 匹配符
缺点:1、keys指令会一次性返回所有匹配到的key
2、键的数量过大会使服务卡顿,消耗比较大
2)使用scan,适合生产环境

	指令:
	scan cursor [MATCH pattern] [COUNT count]
       .cursor - 游标。
         .pattern - 匹配的模式。
         .count - 指定从数据集里返回多少元素,默认值为 10 。
     scan 0 match k.* count 10
    第一个遍历是 cursor 值为0,然后将返回结果的第一个整数作为下一个遍历的游标,如果最后返回的到cursor的值为0就代表结束。
    其中会出现重复的key 需要去重

四、Redis实现分布式锁

这个分布式锁有 3 个重要的考量点:

  • 互斥(只能有一个客户端获取锁)
  • 不能死锁
  • 容错(只要大部分 redis 节点创建了这把锁就可以)

redis实现方案:
通过redis+lua实现分布式锁:

  1. 加锁:就是在 redis 里使用 setnx 命令创建一个 key,这样就算加锁
   	SET resource_name my_random_value NX PX 30000
  • NX:表示只有 key 不存在的时候才会设置成功。(如果此时 redis 中存在这个 key,那么设置失败,返回 nil)
  • PX 30000:意思是 30s 后锁自动释放。别人创建的时候如果发现已经有了就不能加锁了。
  1. 解锁:释放锁就是删除 key ,但是一般可以用 lua 脚本删除,判断 value 一样才删除:
-- 删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除。
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

为啥要用 random_value 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 lua 脚本来释放锁。

但是这样是肯定不行的。因为如果是普通的 redis 单实例,那就是单点故障。或者是 redis 普通主从,那 redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。

通过redisson实现分布式锁:RedLock

Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)

RedLock 算法
这个场景是假设有一个 redis cluster,有 5 个 redis master 实例。然后执行如下步骤获取一把锁:

  1. 获取当前时间戳,单位是毫秒;
  2. 跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒;
  3. 尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点 n / 2 + 1
  4. 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
  5. 要是锁建立失败了,那么就依次之前建立过的锁删除;
  6. 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁

Redis 官方给出了以上两种基于 Redis 实现分布式锁的方法,详细说明可以查看:https://redis.io/topics/distlock 。

redis 分布式锁和 zk 分布式锁的对比

  • redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
  • zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。

另外一点就是,如果是 redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等…zk 的分布式锁语义清晰实现简单。
所以先不分析太多的东西,就说这两点,我个人实践认为 zk 的分布式锁比 redis 的分布式锁牢靠、而且模型简单易用。
redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。

五、持久化机制RDB和AOF

1、RDB 适合备份(时间固定、恢复快)。固定时间写入文件,并且写入文件是会fork一个子线程,然后子线程进行保存工作父进行无需进行io操作
2、AOF 可配置策略(默认每次都写入),随时写入数据可以保证不丢失。但是体积大恢复慢

六、io多路复用

七、禁用危险命令

在redis.conf中,在SECURITY这一项中,新增以下配置禁用指定命令:

 rename-command FLUSHALL ""
 rename-command FLUSHDB  ""
 rename-command CONFIG   ""
 rename-command KEYS ""

八、这文章不错,易懂

瞅瞅 瞧瞧 看看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值