zoukankan      html  css  js  c++  java
  • redis和缓存

    简介

    Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。 键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。  port:6379

     Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘

    redis优点

    (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

    (2) 支持丰富数据类型,支持string,list,set,sorted set,hash

    (3) 支持事务,操作具有原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行。

    服务器在执行事务时,不会转而执行别的客户端的命令。

    客户端的事务命令不是一条一条发的,而是一次性发送,这样可以减少客户端与服务端的网络通信次数而提高性能。

    (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

    值的数据类型

    String 字符串、整数、浮点数 

    set x 1 [ex 1/px 1000]  返回ok [1秒后x自动删除]

    del x

     get x       

    incr x  (自增操作,每次增1)   decr x

    List

    rpush/lpush list_key a b c....  返回列表的长度

    lpop/rpop list_key

    lrange list_key 0  2    区间左闭右闭     lindex list_key 1

    Set    交集、并集 ,实现共同好友等功能

    sadd set_key a b c ...

    srem set_key a

    smembers set_key    sismember set_key c

    Hash

    hset hash_key sub_key1 value1 sub_key2 value2 ...

    hdel hash_key sub_key2

    hget hash_key sub_key1  返回value值 或 nil      hgetall hash_key

    Zset 内部根据score排序     有利于实现排行榜等功能

    zadd zset_key [incr]  score1 member1  score2 member2

    zadd zset_key [incr]  score3 member4  每执行这个命令score3+=score3的初始值

    zrem zset_key a

    zrange  zset 0 -1 [withscores] 

    zrangebyscore zset 0 800 withscores  查找score在0~800之间的记录

    数据结构

    字典:使用两个哈希表,一个是主要的数据存储,另一个只在需要rehash的时候才会用到。

    使用拉链法解决哈希冲突。

    rehash不是一次性完成,而是采用渐进形式,为了避免一次性执行过多的rehash操作给服务器带来过大负担。

    在 rehash 期间,每次对字典执行增删查改操作时,都会执行一次渐进式 rehash。

    采用渐进式 rehash 会导致字典中的数据分散在两个哈希表上,因此对字典的查找操作也需要到对应的哈希表去执行。

    跳跃表:是有序集合的底层实现之一。

    跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。 在查找时,从上层指针开始查找,找到对应的区间之后再到下一层去查找。下图演示了查找 22 的过程。

    与红黑树等平衡树相比,跳跃表具有以下优点: 插入速度非常快速,因为不需要进行旋转等操作来维护平衡性; 更容易实现; 支持无锁操作。

    redis持久化  哪几种方式 优缺点

    RDB:在指定的时间间隔能对你的数据进行快照存储。

    save 300 10  表示300s内有10条写入,就产生快照  save会阻塞redis服务器,直到持久化完成,一般不会使用

    bgsave  fork一个子进程,由子进程负责持久化,阻塞只发生在fork子进程的时候

    AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

    always:把每个写命令都立即同步到aof,很慢,但是很安全

    everysec:每秒同步一次,是折中方案。比较常用

    no:redis不处理交给OS来处理,非常快,但是也最不安全

    RDB、AOF都有手动触发和自动触发方式,且都是先写入到缓冲区的临时文件中,然后通过rename完成文件的替换工作。。

    RDB v.s. AOF

    1、aof文件比rdb更新频率高,优先使用aof还原数据。

        AOF中保存的数据更完整,使用everysec方式的话最多损失1s的数据。

    2、aof比rdb更安全也更大

    3、rdb性能比aof好

    4、如果两个都配了优先加载AOF。

    redis使用场景

    计数器

    string的自增自减运算,适用于实现计数器功能。

    redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

    e.g.密码输入错误次数最多10次。

    缓存

    将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。

      包括浏览器缓存(当 HTTP 响应允许进行缓存时,浏览器会将 HTML、CSS、JavaScript、图片等静态资源进行缓存)、

      会话缓存(可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性)、

      数据库缓存(数据库需要进行持久化,一般是先写入内存,在持久化到硬盘)、

      CPU 多级缓存(CPU 为了解决运算速度与主存 IO 速度不匹配的问题,引入了多级缓存结构,同时使用 MESI 等缓存一致性协议来解决多核 CPU 缓存数据一致性的问题).

    消息队列

    List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息

    分布式锁

    ???

    数据淘汰策略

    先分配一定的页面空间,使用页面的时候首先去查询空间是否有该页面的缓存,如果有的话直接拿出来,如果没有的话先查询,如果页面空间没有满的时候,就把查询到的页面放到缓存中,若页面空间已满,使用新页面的时候,就释放旧的页面空间,把新页面缓存起来,以便下次使用同样的页面的时候方便调用。淘汰策略,就是为了决定释放哪个旧的页面。

    FIFO先进先出:队列

    LRU最近最少使用的页面先出:链表,若访问命中,就将页面放到链表头部,插入新数据时也在链表头部,链表满,则丢弃链表尾部数据。

    LFU最近使用频率最小的页面先出。维护一个有序链表,按引用计数和时间顺序排序。

    插入新数据时,设置新数据引用计数为1,访问命中,则引用计数加1。若链表满,则淘汰链表头部的数据。

    缓存穿透、击穿和雪崩

    穿透:访问数据库中不存在的数据,这样每次的查询都会到数据库中,缓存形同虚设。

    1)存储空值,即使查询为空,也要把键值放到缓存中并设置过期时间,带下一次查询,直接返回空值。

    2)对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。

    击穿:在高并发系统中,大量的请求查询一个key,而此时这个key刚好失效了,会导致大量的请求直接到数据库中。

    同雪崩2)

    雪崩:在某个时间段,缓存中的键值集体失效,导致大量的查询直接到数据库中,增加服务器的压力甚至会导致服务器崩溃、宕机。

    这是由于,高并发查询放到缓存时,设置了同样长度过期时间,所以这些键值才会同时过期。

    1)设置不同的过期时间,例如为不同类型的key设置不同的过期时间,同一类型key设置随机的过期时间。

    2)在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

    3)做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

    缓存一致性

    要求数据更新的同时缓存数据也能够实时更新。

    解决方案:

    • 在数据更新的同时立即去更新缓存;
    • 在读缓存之前先判断缓存是否是最新的,如果不是最新的先进行更新。

    要保证缓存一致性需要付出很大的代价,缓存数据最好是那些对一致性要求不高的数据,允许缓存数据存在一些脏数据。

    缓存 “无底洞” 现象(投入越多,产出不一定越多)

    指的是为了满足业务要求添加了大量缓存节点,但是性能不但没有好转反而下降了的现象。 产生原因:缓存系统通常采用 hash 函数将 key 映射到对应的缓存节点,随着缓存节点数目的增加,键值分布到更多的节点上,导致客户端一次批量操作会涉及多次网络操作,这意味着批量操作的耗时会随着节点数目的增加而不断增大。此外,网络连接数变多,对节点的性能也有一定影响。

    解决方案: 优化批量数据操作命令; 减少网络通信次数; 降低接入成本,使用长连接 / 连接池,NIO 等。

    redis集群

    为了保证Redis的高可用,提高Redis的读写性能,最简单的方式我们会做主从复制,组成Master-Master或者Master-Slave的形式,或者搭建Redis集群,进行数据的读写分离,类似于数据库的主从复制和读写分离。

    数据结构

    传统的哈希分布:就是将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。 当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。

    顺序分布 将数据划分为多个连续的部分,按数据的 ID 或者时间分布到不同节点上。例如 User 表的 ID 范围为 1 ~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 ~ 1000,1001 ~ 2000,...,6001 ~ 7000。

    顺序分布相比于哈希分布的主要优点如下: 能保持数据原有的顺序; 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。 一致性哈希 Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据迁移的问题。 基本原理 将哈希空间 [0, 2n-1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。

    虚拟节点 当服务器节点较少,容易出现数据数据分布不均匀的问题(数据倾斜),节点存储的数据量有可能会存在很大的不同。 解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得多,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。

  • 相关阅读:
    【leetcode】Binary Search Tree Iterator
    【leetcode】Palindrome Partitioning II
    【leetcode】Best Time to Buy and Sell Stock III
    【leetcode】Best Time to Buy and Sell Stock II
    【leetcode】Longest Consecutive Sequence
    【leetcode】Factorial Trailing Zeroes
    【leetcode】Simplify Path
    【leetcode】Generate Parentheses
    【leetcode】Combination Sum II
    【leetcode】Combination Sum
  • 原文地址:https://www.cnblogs.com/yvlian/p/13418633.html
Copyright © 2011-2022 走看看