Redis总结笔记(总结自Redis开发与运维)

1、可以通过object encoding命令查询内部编码

内部编码:

string:raw int embstr(小于39)

Hash:hashtable ziplist

List:linked list  ziplist

Set :hashtable intset

Zset skiplist ziplist

这样设计的有点:改进内部编码,对外的数据结构和命令没有影响;多种内部编码实现可以在不同的场景下发挥各自的优势。

Redis的单线程模型:使用了单线程架构和IO多路复用模型来实现高性能的内存数据库服务。

单线程性能高的原因:

  1. 纯内存访问,数据放在内存中,访问快;
  2. 非阻塞IO,使用epoll作为IO多路复用技术的实现,加上自身的事件处理模型将epoll中的链接 读写 关闭都转换为事件,不在网络上浪费过多的时间。
  3. 单线程避免了现成切换和竟态产生的消耗。

单线程模型不会同时有两个命令同时执行,没有并发问题。但是若是一个命令执行时间过长,会造成其他命令的阻塞。因为redis是面向快速执行场景的数据库。

String:

命令 set setnx setex:

SET key value含义:

          将字符串值 value 关联到 key . 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。

SETEX key seconds value

           将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX 命令将覆写旧值。

返回值:

           设置成功时返回 OK 。当 seconds 参数不合法时,返回一个错误。

SETNX key value 含义:  setxx,与nx相反,键必须存在才能设置成功,用户更新。

           将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。

           SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。 设置成功,返回 1 .  设置失败,返回 0 。

GETSET key value  含义:

          将给定 key 的值设为 value ,并返回 key 的旧值(old value)。

          当 key 存在但不是字符串类型时,返回一个错误。

           返回给定 key 的旧值.  当 key 没有旧值时,也即是, key 不存在时,返回 nil 。

因为 SET 命令可以通过参数来实现和 SETNX 、 SETEX 和 PSETEX 三个命令的效果,所以将来的 Redis 版本可能会废弃并最终移除 SETNX 、 SETEX 和 PSETEX 这三个命令。

SET key-with-expire-time "hello" EX 10086  ,为key设置过期时间。

SET key-with-expire-and-NX "hello" EX 10086 NX 若是key不存在 则设置,并设置过期时间。

 

Setnx的应用场景,若是多个客户端同时执行setnx key value,只能有一个客户端设置成功,setnx可以作为分布式锁的一种实现方案。

批量获取命令:mset:提高开发效率,减少网络消耗

计数:
incr key.自增;decr decr 自减,incrby 自增指定数字 decrby 自减指定数字 incrbyfloat 自增浮点数

String使用场景:缓存 ,计数 ,共享session ,限速(限制发短信次数,)

Hash

Hget hset hgetall  存储结构信息。

List:列表原始有序(可通过下标获取),可重复

查询:lindex key index:获取列表指定索引下标的元素。

Lrem key count value :删除指定元素,从列表中找到等于value的元素进行删除,

Ltrim key start end :按照索引范围修建列表 ,保留list的start end之间的元素。

阻塞操作命令:blpop brpop, blpop key timeout;timeout 阻塞时间。

列表为空:timeout=3 ,客户端等3s后返回,timeout=0则一直阻塞。列表不为空 则会立即返回。

list使用场景:
消息队列:lpush +brpop 可实现阻塞队列,

文章列表:分页展示每个人的文章列表。若是列表较大,获取中间数据比较慢,可以考虑将列表做二级拆分。

Lpush +lpop =stack(栈);

lpush +rpop =queue;l

push +ltrim = capped collection (有限集合),

lpush +brpop = message queue.

Set

不允许重复元素,元素无序;

Sadd,添加元素, sad key element ;srem 删除元素 ;scard key 计算元素个数。Sismember key element ,判断元素是否在集合中。Srandmember key count,随机的返回count元素。Smembers key:获取所有元素。Sinter key1 key2 ,求交集

Suinon:求并集;sdiff 求差集

使用场景:标签(tag);sdd = tagging(标签);spop/srandmember=random item(生成随机数,抽奖);sadd+sinter=social graph(社交需求)

有序集合 zset :为每个元素设置一个分数作为排序的依据。Zadd key score member,也存在nx xx ch incr选项,ch表示此次操作后发生变化的个数。

Zrank key member 计算成员的排名 从低到高,zrevrank key member  从高到底排名。Zincrby key increment member .增加成员的分数。

使用场景:排行榜系统,获取赞数。

键管理:单个建管理:rename :重命名,重命名的key若是存在,则值被覆盖。为了避免强行rename,使用renamenx,只有newkey不存在的时候才会覆盖。重命名会删除旧键,若键对应的值比较大,会存在阻塞redis的可能。

随机返回一个键 randomkey

键过期:expire ttl(显示剩余过期时间,-1表示键没有过期时间,-2表示键不存在),persist key:清除键的过期时间。对于字符串类型,执行set会去掉过期时间。Redis不支持二级数据结构(hash 列表)内部元素的过期功能。不能对列表类型的一个元素设置过期时间。Setex= set+expire ,不但是原子执行 还减少了一次网络通讯时间。

Keys获取键可能会产生阻塞,类似还有hgetall smembers zrange,优化可以使用hscan ssan zscan.但是scan的过程中若是有键的变化,新增的可能遍历不到等。

Pipeline:流水线机制。很多命令不支持批量执行的;例如hgetall,若要执行n次hgetall命令,需要消耗多次网络连接,pipeline可以实现将一组Redis命令进行组装,通过一次网络传输给redis,在将这组redis命令的执行结果按顺序返回给客户端。

原生批量命令与pipeline的对比:

原生批量命令是原子的,pipeline不是,原生批量命令是一个命令对应多个key,pipeline支持多个命令,原生批量命令是Redis服务端支持实现的,pipeline需要服务端和客户端的共同实现。

 

为了保证这条命令组合的原子性,Redis提供了简单的事务功能及集成lua脚本来解决这个问题。将一组命令放在multi exec命令之间。之间的命令是原子顺序执行的,exec时才会真正的执行命令,若其中一个命令执行了 另外一个命令运行出错,redis不会回滚。同时也无法实现命令之间的逻辑计算关系。

 

Redis执行lua有两种方式:使用eval命名,客户端将脚本作为字符串发到服务器,服务器将结果返回给客户端。

使用evalsha命令,将lua脚本加载到Redis服务端,得到脚本的sha校验和,evalsha命令使用sha作为参数直接执行对应的lua脚本,避免每次发送lua脚本的开销。脚本常驻服务端,脚本功能得到复用。

Lua脚本在Redis是原子执行的,执行过程中不会插入其他命令。

Lua的缺点:lua执行时间太长,阻碍客户端命令时,需要script kill;将脚本杀掉。不过lua脚本正在执行写操作的时候,scriptkill将不会生效。

Bitmaps:

Redis(减少内存使用量)提供bitmaps这个数据接口实现对位的操作。Bitmaps本身不是一种数据结构,实际上是字符串,但是他可以对字符串的位进行操作。Bitmaps可以认为是一个以为为单位的数组,数组的每个单元只能存储0和1,数组的下表在bitmaps叫做偏移量。

案例:记录每个用户是否访问过某网站,将访问的用户记做1,没有访问过记做0,用偏移量作为用户的id。Setbit key offset value 初始化的时候为0,很多应用的id以(10000)等开头,若直接和bitmap对应,造成浪费,一般将id减去指定数字,不然偏移量太大,初始化的过程比较慢,造成redis阻塞。

获取数据getbit key offset

获取bitmap指定范围值为1的个数:bitcount start end

Bitmaps间的运算:bitop 是一个复合操作,可以做多个bitmap的and or not xor等操作。

Bitmap的使用注意:若是一亿用户,每天只有10W访问量,大量的0用户,这时就不合适使用bitmaps.

Hyperloglog

Hyperloglog实际上是字符串类型,是一种基数算法(给定一个含有重复元素的有限集合,计算其不重复的元素的个数),通过hyperloglog利用极小的内存空间完成独立总数的统计。数据集可以是IP ID EMAIL等。有三个命令:pfadd pfcount pfmerge

选用pyperloglog的考虑:只为了计算独立总数,不需要获取单条数据;

可以容忍一定的误差率,毕竟内存占用上优势很大

用来统计:某家店铺今天有多少不同用户访问;某家店铺今天接待了多少不同买家这类信息,思想:hash函数,将同一个用户hash到同一位中。

发布订阅:

Redis提供了发布订阅的消息机制,发布订阅中不直接通信,发布者客户端向指定的频道发布消息,订阅该频道的每个客户端都可以收到该消息。

Publish channel message 发布消息

订阅消息:subscribe channel订阅消息 可以一次订阅多个频道。

客户端在执行订阅命令之后进入了订阅状态,只能接收subscribe unsubscribe(取消订阅 channel)psubscribe punsubscribe(这两个是支持正则形式的channel)四个命令。

新开启的订阅客户端,无法接收到该频道之前的消息,因为redis不会对发布的消息进行持久化。

Redis的发布订阅消息系统粗糙,无法实现消息堆积和回溯。

使用场景:聊天室 公告牌 服务之间利用消息解耦的都可以。

GEO (地理信息定位)支持存储地理位置信息用来实现附近位置,摇一摇等依赖地理位置信息的功能。

GEO实现是zset

4章客户端

客户端服务器通信使用的协议是resp(redis序列化协议).服务端返回结果格式:状态回复:+;错误回复-;整数回复:;字符串回复$,多条字符串回复 *。

Java的redis客户端:jedis属于java的第三方开发包,下载jedis.jar加入到项目中。

Jedis实现了pipeline,新建对象,进行操作即可。

输入缓冲区:redis为每个客户端分配了输入缓冲区,作用就是将客户端发送的命令临时保存,同时redis从缓冲区里拉取命令并执行,每个客户端缓冲区不能超过1G,超过后将会关闭。

输入缓冲区不受maxmemory控制,若redis存储了2G数据,输入缓冲区使用了3G,超过maxmemory的限制,可能会产生数据丢失,键值淘汰 ooM等情况。

两种情况导致输入缓冲区过大:redis的处理速度跟不上输入速度,并且每次进入缓冲区的命令包含了大量的bigkey;

另外一种情况是redis发生了阻塞,短期内不能处理命令,造成客户端积压。

解决方案:监控 client list

输出缓冲区:

保存命令执行的结果返回给客户端,为redis和客户端交互返回结果提供缓冲。输出缓冲区也不首maxmemory的限制。

及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大导致的。

Monitor:该命令用户监控redis正在执行的命令,monitor能监听到所有的命令,缺点:一旦Redis的并发量太大,monitor客户端的缓冲会暴涨,可能会瞬间站会大量内存。

5章,持久化

Redis支持RDB AOF两种持久化机制,可以避免因进程退出造成的数据丢失问题,当下次重启时利用之前的持久化的文件即可实现数据恢复。

RDB把当前进程数据生成快照保存到硬盘,手动触发(save:保存数据期间会阻塞,  bgsave:fock子进程,子进程完成持久化,fock期间会阻塞,一般时间短)和自动触发(save m n :m秒有n个更新时;从节点执行全量复制操作时,主节点自动执行bgsave生成rdb文件发给从节点;执行shutdown时,没有开启aof就会自动执行bgsave;执行rebug reload时会加载redis.)

Redis默认采用LZF算法对Rdb文件压缩。

RDB优点:压缩的二进制文件,文件小,含有某个时间点的数据快照,数据恢复快,适合备份,灾难恢复

缺点:没办法实时持久化(bgsave每次运行会fock进程,成本高,不能频繁操作)。

Aof:独立日志的方式记录每次写命令,重启时重新执行AOF文件的命令恢复数据,主要是解决数据实时持久化的问题,主流持久化方式。Aof的工作流程:所有命令追加写入AOF缓存;缓存根据策略向硬盘同步操作(AOF文件同步); 文件重写:AOF越来越大需定期重写,达到压缩的目的。 重启加载:服务器重启时,加载sof文件恢复数据。

为什么命令先写缓存?:单线程模式,每次都写硬盘,性能完全取决于当前硬盘的负载,阻塞客户端。

缓存同步文件的策略:always no everysec(默认,命令写入缓存后,调用系统write操作,不对AOF文件做fsync同步,同步操作由专门的线程每秒调用一次,持久化到1秒前操作):

重写:将Redis进程内的数据转化为写命令同步到Aof文件的过程。子进程重写期间使用copy-on write(写时复制,在并发访问的时候, 需要修改的元素,不直接修改容器,若是先复制一份副本,在副本上修改,修改完成后将指向原来容器的引用指向副本容器。读原来的文件,写副本,有数据不一致问题,可以最终一致。)机制与父进程共享内存,避免内存消耗翻倍。AOF重写期间还需要维护重写缓冲区,保存新的写入命令避免数据丢失。

重启加载:AOF开启且存在AOF文件时,优先加载AOF文件,没有采取找RDB文件。

持久化阻塞主线程的场景有:fock阻塞和AOF追加阻塞(超过2s数据还没用同步完,则主线程阻塞,直到同步完成);

6章复制

保证分布式Redis高可用:

  1. 复制的使用方式:只能有主到从复制(从节点的写,无法同步到主)命令:从节点配置slaveof masterhost masterport.

断开复制:从节点执行slaveof no one;slaveof 还可以切主(当前从节点对主节点的复制切换到另一个主节点,slaveof newMasterIp newMasterPort),切主的操作流程:1.断开与旧主节点复制关系。2与新主节点建立复制关系;3删除从节点所有的数据,4对新主节点进行复制操作。

  1. 复制支持的拓扑结构:一主一从(写并发较大时,可以只在从节点开启AOF),一主多从(多个从节点实现读写分离),树状(实现数据一层一层复制,减少主节点同步的数量)
  2. 分析复制的原理:过程 保存主节点信息;主从建立socket连接;发送ping命令(检测主从之间的网络套接字是否可用,主节点是否可以接受命令);权限验证;同步数据集(主节点将全部数据发给从节点,分为全量同步和部分同步);命令持续复制(主节点持续把写命令发送给从节点,保证主从数据的一致性)。

全量复制(开销大) 部分复制,主从维持心跳(ping命令);复制过程是异步的(主节点处理完写直接返回,不等待从节点复制完成,从节点有数据延迟的情况)。使用从节点用于读写分离存在数据延迟,过期数据,从节点可用性等问题。

  1. 复制过程中的问题:

7章 阻塞

单线程架构,所有的读写操作都在一条主线程中完成,高并发场景,若是阻塞,就是噩梦。

阻塞内在原因:不合理的使用API或数据结构(使用keys sort hgetall等),CPU饱和,持久化阻塞等。(慢查询、fork aof阻塞 lua执行太长)

外在原因:CPU竞争,内存交换,网络问题。

8章 理解内存

1、内存消耗分析:info memory统计内存;内存消耗=自身内存+对象内存+缓冲内存+内存碎片

对象内存:sizeof(key)+sizeof(value);缓冲内存=客户端缓冲+复制积压缓冲区(实现部分复制)+AOF缓冲

2、管理内存的原理与方法:通过内存上限和回收策略实现内存管理。

内存回收策略:删除到达过期时间的键对象:采用惰性删除(客户端读取时判断超时,超时会删除并返空)和定时任务删除(定时任务,每秒运行10次,根据键的过期比例,使用快慢等回收键:定时任务在每个数据库空间随机检查20键,发现过期删除,若是超过25%的键过期,则循环回收知道不足25%为止,若回收超时(25ms超时时间)则在触发之前再次快速运行回收(快速超时时间1ms,其他和慢回收一样))机制实现

内存使用到达maxmemory上限时触发内存溢出控制策略:6种

1noeviction.默认策略:不删除数据,拒绝所有写入,返回OOM错误。只支持读

2.volatile-lru:根据LRU算法删除设置超时时间的键,若没有可删除的,则退回1模式

3.all-key-lru:不管设置没设置超时时间,都采用lru算法,知道足够空间

4 allkey-random;随机删除所有键,

5volatile-random 随机删除过期键。

Volatile-ttl:根据键的ttl属性,删除最近将要过期的数据,若是没有,则退回1模式。

3、内存优化技巧:

1.使用scan +object idletime 命令批量查询哪些键长时间未被访问,清理,降低内存占用。

2、高并发场景下,建议字符串长度控制在39字节内,减少创建RedisObject内存分配次数,提高性能。

3.缩减键和值的长度。

4.共享对象池,Redis内部维护0-9999的整数对象池,但是用到LRU的淘汰时Redis禁用共享对象池。

5字符串优化 ; 编码优化;控制键的数量

9章 哨兵(故障自动处理 Redis sentinel实现故障发现 故障自动转移 配置中心客户端通知)

10章集群

11章缓存设计

缓存能加速应用的读写速度,同时降低后端负载。

1、缓存的收益和成本:收益-加速读写,降低后端负载;成本-数据不一致,代码维护成本;运维成本。缓存使用场景:开销大的复杂计算;加速请求响应。

2、缓存更新策略的选择和使用场景:低一致性业务建议配置最大内存和淘汰策略;高一致性业务使用超时剔除和主动更新(可以利用ans通知缓存更新)

3、缓存力度控制方法:缓存全部内容 or部分内容?

4、缓存穿透问题:穿透是查询一个不存在的数据,缓存和存储层都不会命中;缓存穿透将导致不存在的数据每次请求都要到存储层查询,原因:代码数据问题,恶意攻击

解决:1缓存空对象(问题:缓存需要空间多,设置更短的超时时间;数据不一致)2.对key做过滤拦截

Redis总结笔记(总结自Redis开发与运维)_第1张图片

5、缓存无底洞问题优化:添加太多的节点导致 集群性能降低。批量操作key在不同的节点上,多网络延迟

6、缓存雪崩问题优化:缓存突然大量失效,请求打到存储层 调用暴增。解决:1、保证缓存层服务的高可用性;2、依赖隔离组件为后端限流并降级。使用锁,一次只能一个请求访问后端获取数据;缓存失效后端异步更新,

7、热点key重建优化

热点key,并发量大,重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂SQL,多个依赖等。互斥锁来实现:setnx;2 永不过期:缓存层面不过期;功能层面 为每个value设置一个逻辑过期时间(超时后,使用单独现成去构建缓存),但是会有数据不一致的风险。Redis总结笔记(总结自Redis开发与运维)_第2张图片

 

 

你可能感兴趣的:(分布式框架)