Redis

Posted by 余腾 on 2019-04-07
Estimated Reading Time 38 Minutes
Words 9.9k In Total
Viewed Times

REmote DIctionary Server(远程字典服务器) 非关系型数据库

  • NoSQL 这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

Redis 作为一个独立的分布式的缓存服务器,为多个web服务器提供了一个共享的高性能缓存服务。
然后又出现了一致性hash来解决增加或减少缓存服务器导致重新hash带来的大量缓存失效的弊端。

一、Why?主从复制 读写分离

  • 由于数据库的写入压力增加,Memcached只能缓解数据库的读取压力。
  • 读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。
  • Mysql的 master-slave 模式成为这个时候的网站标配了。

分表分库 + 水平拆分 + mysql 集群

  • 在Memcached的高速缓存,MySQL的主从复制,读写分离的基础之上,这时MySQL主库的写压力开始出现瓶颈。
  • 而数据量的持续猛增,由于 MyISAM 使用表锁,在高并发下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。
  • 1、MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是有索引的顺序访问方法,它是存储记录和文件的标准方法。

    • 不支持事务、不具备AICD特性、而且不支持外键。
    • 读写相互阻塞(不仅会在写入的时候阻塞读取、还会在读取的时候阻塞写入、但是读取不会阻塞读取);
    • 读取速度快、占用资源比较少。如果执行大量的select MyISAM比较适合。
    • MyISAM支持全文类型索引。
  • 2、InnoDB:支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。

    • 而InnoDB不支持全文索引。
    • 如果有大量的update和insert,建议用InnoDB,特别是针对多个并发和QPS较高的情况。
    • Innodb的行锁模式有以下几种:
      • 共享锁,排他锁,意向共享锁(表锁),意向排他锁(表锁),间隙锁。
对比项 MyISAM InnoDB
主外键 不支持 支持
事务 不支持 支持
行表锁 表锁,不适合高并发 行锁,适合高并发
缓存 只缓存索引,不缓存真实数据 不仅缓存索引,还要缓存真实数据
表空间
关注点 性能 事务
默认安装 Y Y

  • 同时,开始流行使用 分表 分库 来缓解写压力和数据增长的扩展问题。
  • MySQL推出了还不太稳定的表分区,虽然MySQL推出了 MySQL Cluster 集群,但性能也不能满足互联网的要求,只是在高可靠性上提供了非常大的保证。

Mysql 扩展瓶颈

  • MySQL数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库。
    • 比如1000万4KB大小的文本就接近40GB,如果能把这些数据从MySQL省去,MySQL将变得非常的小。关系数据库很强大,但是它并不能很好的应付所有的应用场景。
  • MySQL的扩展性差(需要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用MySQL的开发人员面临的问题。


二、NoSQL

当然,目前还是 SQL和NoSQL 一起使用。

  • NoSQL:不仅仅是SQL、没有声明性查询语言、没有预定义模式、非结构化和不可预知的数据
  • 键:值对存储。 列存储,文档存储,图形数据库
  • 最终一致性,而非ACID属性
  • CAP定理
  • 高性能,高可用性,可伸缩性

  • 容易扩展
    • NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。
    • 数据之间无关系,这样就非常容易扩展。无形之间,在架构层面上带来了可扩展的能力。

  • 大数据量高性能
    • NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
    • 一般MySQL使用 Query Cache,每次表的更新Cache就失效,是一种大粒度的Cache。
    • NoSQL的 Cache 是记录级的,是一种细粒度的Cache。

  • 多样灵活的数据模型
    • NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。

NoSQL 数据库分类

  • K/V 键值对:Redis 高性能的(key/value)分布式内存数据库,基于内存运行。
  • 列存储数据库: HBase
  • 文档数据库:MongDB。基于分布式文件存储、多文字信息描述类(文档数据库 BSON 格式比较多),IO读写性能变差。
    • BSON:是一种类json的一种二进制形式的存储格式。
    • 简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象。
  • 图形数据库:Graph。

Mysql:冷数据(名称、价格,出厂日期,生产厂商等)
TFS:文件系统
ISearch:搜索引擎、关键字
Redis:热点高频信息


三、CAP + BASE

CAP 理论

C:Consistency(强一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错性)

CAP的 3 进 2 —— 相当于(CA 2进1) P必占

  • CAP理论就是在分布式存储系统中,最多只能实现上面的两点。由于网络硬件肯定会出现延迟丢包等问题,分区容错性是我们必须需要实现的。
  • 只能在一致性 C 和 可用性 A 之间进行权衡,没有NoSQL系统能同时保证这三点。

  • CAP理论的核心:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
    • CA - 传统数据库。单点集群,满足一致性,可用性的系统,在可扩展性上不太强。
    • CP - Redis、Mongodb。 满足一致性,分区容忍必的系统,性能不是特别高。
    • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。(大多数网站架构的选择)

BASE

BASE 理论是对 CAP理论中的AP的一个扩展,通过牺牲强一致性来获得高可用性,当出现故障允许部分不可用,但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终要达到一致的状态。满足 BASE理论的事务,称之为 柔性事务

  • 基本可用 Basically Available
  • 软状态 Soft State
  • 最终一致 Eventually Consistent

分布式系统(distributed system)

  • 由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。

分布式: 不同的多台服务器上面部署不同的服务模块(工程),他们之间通过Rpc/Rmi之间通信和调用,对外提供服务和组内协作。

集群: 不同的多台服务器上面部署相同的服务模块,通过分布式调度软件进行统一的调度,对外提供服务和访问。


四、Redis

官方文档
中文文档
命令

  • Redis支持数据的持久化,可以将内存中的数据(支持异步)保持在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的 key-value 类型的数据,同时还提供 string,list,set,zset,hash 等数据结构的存储。
  • Redis支持数据的备份,即 master-slave 模式的数据备份。

getconf LONG_BIT 查看 (Linux是 32位还是64位)


安装 Redis

下载 centos7镜像
下载 Redis Linux版
操作命令 网速稍慢
操作命令 runoob

  • 1、下载获得 redis-x.x.x.tar.gz 后将它放入我们的Linux目录 /opt
  • 2、在 /opt 目录下,解压命令: tar -zxvf redis-x.x.x.tar.gz
  • 3、解压完成后出现文件夹:redis-x.x.x
  • 4、进入目录:cd redis-x.x.x
  • 5、在redis-x.x.x 目录下执行 make 命令
    • make命令出错: yum install gcc-c++ (安装gcc)
    • 然后运行 make distclean 之后再 make
      • (错误:jemalloc/jemalloc.h:没有那个文件或目录)
  • 6、make 完成后继续执行 make install

五、安装成功后 Hello Redis

  • 1、查看默认安装目录:cd /usr/local/bin —> ls -l
    • redis-benchmark: 性能测试工具,可以在自己本子运行,看看自己本子性能如何。 (服务启动起来后执行)
    • redis-check-aof: 修复有问题的AOF文件。
    • redis-check-dump: 修复有问题的dump.rdb文件。
    • redis-cli: 客户端,操作入口。
    • redis-sentinel: redis集群使用,监控。
    • redis-server: Redis服务器启动命令。
  • 2、cd / ->在根目录创建一个文件夹 myredis(任意)。
  • 3、cd /opt/redis-x.x.x —> 复制 Redis 配置文件。cp redis.conf /myredis
  • 4、修改 /myredis/redis.conf 下的配置文件。vim 编辑,将 daemonize no 改成 yes。
    • 查一下后台 Redis —> ps -ef|grep redis 有没有启动服务。 lsof -i:6379
  • 5、cd /usr/local/bin —>启动服务。redis-server /myredis/redis.conf 运行copy的conf文件。
  • 6、redis-cli -p 6379 (Hello Redis完成) ping pong
    • 查一下后台 Redis —> ps -ef|grep redis 有没有启动服务。 lsof -i:6379
    • set k1 a; get k1 —> “a”
  • 7、单实例关闭: redis-cli shutdown。exit(退出)
    • 多实例关闭,指定端口关闭:redis-cli -p 6379 shutdown

六、Redis 基础知识

  • Redis 是单进程
  • 默认16个数据库,类似数组下表从 0 开始,初始默认使用 0 号库。
  • select :命令切换数据库。SELECT 7 ——> 选择 6号库。
  • dbsize :查看当前数据库的key的数量。 keys *:查看所有的 key。(支持占位符 key k?)
  • flushdb:清空当前库。
  • flushall:通杀全部库。
  • 统一密码管理,16个库都是同样密码,要么都OK要么一个也连接不上。
  • Redis 索引都是从 0 开始。
  • 默认端口是6379 (9宫格键盘 merz)。

七、Redis的五大数据类型

Redis 键(key)

  • set key
  • get key

常用:

  • keys * 查看所有的 key。
  • exists key: 判断某个key是否存在。
  • move key db: 把当前 key 移动到指定 DB (当前库就没有了)。
  • expire key 秒钟: 为给定的key设置过期时间。
  • ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期。(过期移除内存系统)
  • type key: 查看你的key是什么类型。

1、String

string(字符串)

  • string 是 redis 最基本的类型,可以理解成与Memcached一样的类型,key:value。
  • string 类型是二进制安全的。意思是redis的string可以包含任何数据。比如序列化的对象..
  • string 一个redis中字符串 value 最多可以是512M。
  • 单值 单value


String 常用命令

  • set/get/del/append/strlen 设置、获取、删除、追加、获取长度。
  • incr/decr/incrby/decrby: 一定要是数字才能进行加减。

  • getrange: 获取指定区间范围内的值,截取字符串。从0到-1表示全部。
  • setrange: 设置指定区间范围内的值,插入字符串。格式是setrange key值 具体值。对指定位置 覆盖操作。

  • setex(set with expire)键秒值: 对该key设置存活时间。setex k1 10 v1
  • setnx(set if not exist): 如果key值存在返回0,如果不存在进行set设置值。setnx k1 v1

  • mset: 合并set。mset k1 v1 k2 v2 k3 v3
  • mget: 合并get。gset k1 k2 k3
  • msetnx: mset k1 v1 k2 v2 如果 k1 已经存在了。全部set失败!
  • getset(先get再set): 获取输出当前value值,然后set。

2、list(列表)

  • Redis 列表是简单的字符串列表,按照插入顺序排序。
    • 你可以添加一个元素列表的头部(左边)或者尾部(右边)。
  • 底层实际是个链表
  • 单值多value


list 常用命令:

  • lpush/rpush/lpop/rpop/lrange

  • lindex: 按照索引下标获得元素(从上到下)。LINDEX KEY_NAME 1

  • llen: 获取长度。 LLEN KEY_NAME

  • lrem: 删2个1。LREM KEY_NAME 2 1

  • ltrim: 开始index 结束index,截取指定范围的值后再赋值给key。LTRIM KEY_NAME 3 5

    • 👆ltrim:
  • rpoplpush: 源列表 目的列表 rpoplpush KEY_NAME01 KEY_NAME02

  • lset: 对下标的具体某个值设置值。lset KEY_NAME 1 x 把下标为1的值变为x。

  • linsert: 在list某个已有值的前后再添加具体值。LINSERT KEY_NAME before 1 Y 在值1的前面添加Y。

性能总结

  • list 是一个字符串链表,left、right 都可以插入添加。
  • 如果键不存在,创建新的链表。
  • 如果键已存在,新增内容。
  • 如果值全移除,对应的键也就消失了。
  • 链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很低。


3、set(集合)

  • Redis 的Set是string类型的无序集合,且不允许重复的成员。它是通过HashTable实现实现的。
  • 单值多value

set 常用命令

  • sadd: 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。sadd KEY_NAME v1
  • del: 删除集合。del KEY_NAME
  • smembers: 命令返回集合中的所有的成员。不存在的集合 set 被视为空集合。smembers KEY_NAME
  • srandmember: srandmember KEY_NAME value 从集合中随机出几个数。
  • sismember: 判断成员元素是否是集合的成员。sismember KEY_NAME 1
  • scard: 返回集合中元素的数量。scard KEY_NAME
  • srem: 删除集合中元素。srem KEY_NAME value
  • spop: 随机集合中一个value出栈。spop KEY_NAME
  • smove: 将指定成员 member 元素从 source 集合移动到目标集合。smove set01 set02 value
  • 数学集合类
    • 差集:sdiff 返回在KEY_NAME01里面,不在set02里面的 value。
      • sdiff KEY_NAME01 KEY_NAME02
    • 交集:sinter 返回在KEY_NAME01里面,也在set02里面的 value。
      • sinter KEY_NAME01 KEY_NAME02
    • 并集:sunion 返回去重后的集合。


4、hash(哈希)

  • Redis hash 是一个键值对集合。
  • Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象 类似Java里面的 Map< String,Object>
  • K/V模式不变,但V是一个键值对。

hash 常用命令:

  • hset/hget: hset KEY_NAME name zs hget KEY_NAME name
  • hmset/hmget: hmset KEY_NAME id 11 name zs hmget KEY_NAME id name
  • hgetall/hdel: hgetall KEY_NAME hdel KEY_NAME name
  • hexists: 用于查看哈希表的指定字段是否存在。hexists KEY_NAME id
  • hkeys/hvals: 用于获取哈希表中的所有域(key),返回哈希表所有域(value)的值。hkeys KEY_NAME hvals KEY_NAME
  • hincrby/hincrbyfloat: 用于为哈希表中的字段值加上指定增量值。hincrby user id
  • hlen: 获取哈希表中字段的数量。hlen KEY_NAME
  • hsetnx: 用于为哈希表中不存在的的字段赋值。hsetnx KEY_NAME key value v1


5、zset(sorted set:有序集合)

  • Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员
  • 不同的是每个元素都会关联一个整数或者双精度浮点数的分数。(比如游戏分数)
  • redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
  • 在set基础上,加一个score值。之前set是k1 v1 v2 v3,现在 zset是k1 score1 v1 score2 v2

zset 常用命令:命令 KEY_NAME value [WITHSCORES] [LIMIT offset count]

  • zadd: 用于将一个或多个成员元素及其分数值加入到有序集中。zadd KEY_NAME 60 v1 70 v2
  • zrange: ZRANGE KEY_NAME 0 -1 | ZRANGE KEY_NAME 0 -1 withscores
  • zrangebyscore:返回有序集合中指定分数区间的成员列表。 zrangebyscore KEY_NAME 开始score 结束score withscores
    • zrangebyscore zset 10 (50 不包含50。
    • zrangebyscore zset 10 (50 limit 2 2 limit 返回限制,limit 开始下标走多少步。
  • zrem: 用于移除有序集中的一个或多个成员,不存在的成员将被忽略。zrem KEY_NAME value
  • zcard: 用于计算集合中元素的数量。ZCARD KEY_NAME
  • zcount: 用于计算有序集合中指定分数区间的成员数量。ZCOUNT KEY_NAME min max
  • zrank: 作用是获得下标值。zrank KEY_NAME value
  • zscore: 对应值,获得分数。 zscore KEY_NAME value
  • zrevrank: 作用是逆序获得下标值。 zrevrank KEY_NAME value
  • zrevrange: 返回有序集中,指定区间内的成员。其中成员的位置按分数值递减(从大到小)来排列。ZREVRANGE key 0 -1 [WITHSCORES]
  • zrevrangebyscore: 返回有序集中指定分数区间内的所有的成员。有序集成员按分数值递减(从大到小)的次序排列。


八、redis.conf 配置文件

units单位

  • 配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit。对大小写不敏感
    1k => 1000 bytes
    1kb => 1024 bytes
    1m => 1000000 bytes
    1mb => 1024 * 1024 bytes
    1g => 1000000000 bytes
    1gb => 1024 * 1024 * 1024 bytes

1、INCLUDES 包含

  • 通过includes包含,redis.conf可以作为总闸,包含其他 other.conf。

2、GENERAL 通用

  • daemonize yes:后台运行
  • pidfile:当Redis以守护进程方式运行时,Redis默认会把pid写入 /var/run/redis.pid文件。
  • port:6379 端口号。
  • tcp-backlog:设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。
  • timeout:超时断开连接。(0 to disable)
  • bind:端口的绑定。
  • tcp-keepalive:单位为秒,设置为60s (0即为不检测)
  • loglevel:默认(verbose)。日志级别,debug、verbose、notice、warning。
  • logfile:指定日志文件名,为空则标准输出。
  • syslog-enabled:是否把系统日志输出到syslog中。默认关闭,开启后。
  • syslog-ident:日志鉴别,日志以 redis 开头。
  • syslog-facility:指定syslog设备,值可以是USER或 LOCAL0-LOCAL7。默认为 LOCAL 0
  • databases 16:默认为 16 个库。

3、SECURITY 安全

  • 访问密码的查看、设置和取消
  • 127.0.0.1:6379 > config get requirepass (获取密码)
  • 127.0.0.1:6379 > config set requirepass “123456” (设置密码) ping 不通了。
  • 127.0.0.1:6379 > auth 123456 (输入密码)

4、LIMITS 限制

  • maxclients 10000: redis同时可以与多少个客户端进行连接,默认情况为10000个客户端。

  • maxmemory: 设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。

  • maxmemory-policy:
    (1)volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
    (2)volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
    (3)volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
    (4)allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。
    (5)allkeys-random:从数据集中随机选择数据淘汰。
    (6)no-enviction(驱逐):永不过期,不进行移除。针对写操作,只是返回错误信息。禁止驱逐数据。
    (7)volatile-lfu: 使用具有过期集的键之间的近似LFU(数据过去被访问多次,那么将来被访问的频率也更高)将访问频率最少的键值对淘汰。
    (8)allkeys-lfu:从数据集中近似LFU(将访问频率最少的键值对淘汰。)选择数据淘汰。

  • maxmemory-samples: 设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小。

    • redis默认会检查这么多个key并选择其中LRU的那个。

5、SNAPSHOTTING 快照

Save the DB on disk; (默认) dump.rdb 文件

  • Save: save <seconds> <changes>
    • Redis 默认配置文件中提供了三个条件 触发 RDB 保存:
      • save 900 1 :——> 900s(15min)至少有 1 次 key 的改动。
      • save 300 10 :——> 300s(5min)至少有 10 次 key 的改动。
      • save 60 10000 :——> 60s(1min)至少有 10000 次 key 的改动。
      • 直接输入 save 或者是 bgsave 命令,即刻备份!
        • Save:save时只管保存,其它不管,全部阻塞。
        • BGSAVE:会在后台异步进行快照操作,快照同时还可以响应客户端请求。
          • 可以通过 lastsave 命令获取最后一次成功执行快照的时间。
    • 禁用 RDB: save “” (不设置任何save指令,或者给save传入一个空字符串参数)。
  • stop-writes-on-bgsave-error:yes

    • 如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制。
  • rdbcompression:

    • 对于存储到磁盘中的快照,可以设置是否进行压缩存储。
    • 如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。
  • rdbchecksum

    • 在存储快照后,还可以让redis使用CRC64算法来进行数据校验。
    • 但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
  • dbfilename dump.rdb

    • 指定本地数据库文件名,默认值为dump.rdb。
  • dir:config get dir (获取目录)


6、APPEND ONLY MODE 追加

By default Redis asynchronously dumps the dataset on disk; (默认) appendonly.aof 文件

  • appendonly no: 启动改为 yes
  • appendfilename: 默认名字 appendonly.aof
  • appendfsync everysec:
    • always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好。
    • everysec: 出厂默认推荐,异步操作,每秒记录 如果一秒内宕机,有数据丢失。
    • no
  • no-appendfsync-on-rewrite no: 重写时是否可以运用 Appendfsync,用默认no即可,保证数据安全性。
  • auto-aof-rewrite-percentage 100: (默认)设置重写的基准值
  • auto-aof-rewrite-min-size 64mb: (默认)设置重写的基准值

九、持久化 RDB 和 AOF

RDB(Redis DataBase)

👆 参考 Redis.conf SNAPSHOTTING 快照

RDB 保存的是 (默认) dump.rdb 文件:配置位置在 redis.conf 👆 参考 SNAPSHOTTING 快照

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存里。

  • Redis 会单独创建(fork)一个子进程进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了再用这个临时文件替换上次持久化好的文件
  • fork 的作用是复制一个与当前进程一样的进程。*
    • 新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程

恢复:

  • 将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。

优点:

  • 整个过程中,主进程是不进行任何IO操作的,确保了极高的性能 适合进行大规模数据的恢复。
  • 如果对于数据恢复的 完整性 不是非常敏感,那RDB方式要比AOF方式更加的高效。

缺点:

  • RDB的缺点是最后一次持久化后的数据可能丢失。
    • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。
  • fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。

修复: 先备份

  • 如果 RDB 文件有错误 在安装目录下,用 redis-check-rdb 对 RDB 文件进行修复,把错误的命令全部删除。
  • redis-check-rdb --fix dump.rdb

停止:

  • 动态停止所有RDB保存规则的方法:redis-cli config set save “”


AOF(Append Only File)

👆 参考 Redis.conf

APPEND ONLY MODE 追加

AOF 保存的是 appendonly.aof 文件:配置位置在 redis.conf 👆 参考 APPEND ONLY MODE 追加

以日志的形式来记录每个写操作:

  • 将 Redis 执行过的所有写指令默认异步记录下来(读操作不记录),只许追加文件但不可以改写文件。
  • redis启动之初会读取该文件重新构建数据,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF and RDB persistence can be enabled at the same time without problems.
dump.rdb 文件 和 appendonly.aof 文件 可以共存。优先找 AOF 文件


恢复:

  • 将有数据的aof文件复制一份保存到对应目录(config get dir),重启 redis 然后重新加载。

rewrite:

  • 因为重写机制,所以要经常 ——> free 看内存,df -h 看磁盘空间。
  • AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制。
  • 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof
  • 原理
    • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再 rewrite),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
  • 触发机制
    • Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。

优点:

  • 每次修改修改同步:appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好。
  • 不同步:appendfsync no 从不同步。
  • 每秒同步:appendfsync everysec 默认异步操作,每秒记录,如果一秒内宕机有数据丢失。

缺点:

  • 相同数据集的数据而言 aof 文件要远大于 rdb 文件,恢复速度慢于rdb。
  • aof 运行效率要慢于 rdb,每秒同步策略效率较好,不同步效率和rdb相同。

修复: 先备份

  • 如果 AOF 文件有错误 在安装目录下,用 redis-check-aof 对 AOF 文件进行修复,把错误的命令全部删除。
  • redis-check-aof --fix appendonly.aof


选择 (Which one) RDB/AOF

同时开启两种持久化方式

  • 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据。
  • AOF 文件保存的数据集要比 RDB 文件保存的数据集要 完整 RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。

  • 那要不要只使用AOF呢?
    • 因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

因为 RDB 文件只用作后备用途,建议只在Slave上持久化RDB文件,只要15分钟备份一次就够了,只保留 save 900 1 这条规则。

  • 如果开启 Enalbe AOF
    • 好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO。二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
  • 如果不开启 Enable AOF
    • 仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果 Master/Slave 同时down掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。


十、Redis 的事务

  • 一个队列中可以一次性,顺序性执行多个命令,本质是一组命令的集合。
  • 一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
  • 没有隔离级别的概念。不保证原子性,没有回滚。

命令

  • MULTI: 标记一个事务块的开始。OK
  • EXEC: 执行所有事务块内的命令。
  • DISCARD: 取消事务,放弃执行事务块内的所有命令。
  • UNWATCH: 取消 WATCH 命令对所有 key 的监视。一旦执行了exec,之前加的监控锁都会被取消掉了。
  • WATCH: 监视一个或多个 key;如果在事务执行之前这个 key 被其他命令所改动,事务将被打断。

Case1 正常执行: MULTI 一系列操作 EXEC
Case2 放弃事务: MULTI 一系列操作 DISCARD
Case3 全体连坐: MULTI 一系列操作中有错误操作(直接报错的命令) EXEC(全部失败)
Case4 冤头债主: MULTI 一系列操作中有错误操作(不直接报错的命令,比如 incr 字符串) EXEC (错误命令不执行)
Case4 watch监控: 先监控 在开启事务。watch key 、MULTI 一系列操作 EXEC 如果其他线程没有干扰则成功,反之失败。

  • 悲观锁/乐观锁/CAS(Check And Set)
    • 悲观锁: 行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
    • 乐观锁: 读写不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
    • CAS:
  • Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,整个事务队列都不会被执行。
  • 通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

十一、Redis 的复制(Master/Slave)

  • 主从复制 读写分离 容灾恢复。配从不配主
  • 主机数据更新后根据配置和策略,自动同步到备机的 master/slaver 机制,Master以写为主,Slave以读为主。
  • info replication 查看 role 角色。

常用三种配置方法


1、一主二仆

  • info replication: 返回主从复制的信息。主机才可以写,从机只能读。

    • 从库配置:slaveof 主库IP 主库端口 slaveof 127.0.0.1 6379
      • 如果 master shutdown 后,从库 role 还是 slave。 master 恢复上线,关系也恢复。
      • 每次 slave shutdown 与 master 断开之后,都需要重新连接,除非你配置进redis.conf文件。
        • 从库每次 slave shutdown 后 role 变为 master。
      • info replication 查看 role 角色。

2、薪火相传

  • 上一个 Slave 可以是下一个 Slave 的 Master,Slave 同样可以接收其他 slaves 的连接和同步请求。减轻主 Master 压力。
    • 中途变更转向:会清除之前的数据,重新建立拷贝最新的。
    • slaveof 新主库IP 新主库端口 slaveof 127.0.0.1 6379

3、反客为主

  • 当 主Master shutdown 以后, 使当前数据库停止与其他数据库的同步。
    • SLAVEOF no one 命令 使 Slave 转成 Master 数据库。

哨兵模式(反客为主自动版本)

  • 能够后台自动监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

原有体系 主仆关系
1、新建一个文件名为 sentinel.conf 空 配置文件。
2、配置 sentinel.conf文件。写入 sentinel monitor 被监控数据库名字 127.0.0.1 6379 1

  • 上面最后一个数字1,表示主机挂掉后 salve 投票看让谁接替成为主机,得票数多成为主机。
    3、启用 redis-sentinel /myredis/sentinel.conf 监控。
    4、如果 主Master shutdown 以后,salve 投票多的选为 新Master,而剩余 salve 转向 新Master。
    5、即便 主Master恢复,原来的体系已经建立,由于监控则变成新Master 的 salve
    6、一组sentinel能同时监控多个Master。

Redis复制缺点

  • 由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,
  • 所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

十一、Redis的Java客户端 Jedis

测试

  • jar 包
    • commons-pool-1.6.jar
    • jedis-2.1.0.jar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Set;
import redis.clients.jedis.Jedis;

public class RedisDemo {

public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.98.129",6379);
System.out.println(jedis.ping());

jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
jedis.set("k4", "v4");

String string = jedis.get("k1");
System.out.println(string);

Set<String> keys = jedis.keys("*");
for (String string2 : keys) {
System.out.println(string2);
}

Long dbSize = jedis.dbSize();
System.out.println(dbSize);
}
}

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class RedisDemoTx {

public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.98.129",6379);
System.out.println(jedis.ping());

Transaction transaction = jedis.multi();

transaction.set("k1", "v1");
transaction.set("k2", "v2");
transaction.set("k3", "v3");
transaction.set("k4", "v4");

//transaction.exec();
transaction.discard();
}
}

watch … 监控

主从复制

主写 从读

1
2
3
4
5
6
jedis_S.slaveof("127.0.0.1",6379);
jedis_M.set("K1","V1");


//稍慢执行
jedis_S.("K1");

Redis Pool

单例模式

  • 数据库连接池的设计一般采用单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolUtil {
private static volatile JedisPool jedisPool = null;

private JedisPoolUtil() {
}

public static JedisPool getJedisPoolInstance() {
if (null == jedisPool) {
synchronized (JedisPoolUtil.class) {
if (null == jedisPool) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxActive(1000);
poolConfig.setMaxIdle(32);
poolConfig.setMaxWait(100 * 1000);
poolConfig.setTestOnBorrow(true);

jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
}
}
}
return jedisPool;
}

public static void release(JedisPool jedisPool, Jedis jedis) {
if (null != jedis) {
jedisPool.returnResourceObject(jedis);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class TestPool {

public static void main(String[] args) {
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
JedisPool jedisPool2 = JedisPoolUtil.getJedisPoolInstance();

System.out.println(jedisPool == jedisPool2);

Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.set("aa","bb");
} catch (Exception e) {
e.printStackTrace();
}finally{
JedisPoolUtil.release(jedisPool, jedis);
}
}
}


Redis的发布订阅

  • 进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

感谢阅读

参数说明

redis.conf 配置项说明如下:
1、 Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2、 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
4、 绑定的主机地址
bind 127.0.0.1
5、当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
7、日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
10、指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
12、指定本地数据库存放目录
dir ./
13、设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof
14、当master服务设置了密码保护时,slav服务连接master的密码
masterauth
15、设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
requirepass foobared
16、设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17、指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory
18、指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
20、指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
21、指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
22、虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23、将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
24、Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
25、设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
26、设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
27、设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
28、指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29、指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
30、指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !