什么是Redis
数据从单表,演进出了分库分表,MySQL从单机演进出了集群,读写压力不断增加,在高QPS下MYSQL无法支持,把数据分冷热,经常访问到的热数据存储到内存中,查询数据时如果数据内存中有缓存直接内存读取,否则去磁盘MySQL中去读取
把数据存储到内存中,如果服务器重启内存中数据会丢失,Redis能做到数据的持久化,重启服务器数据不丢失,用程序读取数据时用RESP的协议,才读写数据时会产生日志,讲数据保存到AOF文件,重启服务器时会读取AOF文件,RDB文件保存了Redis中所有的key信息,先去读取RDB文件,再去对比是否存在未执行的命令,如果有继续执行未执行的命令
Redis是单进程处理的系统,排队串行处理,顺序执行,数据同步分布式锁的操作会用到Redis单线程的特性
Redis应用案例
连续签到:用户每日签到,如果断签计数归零,连续签到的定义:每天必须在23:59:59前签到,Redis有自动增加功能Incr,以及自动过期的能力
String在Redis中是二进制安全的结构buf,存储时足够节省空间,能够实现快速读写,扩容时非常重的操作,sds指针向左移动获取元信息,指针再向右移动获取value
消息通知:当文章更新时,将更新的文章推送到ES,用户就能搜索到最新的文章数据,用list作为消息队列(面试会问redis中list是怎么实现的)List数据结构Quicklist,由一个双向链表和listpack实现,每个节点指向前方与后方节点,redis为了节省内存会在一个节点存多个数据,将多个数据压缩成listpack存储在节点中,开辟大的内存空间存元素1元素2,存储会记录有几个元素,整个内存的长度以及第几个元素到哪截至
hash哈希,一个用户有多项计数需求,可通过hash数据结构,写数据哈希key的时候可用pipeline减少网络上的重复传输,先初始化一个pipeline,收到3个写消息的时候,遍历消息,将这些消息塞到pipeline中,然后一次性把打包的数据发送到redis中,减少网络传输。通过hmget,golang中就是HGetAll,在hash中也可以使用increase,redis可以方便的实现一次性取多个数据或是单个数据的变更
Hash数据结构dict,槽位、单向链表的拉链,用hash算法算出数值,数值代表槽位,去槽位中去寻找key对应的value,hash的过程的时间复杂度是o1的,hash冲突的时候会产生单向链表,需要在链表中遍历,当单向链表的拉链很长时会增加槽位,进行数据迁移,rehash:因为hashtable要扩容,扩容中需要保证业务能够正常响应,所以进行渐进式的rehash拷贝,每次用户访问时迁移少量数据,将整个迁移过程平摊到所有的访问用户请求过程中,完全迁移后进行指针的交换,对用户无感,扩容完成。核心点是进行扩容过程中不能阻塞用户请求。
排行榜:积分变化时,排名进行实时变更,需要排序的用户是千万级别的,当积分变化时需要操作积分变更并对积分变更后进行重排序,当qps千万级别的时候服务器容易挂掉,redis有个数据结构叫做zset。zskiplist跳表,将链表生成多层链,redis是跳表+hash的数据结构,通过跳表找到元素,通过哈希kv找到元素的分值,redis中一般跳表不会超过四层,跳表构成双向链表,可以实现倒排,通过指针找前序的节点,快速拉出积分榜
限流:要求1秒内放行的请求未N,超过N则禁止访问,防止用户用脚本去做一些不合理的事情。利用redis的key作为限流的计数,每一次访问+1,如果达到限流条件进行限流,在下一秒时生成一个新的key
分布式锁:要求一次只能有一个协程进行,不能出现并发超限的问题,通过setnx,只有setnx未设置过才能执行成功。setnx之所以能成立,因为redis是单线程执行,setnx不能保证高可用的分布式锁,超时问题,redis进行主从切换时a持有的锁未同步新的主节点时会出现并发访问,出现脑裂时出现多个主节点