Redis的高并发高可用

复制

​ 在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis 副本。复制功能是高可用Redis的基础,后面章节的哨兵和集群都是在复制的基础上实现高可用的。

配置

建立复制

​ 参与复制的Redis实例划分为主节点( master)和从节点(slave)。默认情况下,Redis都是主节点。每个从节点只能有一个主节点,而主节点可以同时具有多个从节点。复制的数据流是单向的,只能由主节点复制到从节点。配置复制的方式有以下三种

  1. 在配置文件中加入slaveof {masterHost } {masterPort}随 Redis启动生效。

  2. 在redis-server启动命令后加入–slaveof {masterHost} {masterPort }生效。

  1. 直接使用命令:slaveof {masterHost} { masterPort}生效。

​ 综上所述,slaveof命令在使用时,可以运行期动态配置,也可以提前写到配置文件中。

​ 例如本地启动两个端口为6880和6881的Redis节点,当然因为是在同一台机器上启动两个redis,所以至少6881的从节点需要修改配置文件slavefor6880_6881.conf:

port 6881
logfile "/home/redis/redis-6.2.4/log/6881.log"
dbfilename dump-6881.rdb
dir "/home/redis/redis-6.2.4/data/"

在127.0.0.1:6881执行如下命令:

slaveof 127.0.0.1 6880

​ slaveof配置都是在从节点发起,这时6880作为主节点,6881作为从节点。复制关系建立后可以看到:

​ 从节点6881上:

主节点6880上:

​ slaveof本身是异步命令,执行slaveof命令时,节点只保存主节点信息后返回,后续复制流程在节点内部异步执行,具体细节见之后。主从节点复制成功建立后,可以使用info replication命令查看复制相关状态。

断开复制

​ slaveof命令不但可以建立复制,还可以在从节点执行slaveof no one来断开与主节点复制关系。例如在6881节点上执行slaveof no one来断开复制。

​ 断开复制主要流程:

  1. 断开与主节点复制关系。
  1. 从节点晋升为主节点。

​ 从节点断开复制后并不会抛弃原有数据,只是无法再获取主节点上的数据变化。

​ 通过slaveof命令还可以实现切主操作,所谓切主是指把当前从节点对主节点的复制切换到另一个主节点。执行slaveof { newMasterIp} { newMasterPort}命令即可,例如把6881节点从原来的复制6880节点变为复制6879节点。

切主内部流程如下:

  1. 断开与旧主节点复制关系。
  1. 与新主节点建立复制关系。

  2. 删除从节点当前所有数据。

  3. 对新主节点进行复制操作。

只读

​ 默认情况下,从节点使用slave-read-only=yes配置为只读模式。由于复制只能从主节点到从节点,对于从节点的任何修改主节点都无法感知,修改从节点会造成主从数据不一致。因此建议线上不要修改从节点的只读模式。

传输延迟

​ 主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP_NODELAY,默认关闭,说明如下:

​ 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机架或同机房部署。

​ 当开启时,主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景,如跨机房部署。

拓扑

​ Redis 的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构,下面分别介绍。

一主一从结构

​ 一主一从结构是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持。

当应用写命令并发量较高且需要持久化时,可以只在从节点上开启 AOF ,这样既保证数据安全性同时也避免了持久化对主节点的性能干扰。但需要注意的是,当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作。

​ 因为主节点之前没有开启持久化功能自动重启后数据集为空,这时从节点如果继续复制主节点会导致从节点数据也被清空的情况,丧失了持久化的意义。安全的做法是在从节点上执行slaveof no one断开与主节点的复制关系,再重启主节点从而避免这一问题。

一主多从结构

​ 一主多从结构(又称为星形拓扑结构)使得应用端可以利用多个从节点实现读写分离。

​ 对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力。同时在日常开发中如果需要执行一些比较耗时的读命令,如:keys、sort等,可以在其中一台从节点上执行,防止慢查询对主节点造成阻塞从而影响线上服务的稳定性。对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也加重了主节点的负载影响服务稳定性。

树状主从结构

​ 树状主从结构(又称为树状拓扑结构)使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量。

​ 数据写入节点A后会同步到B和C节点,B节点再把数据同步到D和E节点,数据实现了一层一层的向下复制。当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力。

复制原理

复制过程

​ 在从节点执行slaveof命令后,复制过程便开始运作。

1、保存主节点(master)信息

​ 执行slaveof后从节点只保存主节点的地址信息便直接返回,这时建立复制流程还没有开始。

2、从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接。

从节点会建立一个socket套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印日志。

​ 如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行slaveof no one取消复制

​ 关于连接失败,可以在从节点执行info replication查看master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印日志。

3、发送ping命令。

连接建立成功后从节点发送ping请求进行首次通信,ping 请求主要目的:检测主从之间网络套接字是否可用、检测主节点当前是否可接受处理命令。

​ 从节点发送的ping命令成功返回,Redis打印日志,并继续后续复制流程:

Master replied to PING,replication can continue. . .

4、权限验证。

​ 如果主节点设置了requirepass参数,则需要密码验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

5、同步数据集。

​ 主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。Redis在2.8版本以后采用新复制命令 psync进行数据同步,原来的sync命令依然支持,保证新旧版本的兼容性。新版同步划分两种情况:全量同步和部分同步。

6、命令持续复制。

​ 当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

数据同步

​ Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。

​ 全量复制:一般用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。

​ 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销。

​ **部分复制是对老版复制的重大优化,有效避免了不必要的全量复制操作。**psync命令运行需要以下支持:

​ 主从节点各自复制偏移量。主节点复制积压缓冲区。主节点运行id。

1.复制偏移量

​ 参与复制的主从节点都会维护自身复制偏移量。主节点( master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在info relication中的master_repl_offset指标中

​ 从节点( slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量。

​ 从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在inforelication中的slave_repl_offset指标中:通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

2.复制积压缓冲区

​ 复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点( master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。

​ 由于缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救。复制缓冲区相关统计信息保存在主节点的info replication中:

repl_backlog_active : 1 //开启复制缓冲区

repl_backlog_size:1048576//缓冲区最大长度

repl_backlog_first_byte_offset : 7479//起始偏移量,计算当前缓冲区可用范围

repl_backlog_histlen: 1048576//已保存数据的有效长度。

3.主节点运行ID

每个Redis节点启动后都会动态分配一个40位的十六进制字符串作为运行ID。**运行ID的主要作用是用来唯一识别Redis节点,**比如从节点保存主节点的运行ID识别自己正在复制的是哪个主节点。如果只使用ip+port的方式识别主节点,那么主节点重启变更了整体数据集(如替换RDB/AOF文件),从节点再基于偏移量复制数据将是不安全的,因此当运行ID变化后从节点将做全量复制。可以运行info server命令查看当前节点的运行ID:

​ run_id:545f7c76183d0798a327591395b030000ee6def9

​ 需要注意的是Redis关闭再启动后,运行ID会随之改变。

4.psync命令

​ 从节点使用psync命令完成部分复制和全量复制功能,命令格式: psync {runId}{ offset},参数含义如下:

runId:从节点所复制主节点的运行id。

offset:当前从节点已复制的数据偏移量。

流程说明:

  1. 从节点(slave)发送psync命令给主节点,参数runId是当前从节点保存的主节点运行ID,如果没有则默认值为?,参数offset是当前从节点保存的复制偏移量,如果是第一次参与复制则默认值为-1。
  1. 主节点(master)根据psync参数和自身数据情况决定响应结果:

如果回复+FULLRESYNC { runId }{ offset},那么从节点将触发全量复制流程。

如果回复+CONTINUE,从节点将触发部分复制流程。

如果回复+ERR,说明主节点版本低于Redis 2.8,无法识别psync命令,从节点将发送旧版的sync命令触发全量复制流程。

全量复制

​ 全量复制是Redis最早支持的复制方式,也是主从第一次建立复制时必须经历的阶段。触发全量复制的命令是sync和psync。

​ psync全量复制流程,它与2.8以前的sync全量复制机制基本一致。

流程说明:

  1. 发送psync命令进行数据同步,由于是第一次进行复制,从节点没有复制偏移量和主节点的运行ID,所以发送psync ? -1。
  1. 主节点根据psync ? -1解析出当前为全量复制,回复 +FULLRESYNC响应。
  1. 从节点接收主节点的响应数据保存运行ID和偏移量offset,并打印日志。
  1. 主节点执行bgsave保存RDB 文件到本地。

  2. 主节点发送RDB文件给从节点,从节点把接收的RDB文件保存在本地并直接作为从节点的数据文件,接收完RDB后从节点打印相关日志,可以在日志中查看主节点发送的数据量。

​ 需要注意,对于数据量较大的主节点,比如生成的RDB文件超过6GB 以上时要格外小心。传输文件这一步操作非常耗时,速度取决于主从节点之间网络带宽,通过细致分析Full resync和 MASTER<-> SLAVE这两行日志的时间差,可以算出RDB文件从创建到传输完毕消耗的总时间。如果总时间超过repl-timeout所配置的值(默认60秒),从节点将放弃接受RDB文件并清理已经下载的临时文件,导致全量复制失败。

​ 针对数据量较大的节点,建议调大repl-timeout参数防止出现全量同步数据超时。例如对于千兆网卡的机器,网卡带宽理论峰值大约每秒传输100MB,在不考虑其他进程消耗带宽的情况下,6GB的RDB文件至少需要60秒传输时间,默认配置下,极易出现主从数据同步超时。

  1. 对于从节点开始接收RDB快照到接收完成期间,主节点仍然响应读写命令,因此主节点会把这期间写命令数据保存在复制客户端缓冲区内,当从节点加载完RDB文件后,主节点再把缓冲区内的数据发送给从节点,保证主从之间数据一致性。如果主节点创建和传输RDB的时间过长,对于高流量写入场景非常容易造成主节点复制客户端缓冲区溢出。默认配置为:
client-output-buffer-limit slave 256MB 64MB 60

​ 意思是如果60秒内缓冲区消耗持续大于64MB或者直接超过256MB时,主节点将直接关闭复制客户端连接,造成全量同步失败。

​ 对于主节点,当发送完所有的数据后就认为全量复制完成,打印成功日志,但是对于从节点全量复制依然没有完成,还有后续步骤需要处理。

  1. 从节点接收完主节点传送来的全部数据后会清空自身旧数据
  1. 从节点清空数据后开始加载RDB文件,对于较大的RDB文件,这一步操作依然比较耗时。
  2. 从节点成功加载完RDB后,如果当前节点开启了AOF持久化功能,它会立刻做bgrewriteaof操作,为了保证全量复制后AOF持久化文件立刻可用。

​ 通过分析全量复制的所有流程,读者会发现全量复制是一个非常耗时费力的操作。它的时间开销主要包括:

主节点bgsave时间。

RDB文件网络传输时间。

从节点清空数据时间。

从节点加载RDB的时间。

可能的AOF重写时间。

​ 例如我们线上数据量在6G左右的主节点,从节点发起全量复制的总耗时在2分钟左右。因此当数据量达到一定规模之后,由于全量复制过程中将进行多次持久化相关操作和网络数据传输,这期间会大量消耗主从节点所在服务器的CPU、内存和网络资源。所以除了第一次复制时采用全量复制在所难免之外,对于其他场景应该规避全量复制的发生。正因为全量复制的成本问题,Redis实现了部分复制功能。

部分复制

​ 部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync{runId} {offset}命令实现。当从节点(slave)正在复制主节点(master)时,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区内存在这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据,所以开销很小。

流程说明:

  1. 当主从节点之间网络出现中断时,如果超过repl-timeout时间,主节点会认为从节点故障并中断复制连接,打印日志。如果此时从节点没有宕机,也会打印与主节点连接丢失日志。
  1. 主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内部存在的复制积压缓冲区,依然可以保存最近一段时间的写命令数据,默认最大缓存1MB。

  2. 当主从节点网络恢复后,从节点会再次连上主节点,打印日志。

  3. 当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们当作psync参数发送给主节点,要求进行部分复制操作。

  4. 主节点接到psync命令后首先核对参数runId是否与自身一致,如果一致,说明之前复制的是当前主节点;之后根据参数offset在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送+CONTINUE响应,表示可以进行部分复制。如果不在,则退化为全量复制。

  5. 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。发送的数据量可以在主节点的日志,传递的数据远远小于全量数据。

心跳

​ 主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令。

主从心跳判断机制:

  1. 主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信,通过client list命令查看复制相关客户端信息,主节点的连接状态为flags=M,从节点连接状态为flags=S。

  2. 主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活性和连接状态。可通过参数repl-ping-slave-period控制发送频率。

  3. 从节点在主线程中每隔1秒发送replconf ack {offset}命令,给主节点上报自身当前的复制偏移量。replconf命令主要作用如下:

    实时监测主从节点网络状态;

    上报自身复制偏移量,检查复制数据是否丢失,如果从节点数据丢失,再从主节点的复制缓冲区中拉取丢失数据

    主节点根据replconf命令判断从节点超时时间,体现在info replication统计中的lag信息中,lag表示与从节点最后一次通信延迟的秒数,正常延迟应该在0和1之间。如果超过repl-timeout配置的值((默认60秒),则判定从节点下线并断开复制客户端连接。即使主节点判定从节点下线后,如果从节点重新恢复,心跳检测会继续进行。

异步复制

​ **主节点不但负责数据读写,还负责把写命令同步给从节点。**写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成。

​ 由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟(ap系统)。具体延迟多少字节,我们可以在主节点执行info replication命令查看相关指标获得。

​ 在统计信息中可以看到从节点slave信息,分别记录了从节点的ip和 port,从节点的状态,offset表示当前从节点的复制偏移量,master_repl_offset表示当前主节点的复制偏移量,两者的差值就是当前从节点复制延迟量。Redis 的复制速度取决于主从之间网络环境,repl-disable-tcp-nodelay,命令处理速度等。正常情况下,延迟在1秒以内。

哨兵Redis Sentinel

​ Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的。Redis 从 2.8开始正式提供了Redis Sentinel
[ˈsentɪnl](哨兵)架构来解决这个问题。

主从复制的问题

​ Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:第一,作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备“顶”上来,并且保证数据尽量不丢失(主从复制是最终一致性)。第二,从节点可以扩展主节点的读能力,一旦主节点不能支撑住大并发量的读操作,从节点可以在一定程度上帮助主节点分担读压力。

但是主从复制也带来了以下问题:

1、一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预。

2、主节点的写能力受到单机的限制。

3、主节点的存储能力受到单机的限制。

​ **其中第一个问题就是Redis 的高可用问题,**将在下一个小节进行分析。第二、三个问题属于Redis的分布式问题,需要使用Redis Cluster,后面将会说明。

Redis Sentinel的高可用性

名称解释

​ 由于对Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel之前,我们统一下几个名词的定义。

主节点(master): Redis主服务,一个独立的Redis进程

从节点(slave): Redis从服务,一个独立的Redis进程

Redis 数据节点: 主节点和从节点

Sentinel节点: 监控Redis数据节点,一个独立的Sentinel进程

Sentinel节点集合: 若干 Sentinel节点的组合

Redis Sentinel: Redis高可用实现方案,Sentinel节点集合和 Redis数据节点集合

应用方: 泛指一个或多个客户端一个或者多个客户端进程或者线程

可用性分析

​ Redis主从复制模式下,一旦主节点出现了故障不可达,需要人工干预进行故障转移,无论对于Redis的应用方还是运维方都带来了很大的不便。对于应用方来说无法及时感知到主节点的变化,必然会造成一定的写数据丢失和读数据错误,甚至可能造成应用方服务不可用。对于Redis的运维方来说,整个故障转移的过程是需要人工来介入的,故障转移实时性和准确性上都无法得到保障。

​ 当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

​ Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。

​ 注这里的分布式是指:Redis数据节点、Sentinel节点集合、客户端分布在多个物理节点的架构。

​ Redis Sentinel与 Redis 主从复制模式只是多了若干Sentinel节点,所以Redis Sentinel并没有针对Redis节点做了特殊处理。

​ 从逻辑架构上看,Sentinel节点集合会定期对所有节点进行监控,特别是对主节点的故障实现自动转移。

​ 下面以1个主节点、2个从节点、3个 Sentinel节点组成的Redis Sentinel为例子进行说明。

整个故障转移的处理逻辑有下面4个步骤:

1)主节点出现故障,此时两个从节点与主节点失去连接**,主从复制失败。**

2)每个Sentinel节点通过定期监控发现主节点出现了故障。

3)多个Sentinel节点对主节点的故障达成一致,选举出sentinel-3节点作为领导者负责故障转移。

4)Sentinel领导者节点执行了自动化故障转移,包括通知客户端,重新选择主节点,建立新的复制关系等等。

​ 通过上面介绍的Redis Sentinel逻辑架构以及故障转移的处理,可以看出Redis Sentinel具有以下几个功能:

监控: Sentinel节点会定期检测Redis 数据节点、其余Sentinel节点是否可达。

通知: Sentinel节点会将故障转移的结果通知给应用方。

主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。

配置提供者:在Redis Sentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。

​ 同时看到,Redis Sentinel包含了若个 Sentinel节点,这样做也带来了两个好处:对于节点的故障判断是由多个Sentinel节点共同完成,这样可以有效地防止误判。Sentinel节点集合是由若干个Sentinel节点组成的,这样即使个别Sentinel节点不可用,整个 Sentinel节点集合依然是健壮的。

​ 但是Sentinel节点本身就是独立的Redis节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令。

安装和部署

部署拓扑结构

我们以以3个 Sentinel节点、1个主节点、2个从节点组成一个Redis Sentinel进行说明。

master: 127.0.0.1 6885主节点

slave-1: 127.0.0.1 6886 slave-1节点

slave-2: 127.0.0.1 6887 slave-2节点

sentinel-1: 127.0.0.1 26885 sentinel-1节点

sentinel-2: 127.0.0.1 26886 sentinel-2节点

sentinel-3: 127.0.0.1 26887 sentinel-3节点

集群结构:

部署Redis 数据节点

启动主节点

配置:

sen_master_6885.confport 6885daemonize yeslogfile "/home/redis/redis-6.2.4/log/6885.log"dbfilename dump-6885.rdbdir "/home/redis/redis-6.2.4/data/"

启动主节点:

./redis-server ../conf/sen_master_6885.conf

确认是否启动。一般来说只需要ping命令检测一下就可以,确认 Redis数据节点是否已经启动。

[root@localhost src]# ./redis-cli -p 6885
127.0.0.1:6885> keys
(error) ERR wrong number of arguments for 'keys' command
127.0.0.1:6885> keys *
(empty array)
127.0.0.1:6885> quit
[root@localhost src]# ./redis-cli -p 6885 ping
PONG
启动两个从节点配置
  1. 两个从节点的配置是完全一样的,下面以一个从节点为例子进行说明,和主节点的配置,不一样的是添加了slaveof配置。
sen_slave_6886.confport 6886daemonize yeslogfile "/home/redis/redis-6.2.4/log/6886.log"dbfilename dump-6886.rdbdir "/home/redis/redis-6.2.4/data/"slaveof 127.0.0.1 6885
  1. 启动两个从节点:
./redis-server ../conf/sen_slave_6886.conf./redis-server ../conf/sen_slave_6887.conf

验证:

[root@localhost src]# ./redis-cli -p 6886 ping
PONG
[root@localhost src]# ./redis-cli -p 6887 ping
PONG
  1. 确认主从关系

​ 主节点的视角,它有两个从节点,分别是127.0.0.1:6886和127.0.0.1:6887

./redis-cli -p 6885 info replication[root@localhost src]# ./redis-cli -p 6885 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6886,state=online,offset=126,lag=1
slave1:ip=127.0.0.1,port=6887,state=online,offset=126,lag=1
master_failover_state:no-failover
master_replid:f5603cdcc7326c9d39cac434224e6c022dfc53dd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:126

​ 从节点的视角,它的主节点是127.0.0.1:6885

./redis-cli -p 6886 info replication[root@localhost src]# ./redis-cli -p 6886 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6885
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:210
slave_repl_offset:210
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f5603cdcc7326c9d39cac434224e6c022dfc53dd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210

部署Sentinel节点

​ 3个Sentinel节点的部署方法是完全一致的(端口不同),下面以sentinel-26885节点的部署为例子进行说明。

配置Sentinel节点

sentinel_26885.conf

port 26885
daemonize yes
logfile "/root/redis-6.2.6/log/26885.log"
dbfilename dump-26885.rdb
dir "/root/redis-6.2.6/data/"
sentinel monitor mymaster 127.0.0.1 6885 2
sentinel down-after-milliseconds mymaster 3000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 1000

1 ) Sentinel节点的默认端口是26379,我们改为26885。

2 )sentinel monitor mymaster 127.0.0.1 6885 2配置代表sentinel-26885节点需要监控127.0.0.1:6885 这个主节点,2代表判断主节点失败至少需要2个Sentinel节点同意,mymaster是主节点的别名,其余Sentinel配置将在下一节进行详细说明。

启动Sentinel节点

Sentinel节点的启动方法有两种:

方法一,使用redis-sentinel命令:

./redis-sentinel ../conf/sentinel_26885.conf

方法二,使用redis-server命令加–sentinel参数:

./redis-server ../conf/sentinel_26885.conf --sentinel

两种方法本质上是—样的。

[root@localhost src]# ./redis-cli -p 26885 ping
PONG
确认

​ Sentinel节点本质上是一个特殊的Redis节点,所以也可以通过info命令来查询它的相关信息,从下面info的Sentinel片段来看,Sentinel节点找到了主节点127.0.0.1:6885,发现了它的两个从节点。

./redis-cli -p 26885 info Sentinel
[root@localhost src]# ./redis-cli -p 26885 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6885,slaves=2,sentinels=3

​ 当三个 Sentinel节点都启动后,同时发现Redis Sentinel一共有3个Sentinel节点。这里这里我们可以看到Sentinel节点能够彼此感知到对方,同时能够感知到Redis数据节点。

至此Redis Sentinel已经搭建起来了,整体上还是比较容易的,但是有2点需要强调一下:

  1. 生产环境中建议Redis Sentinel的所有节点应该分布在不同的物理机上。

  2. Redis Sentinel中的数据节点和普通的Redis数据节点在配置上没有任何区别,只不过是添加了一些Sentinel节点对它们进行监控。

配置说明

参数变化

​ 当所有节点启动后,配置文件中的内容会发生变化,体现在三个方面:

​ Sentinel节点自动发现了从节点、其余Sentinel节点。

去掉了默认配置,例如 parallel-syncs、failover-timeout参数。

​ 添加了配置纪元相关参数。

启动之前:

port 26885
daemonize yes
logfile "/root/redis-6.2.6/log/26885.log"
dbfilename dump-26885.rdb
dir "/root/redis-6.2.6/data/"
sentinel monitor mymaster 127.0.0.1 6885 2
sentinel down-after-milliseconds mymaster 3000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 1000

启动之后:

port 26885
daemonize yes
logfile "/root/redis-6.2.6/log/26885.log"
dbfilename "dump-26885.rdb"
dir "/root/redis-6.2.6/data"
sentinel monitor mymaster 127.0.0.1 6885 2
sentinel down-after-milliseconds mymaster 3000sentinel failover-timeout mymaster 1000
# Generated by CONFIG REWRITE
protected-mode no
pidfile "/var/run/redis.pid"
user default on nopass ~* &* +@all
sentinel myid 1e57be59952991c9c1270d25539cddcba6544986
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel current-epoch 0
sentinel known-replica mymaster 127.0.0.1 6886
sentinel known-replica mymaster 127.0.0.1 6887
sentinel known-sentinel mymaster 127.0.0.1 26887 9175a26e2248b1db1e588f2c4f69bef2c44f3a37
sentinel known-sentinel mymaster 127.0.0.1 26886 0befaca85c7d715d53b6c7ed87eb84559e7a9bf3

配置文件会自动增加

# Generated by CONFIG REWRITE
protected-mode no
pidfile "/var/run/redis.pid"
user default on nopass ~* &* +@all
sentinel myid 1e57be59952991c9c1270d25539cddcba6544986
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel current-epoch 0
sentinel known-replica mymaster 127.0.0.1 6886
sentinel known-replica mymaster 127.0.0.1 6887
sentinel known-sentinel mymaster 127.0.0.1 26887 9175a26e2248b1db1e588f2c4f69bef2c44f3a37
sentinel known-sentinel mymaster 127.0.0.1 26886 0befaca85c7d715d53b6c7ed87eb84559e7a9bf3
常见配置说明
sentinel monitor
sentinel monitor host-name ip 端口 quorum

​ Sentinel节点会定期监控主节点,所以从配置上必然也会有所体现,本配置说明Sentinel节点要监控的是一个名字叫做,ip地址和端口为 的主节点。代表要判定主节点最终不可达所需要的票数。但实际上Sentinel节点会对所有节点进行监控,但是在Sentinel节点的配置中没有看到有关从节点和其余Sentinel节点的配置,那是因为Sentinel节点会从主节点中获取有关从节点以及其余Sentinel节点的相关信息。

​ 参数用于故障发现和判定,例如将quorum配置为2,代表至少有2个Sentinel节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为Sentinel节点的一半加1。

​ 同时还与Sentinel节点的领导者选举有关,至少要有max(quorum,num(sentinels)/2+1)个Sentinel节点参与选举,才能选出领导者Sentinel,从而完成故障转移。例如有5个 Sentinel节点,quorum=4,那么至少要有max(quorum, num (sentinels)/2 +1)=4个在线Sentinel节点才可以进行领导者选举。

sentinel down-after-milliseconds
sentinel down-after-milliseconds host-name times

​ 每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没有有效的回复,则判定节点不可达,(单位为毫秒)就是超时时间。这个配置是对节点失败判定的重要依据。

​ down-after-milliseconds虽然以为参数,但实际上对Sentinel 节点、主节点、从节点的失败判定同时有效。

sentinel parallel-syncs
sentinel parallel-syncs <host-name> <nums>

​ 当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数。

​ 如果这个参数配置的比较大,那么多个从节点会向新的主节点同时发起复制操作,尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制,必然会对主节点所在的机器造成一定的网络和磁盘IO开销。

sentinel failover-timeout
sentinel failover-timeout <host-name> <times>

failover-timeout通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段:

a)选出合适从节点。

b)晋升选出的从节点为主节点。

c)命令其余从节点复制新的主节点。

d)等待原主节点恢复后命令它去复制新的主节点。

failover-timeout的作用具体体现在四个方面:

  1. 如果Redis Sentinel对一个主节点故障转移失败,那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
  1. 在b)阶段时,如果Sentinel节点向a)阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障),当此过程超过failover-timeout时,则故障转移失败。

  2. 在b)阶段如果执行成功,Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点,如果此过程执行时间超过failover-timeout时,则故障转移失败。

  3. 如果c)阶段执行时间超过了failover-timeout(不包含复制时间),则故障转移失败。注意即使超过了这个时间,Sentinel节点也会最终配置从节点去同步最新的主节点。

监控多个主节点

Redis Sentinel可以同时监控多个主节点。

​ 配置方法也比较简单,只需要指定多个masterName来区分不同的主节点即可,例如下面的配置监控monitor master-1(10.10.xx.1:6379)和monitor master-2 (10.10.xx.2:6379)两个主节点:

#监控master-business-1

sentinel monitor master-1 10.10.xx.1 6379 2sentinel down-after-milliseconds master-1 60000sentinel failover-timeout master-1 180000sentinel parallel-syncs master-1 1

#监控master-business-2

sentinel monitor master-2 10.16.xx.2 6380 2sentinel down-after-milliseconds master-business-210000sentinel failover-timeout master-business-2180000sentinel parallel-syncs master-business-21

部署建议

1 ) Sentinel节点不应该部署在一台物理“机器”上。

​ 这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的IP地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现Sentinel节点集合真正的高可用,请勿将Sentinel节点部署在同一台物理机器上。

2)部署至少三个且奇数个的Sentinel节点。

​ 3个以上是通过增加 Sentinel节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加1个节点,奇数个节点可以在满足该条件的基础上节省一个节点。这是因为:

A、在节点数量是奇数个的情况下, 集群总能对外提供服务(即使损失了一部分节点);如果节点数量是偶数个,会存在集群不能用的可能性(脑裂成两个均等的子集群的时候)。

B、假如集群1 ,有3个节点,3/2=1.5 , 即集群想要正常对外提供服务(即leader选举成功),至少需要2个节点是正常的。换句话说,3个节点的集群,允许有一个节点宕机。

​ 假如集群2,有4个节点,4/2=2 ,即想要正常对外提供服务(即leader选举成功),至少需要3个节点是正常的。换句话说,4个节点的集群,也允许有一个节点宕机。

​ 那么问题就来了, 集群1与集群2都有允许1个节点宕机的容错能力,但是集群2比集群1多了1个节点。在相同容错能力的情况下,本着节约资源的原则,集群的节点数维持奇数个更好一些。

4)只有一套Sentinel,还是每个主节点配置一套 Sentinel ?

Sentinel节点集合可以只监控一个主节点,也可以监控多个主节点。

​ 那么在实际生产环境中更偏向于哪一种部署方式呢,下面分别分析两种方案的优缺点。

方案一,一套Sentinel,很明显这种方案在一定程度上降低了维护成本,因为只需要维护固定个数的Sentinel节点,集中对多个Redis数据节点进行管理就可以了。但是这同时也是它的缺点,如果这套 Sentinel节点集合出现异常,可能会对多个Redis数据节点造成影响。还有如果监控的Redis数据节点较多,会造成Sentinel节点产生过多的网络连接,也会有一定的影响。

方案二,多套Sentinel,显然这种方案的优点和缺点和上面是相反的,每个Redis主节点都有自己的Sentinel节点集合,会造成资源浪费。但是优点也很明显,每套Redis Sentinel都是彼此隔离的。

​ 如果Sentinel节点集合监控的是同一个业务的多个主节点集合,那么使用方案一、否则一般建议采用方案二。

监控API

Sentinel节点是一个特殊的Redis 节点,它有自己专属的API。

1、sentinel masters

展示所有被监控的主节点状态以及相关的统计信息

2.sentinel master

展示指定的主节点状态以及相关的统计信息

3.sentinel slaves

展示指定的从节点状态以及相关的统计信息

4.sentinel sentinels

展示指定的 Sentinel节点集合(不包含当前Sentinel节点)

5.sentinel get-master-addr-by-name

​ 返回指定主节点的IP地址和端口

6.sentinel reset

​ 当前Sentinel节点对符合 (通配符风格)主节点的配置进行重置,包含清除主节点的相关状态(例如故障转移),重新发现从节点和 Sentinel节点。

7.sentinel failover

​ 对指定主节点进行强制故障转移(没有和其他Sentinel节点“协商”),当故障转移完成后,其他Sentinel节点按照故障转移的结果更新自身配置,这个命令在Redis Sentinel的日常运维中非常有用。

8.sentinel ckquorum

​ 检测当前可达的 Sentinel节点总数是否达到的个数。例如 quorum=3,而当前可达的Sentinel节点个数为2个,那么将无法进行故障转移,Redis Sentinel的高可用特性也将失去。

9.sentinel flushconfig

​ 将Sentinel节点的配置强制刷到磁盘上,这个命令Sentinel节点自身用得比较多,对于开发和运维人员只有当外部原因(例如磁盘损坏)造成配置文件损坏或者丢失时,这个命令是很有用的。

10.sentinel remove

​ 取消当前Sentine 节点对于指定主节点的监控。

11.sentinel monitor

这个命令和配置文件中的含义是完全一样的,只不过是通过命令的形式来完成Sentinel节点对主节点的监控。

12.sentinel set

​ 动态修改Sentinel节点配置选项。

13.sentinel is-master-down-by-addr

​ Sentinel节点之间用来交换对主节点是否下线的判断,根据参数的不同,还可以作为Sentinel领导者选举的通信方式。

实现原理

​ Redis Sentinel的基本实现中包含以下: Redis Sentinel 的定时任务、主观下线和客观下线、Sentinel领导者选举、故障转移等等知识点,学习这些可以让我们对Redis Sentinel的高可用特性有更加深入的理解和认识。

三个定时监控任务

​ 一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控:

  1. 每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构,Sentinel节点通过对上述结果进行解析就可以找到相应的从节点。

这个定时任务的作用具体可以表现在三个方面:

通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显式配置监控从节点。

当有新的从节点加入时都可以立刻感知出来。

节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息。

  1. 每隔2秒,每个Sentinel节点会向Redis数据节点的_sentinel_:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:

发现新的Sentinel节点:通过订阅主节点的__sentinel__:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该 Sentinel节点创建连接。

Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。

  1. 每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。

通过上面的定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点的监控,这个定时任务是节点失败判定的重要依据。

主观下线和客观下线

主观下线

​ 上一小节介绍的第三个定时任务,每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,**Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。**从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判的可能。

客观下线

​ 当Sentinel主观下线的节点是主节点时,**该Sentinel节点会通过sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,**当超过个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的。

领导者Sentinel节点选举

​ **假如Sentinel节点对于主节点已经做了客观下线,那么是不是就可以立即进行故障转移了?**当然不是,实际上故障转移的工作只需要一个Sentinel节点来完成即可,所以 Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举,Redis Sentinel进行领导者选举的大致思路如下:

1)每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。

2)收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。

3)如果该Sentinel节点发现自己的票数已经大于等于max (quorum,num(sentinels)/2+1),那么它将成为领导者。

4)如果此过程没有选举出领导者**,将进入下一次选举。**

选举的过程非常快,基本上谁先完成客观下线,谁就是领导者。

Raft算法的具体说明,以下链接:

https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md

具体的实现可以参考蚂蚁金服开源的生产级 Java Raft 算法库:

https://github.com/sofastack/sofa-jraft

故障转移

领导者选举出的Sentinel节点负责故障转移,具体步骤如下:

1)在从节点列表中选出一个节点作为新的主节点,选择方法如下:

a)过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节点 ping响应、与主节点失联超过down-after-milliseconds*10秒。

b)选择slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。

c)选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。

d)选择runid最小的从节点。

2 ) Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。

3 ) Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关。

4 ) Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点。

高可用读写分离

  1. 从节点一般可以起到两个作用:第一,当主节点出现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel已经实现了该功能的自动化,实现了真正的高可用。第二,扩展主节点的读能力,尤其是在读多写少的场景非常适用。

但上述模型中,从节点不是高可用的,如果slave-1节点出现故障,首先客户端client-1将与其失联,其次Sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移是针对主节点的。所以很多时候,Redis Sentinel中的从节点仅仅是作为主节点一个热备,不让它参与客户端的读操作,就是为了保证整体高可用性,但实际上这种使用方法还是有一些浪费,尤其是在有很多从节点或者确实需要读写分离的场景,所以如何实现从节点的高可用是非常有必要的。

  1. Redis Sentinel读写分离设计思路参考

​ Redis Sentinel在对各个节点的监控中,如果有对应事件的发生,都会发出相应的事件消息,其中和从节点变动的事件有以下几个:

+switch-master:切换主节点(原来的从节点晋升为主节点),说明减少了某个从节点。

+convert-to-slave :切换从节点(原来的主节点降级为从节点),说明添加了某个从节点。

**+sdown

【5. Redis的高并发高可用】相关推荐

  1. 高并发高可用的 架构实践

    一. 设计理念 1.     空间换时间 1)     多级缓存,静态化 客户端页面缓存(http header中包含Expires/Cache of Control,last modified(30 ...

  2. 构建高并发高可用的电商平台架构实践 转载

    2019独角兽企业重金招聘Python工程师标准>>> 构建高并发高可用的电商平台架构实践 转载 博客分类: java 架构 [-] 一 设计理念 空间换时间 多级缓存静态化 索引 ...

  3. 构建高并发高可用的电商平台架构实践 转自网络

    从各个角度总结了电商平台中的架构实践,由于时间仓促,定了个初稿,待补充完善,欢迎大家一起交流. 转载请声明出处: 作者:杨步涛 关注分布式架构.大数据.搜索.开源技术 QQ:306591368 技术B ...

  4. 构建高并发高可用的电商平台架构实践

    问题导读: 1.如何构建高并发电商平台架构 2.哈希.B树.倒排.bitmap的作用是什么? 3.作为软件工程师,该如何实现读写? 4.如何实现负载均衡.反向代理? 5.电商业务是什么? 6.基础中间 ...

  5. 构建高并发高可用的电商平台架构实践(一)

    从各个角度总结了电商平台中的架构实践,由于时间仓促,定了个初稿,待补充完善,欢迎大家一起交流. 一. 设计理念 1.      空间换时间 1)      多级缓存,静态化 客户端页面缓存(http ...

  6. 高并发高可用系统的常见应对策略 秒杀等-(阿里)

    对于一个需要处理高并发的系统而言,可以从多个层面去解决这个问题. 1.数据库系统:数据库系统可以采取集群策略以保证某台数据库服务器的宕机不会影响整个系统,并且通过负载均衡策略来降低每一台数据库服务器的 ...

  7. 什么是高并发高可用一致性?| 现代网站架构发展 | C 语言实现布隆过滤器

    大话高并发高可用一致性|网站架构发展|网络编程缓存|C 语言实现布隆过滤器 Bloom Filter 编程练习 | GTest 教程 两个部分分为本文章,一部分是布隆过滤器的实现指引. 一个提供的前置 ...

  8. 如何设计一个高并发高可用的秒杀或抢券系统

    一个大型网站应用一般都是从最初小规模网站甚至是单机应用发展而来的,为了让系统能够支持足够大的业务量,从前端到后端也采用了各种各样技术,前端静态资源压缩整合.使用CDN.分布式SOA架构.缓存.数据库加 ...

  9. php小程序秒抢高并发,PHP 如何设计一个高并发高可用的秒杀或抢券系统

    一个大型网站应用一般都是从最初小规模网站甚至是单机应用发展而来的,为了让系统能够支持足够大的业务量,从前端到后端也采用了各种各样技术,前端静态资源压缩整合.使用CDN.分布式SOA架构.缓存.数据库加 ...

最新文章

  1. sscanf用法(转)
  2. Windows平台Android开发环境搭建几个注意点
  3. poj2955Brackets(区间DP)
  4. python初级数据分析师薪资_学会数据分析,薪资翻倍?!
  5. 美国大学生数学建模竞赛 细节问题(23条)汇总!!!
  6. lesson7 集合set
  7. python matlib库_python matplotlib 库学习
  8. Java 8开发的4大顶级技巧
  9. java父类引用子类_java多态,如何理解父类引用指向子类对象
  10. [转载]如何用C#语言构造蜘蛛程序
  11. 被Google收购的Postini
  12. 敏捷开发之产品级经验分享
  13. 《图解数据结构》.pdf
  14. InstallShield打包程序
  15. acm竞赛2016c语言真题,C语言acm竞赛习题集锦.doc
  16. 用计算机软件绘制思维导图,电脑软件绘制思维导图操作教程分享
  17. PHP语言之MySQL操作
  18. 10个3d立体字ps金属字图层样式下载
  19. 国外开放的硕博论文、期刊、数据库下载网站
  20. 编程之美1 哈利波特买书问题

热门文章

  1. win10计算机网络设置在哪,Win10系统电脑中的网络状态在哪里查看
  2. eclipse maven 搭建 SSM(Spring+SpringMVC+MyBatis)开发环境 和 MyBatis 自动生成的 maven 插件配置
  3. 9种小程序赚钱方法!看懂的人已经在行动了
  4. 【HBase】17-协处理器
  5. Excel区间数据拆分
  6. 检查两个时间段是否有时间重叠(允许重叠一部分时间不算重叠)
  7. java架构师年薪_Java架构师一般的薪资是多少?高不高?
  8. 移动端登录页面-vue
  9. 单链表的头插法与尾插法
  10. mysql---创建学生表