Redis01-Redis要点汇总

DragonflyDavid

Redis要点汇总

单机Redis

为什么会有Redis的出现

  1. 传统关系型数据库磁盘存储,必须给出schema类型(字节宽度),所以数据库是行级存储。数据表很大性能会下降。如果表有索引,增删改表慢,查询速度并发过多也会慢
  2. SAP的HANA是内存级别的关系型数据裤,虽然快但是非常贵,而且数据在磁盘和内存中体积是不一样的(因为磁盘上没有指针,所以同样的数据会多地复制存储,这样就会产生数据涨出,而内存是有指针的,所以同样的数据只存一份,然后用不同指针指向该片数据,所以内存存放数据空间是小于磁存放的)
  3. 磁盘->寻址:ms,带宽:G/M

    内存->寻址:ns,带宽:很大

    磁盘比内存在寻址上慢了10W倍,I/O buffer:成本问题,磁盘与磁道,一个扇区512Byte

    索引4K,操作系统,无论你读取多少都是最少从磁盘拿4K

  4. Redis和memcache是一种折中的选择,速度快,而且价格便宜

架构师数据存储选型对比网站

技术选型和技术对比: https://db-engines.com/en 

https://redis.io  
http://www.redis.cn/  ---中文官网

Redis安装

https://redis.io
http://www.redis.cn/  ---中文官网
  1. BIO阻塞I/O:应用程序进程如果发起1K个请求,则开启1K个socket文件描述符,socket在等待内核返回数据时是阻塞式的,数据未准备好就一直阻塞等待,直到返回数据后才等待下一个socket的返回
  2. NIO轮询非阻塞I/O:应用进程如果发起1K个请求,则在用户空间不停轮询这1K个socket文件描述符,查看是否有结果返回。这种方法虽然不阻塞,但是效率太低,有大量无效的循环
  3. I/O多路复用(select,poll,epoll)

    select:能打开的文件描述符个数有限(最多1024个),如果有1K个请求,用户进程每次都要把1K个文件描述符发送给内核,内核在内部轮询后将可读描述符返回,用户进程再依次读取。因为文件描述符(fd)相关数据需要在用户态和内核态之间拷来拷去,所以性能还是比较低

    poll:可打开的文件描述符数量提高,但性能仍然不够

    epoll(Linux下多为该技术):用户态和内核态之间不用文件描述符(fd)的拷贝,而是通过mmap技术开辟共享空间,所有fd用红黑树存储,有返回结果的fd放在链表中,用户进程通过链表读取返回结果,伪异步I/O,性能较高

  4. AIO:异步I/O,性能最高,但是使用非常复杂,不是很常用(windows系统中多见)

RPC I/O模型,BIO,NIO,多路复用I/O,AIO

  • BIO阻塞I/O:应用程序进程如果发起1K个请求,则开启1K个socket文件描述符,socket在等待内核返回数据时是阻塞式的,数据未准备好就一直阻塞等待,直到返回数据后才等待下一个socket的返回
  • NIO轮询非阻塞I/O:应用进程如果发起1K个请求,则在用户空间不停轮询这1K个socket文件描述符,查看是否有结果返回。这种方法虽然不阻塞,但是效率太低,有大量无效的循环
  • I/O多路复用(select,poll,epoll)

    select:能打开的文件描述符个数有限(最多1024个),如果有1K个请求,用户进程每次都要把1K个文件描述符发送给内核,内核在内部轮询后将可读描述符返回,用户进程再依次读取。因为文件描述符(fd)相关数据需要在用户态和内核态之间拷来拷去,所以性能还是比较低

    poll:可打开的文件描述符数量提高,但性能仍然不够

    epoll(Linux下多为该技术):用户态和内核态之间不用文件描述符(fd)的拷贝,而是通过mmap技术开辟共享空间,所有fd用红黑树存储,有返回结果的fd放在链表中,用户进程通过链表读取返回结果,伪异步I/O,性能较高

  • AIO:异步I/O,性能最高,但是使用非常复杂,不是很常用(windows系统中多见)

缓存数据类型

memcache

key,value型存储:value是没有类型的概念,不同于Redis。对于复杂的结构可以存json,在应用程序端自己计算  
该方法  
这不符合计算向数据移动的思想

Redis

1. Redis是单进程、单线程、单实例、采用epoll的多路复用I/O模式,因此可以按顺序执行一个client端发起的所有命令  
2. Redis存储的是二进制安全的字节流,而不是字符流  
3. Redis的value类型String、List、Hash、Set、Sorted\_set  
 1) String:可以存储字符串、数值、bitmap(1.可用于用户登录天数统计,时间窗口随机 2.活跃用户统计,时间窗口  
 随机)  
 2) List:同向命令使用时栈,反向命令使用是队列,List正向索引(0,1,2...),反向索引(-1,-2,-3...)  
 3) Hash: key field value  
 4) Set:无序性和随机性,不去重,可以做随机抽取的动作  
 5) Sorted\_set:通过权重数值进行排序,默认相同权重,按字典序排序,排序采用算法跳跃表(skip list)

Redis进阶使用

  1. 订阅和发布
  2. Redis的事务:谁先exec,先执行哪个事物,Redis不支持事物的回滚
  3. 布隆过滤器(升级版:Counting 布隆过滤器、布谷鸟过滤器):通过多重Hash算法大概率过滤不存在的Key,解决缓存穿透问题,但是仍然有一些不存在Key的查询会到达数据库,可以在Redis中设置相关Key,value设置为null,但是这样的设置需要设定一个过期时间(https://github.com/RedisBloom/RedisBloom

缓存中的常见问题

  1. 击穿
  2. 雪崩
  3. 穿透
  4. 一致性(双写)

Redis单机持久化

  1. RDB(存储和恢复较快,但是不实时,会丢数据):快照/副本
  2. AOF(数据全,但是文件较大,恢复较慢):日志,4.0以后的Redis支持AOF重写,将老的数据RDB到AOF中,增量部分为AOF

Redis集群

为什么要有Redis集群

Redis单机单进程,可以做缓存也可以用作数据库,具有两种持久化方式RDB和AOF

单机、单节点、单实例问题:

1. 单点故障  
2. 容量有限  
3. 访问压力过大  
利用AKF微服务拆分原则,进行一变多的拆分  
AKF----X,Y,Z三个方向进行拆分  
X轴:Redis的全量镜像备份,解决单点故障和访问压力问题  
Y轴:根据业务、功能进行Redis库的拆分,比如商品一个库,价格一个库,解决容量有限和访问压力问题  
Z轴:根据优先级、逻辑再进行拆分,比如商品1~10000一个库,20001~30000一个库

X轴数据镜像备份数据一致性问题:

数据同步的三种架构:

  1. 更新主库,并同步阻塞更新备库,强一致性会破坏可用性
  2. 更新主库,并异步更新备库,弱一致性,可能带来数据的丢失(Redis选取方式)
  3. 更新主库,并同步阻塞更新kafka(可靠的消息队列集群,响应速度较快),最终数据一致性,可能短时间存在数据不一致情况

Redis系统最强调的是性能,它就是为快而生,所以没有选取最后kafka的方案,而是选取的第二种方案

Redis主从复制模式

Redis的主备方式,主读写,自己又是一个单点,仍然存在单点故障问题,如果主挂了,就不能再进行写数据操作,可能读数据操作还可以进行(从备库读取)

因此需要对主做HA高可用,自动故障转移,选择“代替人”,将从升级为主

需要监控程序监控主的健康情况,为了保证不误杀,需要多个监控程序同时监控,并对主的健康情况进行投票表决,因此监控程序一般为奇数个,超过半数监控认为主挂了,才会选出新的主

主从复制配置项:

replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no   ----主从复制是否经过磁盘
repl-backlog-size 1mb   ----增量复制配置
min-replicas-to-write 3
min-replicas-max-lag 10

Redis高可用Sentinel哨兵模式

哨兵启动方法

redis-sentinel  
redis-server --sentinel  
#配置文件,Redis源码目录  
port 26379  
sentinel monitor mymaster 127.0.0.1 6381 2  
                                         |  
                                  需要至少几票表决才能决定换主

Redis容量问题解决

AKF拆分原则:X、Y、Z

主从复制可以解决主的HA,X问题,但是单节点容量问题还没有解决,因此需要对数据进行拆分,数据的拆封有四种方案:

  1. 数据可以分类,可以根据业务进行拆分
  2. 数据不可以分类,通过modula算法(hash+取模)进行sharding,弊端:取模的数必须固定、影响分布式下的扩展性
  3. 数据不可以分类,通过randam算法(随机lpush到Redis一个节点上,再另一头rpop取)进行sharding,类似消息队列场景
  4. 数据不可以分类,通过kemata算法(一致性哈希算法)进行sharding,hash算法包括(CRC16、CRC32、FNV、MD5映射算法)

    一致性Hash算法:规划一个虚拟的环,上面有0~2^32个虚拟的点,通过计算(IP的HASH值)让每个物理节点位于环上面的某一个点,数据来了计算它位于环上的某一个点,这个点一般没有物理节点,就顺时针找寻离他最近的物理节点,这样如果新加一个节点,那么不需要数据迁移,只需要等该节点自己慢慢预热,他后面的节点设置LRU、LFU淘汰算法,就可以实现数据转移

    优点:添加节点、的确可以分担其他节点的压力,不会造成全局洗牌

    缺点:新增节点造成一小部分数据不能命中,带来击穿问题,压到mysql,可以通过设法取离数据最近的两个Redis方法解决

以上方案都是将Redis当缓存来使用,而不是当数据库使用

Redis集群代理方案

如果没有Redis集群代理,则连接成本过高,每个客户端都要连接每个Server

在代理层实现sharding算法,modula、random、kemata,代理proxy前面还可以使用LVS+VIP,让客户端的访问是透明的,keepalived保证LVS的高可用

数据分片的预分配方案:先将数据预分片为较多快,每个实际节点存几块(Redis cluster方案)

proxy代理方案

1. twitter的Redis集群方案,在Github上面搜索twemproxy  ---- 不支持事务(https://github.com/twitter/twemproxy  http://www.redis.cn/topics/partitioning.html)
2. predixy Redis集群代理方案,支持sentinel和cluster  ---- 不支持事务(https://github.com/joyieldInc/predixy/blob/master/README_CN.md)
3. redis cluster   ----  共16384个槽位,每个节点分一些槽位,可以很方便的进行槽位迁移,均衡,客户端请求任意sharding,如果数据不在该sharding,则自动跳转sharding。该方案支持事物,但需要业务自己设计Key(基因法)保证数据在一个sharding上(http://www.redis.cn/topics/cluster-tutorial.html)

Redis 面试常见问题和API

Redis常见问题

击穿

前置条件:单个KEY在缓存中过期,但是忽然有大量并发查询,缓存还没有预热
后果:KEY的过期造成大量并发压到数据库
解决方案:1.get key 2.setnx(分布式锁) 3-1.OK,去DB取数据 3-2.false sleep ->1
该方案的问题:1.如果第一个人挂了,不释放锁怎么办?->可以设置锁的过期时间
            2.如果第一个人没挂,但是锁过期怎么办?->多线程,一个线程取DB,另外一个线程监控是否DB返回,没有    
              返回延长锁时间
#以上解决方案非常繁琐,通过redis实现分布式锁做协调很麻烦,原因Redis是AP模型,分布式锁是CP模型,用AP实现CP,不合理

穿透

前置条件:从业务接收的查询是系统中根本不存在的数据  
后果:查询总是透过缓存,抵达数据库  
解决方案:使用布隆过滤器(布隆过滤器有缺陷,只能增加不能删除),可以添加key对应空值解决或者用布谷鸟过滤器(可  
 以删除)

雪崩->击穿

前置条件:大量KEY同时失效  
后果:造成大量访问达到DB  
解决方案:如果KEY之间是时点性无关的,那么设置随机过期时间,如果是有关的,那么和击穿方案一样,如果能预知缓存结  
 果可以提前预热缓存

RedisAPI

SpringBoot

https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/reference/html/spring-boot-features.html#boot-features

https://docs.spring.io/spring-data/redis/docs/2.2.4.RELEASE/reference/html/#

Jedis

https://github.com/xetorthio/jedis

Lettuce

https://github.com/lettuce-io/lettuce-core

阅读 528

苏格拉没有底
尽心,知命

尽心,知命

182 声望
9 粉丝
0 条评论

尽心,知命

182 声望
9 粉丝
宣传栏