李子骅老师《Redis入门指南(第二版)》学习笔记
Redis是一个开源的、高性能的、基于键值对的缓存与存储系统。
redis和mysql的区别
redis和Mencached的区别
都是非关系型、基于内存的存储系统。
redis支持字符串,散列,列表,集合,有序集,位图,超级日志和空间索引;而Memcached支持字符串和整数。
redis是单线程模型,Memcached支持多线程,理论在多核服务器上Memcached的性能更高一些。
Memcached不支持复制,redis支持主从复制。
Redis字符串是最基本的Redis数据类型,表示字节序列
| 命令 | 含义 |
| set key value | 赋值 |
| get key | 取值 |
| incr key | 递增(+1) |
| incrby key value | 增加指定整数 |
| decrby key value | 减少指定整数 |
| incrbyfloat key value | 增加指定浮点数 |
| append key value | 向尾部增加值 |
| strlen key | 获取字符串长度 |
| mset key value [ key2 value2 ...] | 同时设置多个键值 |
| mget key value [ key2 value2 ...] | 同时获得多个键值 |
Redis列表是按插入顺序排序的字符串列表。 内部是使用双向链表实现的,所以在列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快,代价是通过索引访问元素比较慢。

| 命令 | 含义 |
| lpush key value [ value ...] | 向列表左边增加元素 |
| rpush key value [ value ...] | 向列表右边增加元素 |
| lpop key | 从列表左边弹出元素 |
| rpop key | 从列表右边弹出元素 |
| llen key | 获取列表中元素个数 |
| lrange key start stop | 获得列表片段(包含两端) |
| lrem key count value | 删除列表中指定值 |
| lindex key index | 获得指定索引的元素值 |
| lset key index value | 设置指定索引的元素值 |
| ltrim key start stop | 删除指定范围外的所有元素 |
| linsert key before/after pivot value | 向列表中插入元素 |
集合是唯一字符串的无序集合,其行为类似于您喜爱的编程语言(例如,Java HashSet、Python集合等)中的集合。 由于集合类型在redis内部是使用值为空的散列表实现的所以这些操作的时间复杂度都是O(1)
集合与列表的区别
| 集合 | 列表 | |
| 存储内容 | 至多223-1个字符串 | 至多223-1个字符串 |
| 有序性 | 否 | 是 |
| 唯一性 | 是 | 否 |
| 命令 | 含义 |
| sadd key member [ member ...] | 增加元素 |
| srem key member [ member ...] | 删除元素 |
| smembers key | 获得集合中的所有元素 |
| sismember key value | 判断元素是否在集合中 |
| sdiff key [ key ...] | 集合差集运算 |
| sunion key [ key ...] | 集合并集运算 |
| sinter key [ key ...] | 集合交集运算 |
| scard key | 获得集合中元素个数 |
| srandmember key | 随机获得集合中元素 |
| spop key | 从集合中弹出一个元素 |
散列的键值也是一种字典结构,存储了字段和字段值的映射。 类似于Java的hashmap

| 命令 | 含义 |
| hset key field value | 赋值 |
| hget key field value | 取值 |
| hmset / hmget key field value [field1 value1 ...] | 赋值/取值多个字段 |
| hexists key field | 判断字段是否存在 |
| hsetnx key field value | 字段不存在时赋值 |
| hincrby key field num | 增加数字 |
| hdel key field [ field1 ...] | 删除字段 |
| hkeys key | 只获取字段名 |
| hvals key | 只获取字段值 |
| hlen key | 获得字段数量 |
| hgetall key | 获取字段和字段值 |
有序集合是唯一字符串的集合,通过每个字符串的关联分数维持顺序
| 命令 | 含义 |
| zadd key score member [ score member ...] | 增加元素 |
| zscore key member | 获得元素分数 |
| zrange / zrevrange key start stop [withscores] | 获得排名在某个范围内的元素列表 |
| zrangebyscore key min max [withscores] [limit offset count] | 获得指定分数范围的元素 |
| zincrby key increment member | 增加某个元素的分数 |
| zcard key | 获得集合中元素的数量 |
| zcount key min max | 获得指定分数范围内的元素个数 |
| zrem key member [ member ...] | 删除一个或多个元素 |
| zremrangebyrank key start stop | 按照排名范围删除元素 |
| zremrangebyscore key min max | 按照分数范围删除元素 |
| zrank/zrevrank key member | 获得元素的排名 |
Redis流是一种数据结构,其作用类似于只追加日志。流有助于按照事件发生的顺序记录事件,然后将其联合起来进行处理
Redis地理空间索引对于查找给定地理半径或边界框内的位置非常有用
Redis位图允许您对字符串执行逐位操作。
Redis位字段有效地编码字符串值中的多个计数器。位字段提供原子获取、设置和增量操作,并支持不同的溢出策略
Redis HyperLogLog数据结构提供了大集合基数(即元素数量)的概率估计
redis中的事务(transaction),是一组命令的集合。
使用multi开始一个事务,exec执行事务

错误处理:
1) 语法错误,命令不存在 / 命令参数个数不对 : 执行exec命令后,连正确的命令也不会执行

2)运行错误,命令执行时出现的错误:事务里其他的命令都会执行,包括出错命令之后的命令


设置过期时间: expire key 秒
取消过期时间:persist key ; 使用set/getset为键赋值也会清除键的过期时间
只对键值进行操作的命令(incr、lpush、hset、zrem)不会影响过期时间
当服务器内存有限时,如果大量的使用缓存键且过期时间设置的过长就会导致redis占满内存;另一方面如果为了防止redis占用内存过大而将缓存键的过期时间设的太短,就可能导致缓存命中率过低并且大量内存白白的闲置,为此可以限制redis的最大使用内存。
设置方法:修改配置文件的maxmemory参数,限制redis最大可用内存大小(单位是字节),当超出了这个限制时redis会根据maxmemory-police参数指定的策略来删除不需要的键直到redis占用的内存小于指定内存
sort
排序列表

排序集合


排序有序集合: 会忽略元素的分数,只针对元素自身的值进行排序

by
by参数的语法为by参考键。其中参考键可以是字符串类型或者是散列类型键的某个字段(表示为 键名->字段名),如果提供了by参数,sort命令将不再依据元素自身的值进行排序,而是对每个元素使用元素的值,替换参考键中的第一个* 并获取其值,然后根据该值对元素进行排序
散列类型的键:

字符串类型的键:


get
get参数可以有多个,不影响排序,支持字符串类型和散列类型的键。 他的作用是使sort命令返回的结果不再是元素自身的值,而是get参数重指定的键值


get # 返回元素本身的值
store
保存排序结果

任务队列
任务队列顾名思义就是“传递任务的队列”,与任务队列交互的实体有两类,一类是生产者,一类是消费者。生产者会将需要处理的任务放入任务队列中,而消费者不断的从任务队列中读入任务信息并执行
好处:松耦合、易于扩展
利用BRPOP命令,会一直阻塞链接,直到有新元素加入。
格式: brpop 键名 超时时间 。 超市时间单位时秒,为0表示不限制等待时间

如上图,实例A处于阻塞状态, 此时在实例B中像queue加入一个元素

A马上返回了结果

优先级队列
格式:blpop queue:1 queue:2 queue:3 0
解释:如果所有键都没有元素则阻塞;
如果其中有一个键有元素则弹出;
如果多个键有元素则按照从左往右的顺序取第一个键中的一个元素。


发布/订阅模式
发布/订阅模式同样可以实现进程间的消息传递
发布者发消息: publish channel message , 返回值表示收到此条消息的订阅者数量,没有人订阅,则返回0
订阅消息: subscribe channel [ channel ...]
取消订阅消息: unsubscribe channel [ channel ...]
实例A进入订阅状态:

实例B发布消息:

客户端和redis使用TCP协议链接,执行多个命令时每条都需要等待上一条命令执行完才能执行。
redis对管道(pipelining)提供了支持,通过管道可以一次性发送多条命令,并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时可以将这组命令一起通过管道发出。

我们希望redis能将数据从内存中以某种形式同步到硬盘中,使得重启之后可以根据硬盘中的记录恢复数据,这一过程就是持久化。
redis支持两种方式的持久化,一种是RDB方式,另一种是AOF方式
ADB
ADB的方式是通过快照完成的,当符合一定条件时Redis会自动将内存中的所有数据生成一份副本并存储在硬盘上。redis会在以下几种情况对数据进行快照:
1) 根据配置规则(redis.conf)进行自动快照

2) 用户执行SAVE或BGSAVE命令
执行save时,同步进行快照操作,执行过程会阻塞所有来自客户端的请求
执行bgsave时,异步进行快照操作,使用lastsave获取最近一次快照的时间
3) 执行FLUSHALL命令
会清空所有数据
4) 执行复制(replication)时
设置了主从模式时,redis会在复制初始化时进行自动快照
快照原理:
优点:
在进行快照的过程中不回修改rdb文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候rdb文件都是完整的。rdb文件是经过压缩的二进制格式,占用空间小传输快
缺点:
一旦redis异常退出,就会丢失最后一次快照以后更改的所有数据
AOF
默认是关闭的,开启AOF持久化后每执行一条会更改redis中数据的命令,redis就会将该命令写入硬盘中的AOF文件,默认文件名是appendonly.aof

缺点:
在启动时redis会逐个执行AOF文件中的命令将硬盘中的数据载入到内存中,载入的速度相对rdb会慢一些
为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务,为此redis提供了复制的功能。
主数据库:可以进行读写操作,当写操作导致数据变化时自动将数据同步给从数据库
从数据库:只读,接受主数据库同步过来的数据,一个从数据库只能有一个主数据库,从数据库自己也可作为主数据库
启动一个主redis数据库 redis-server
启动一个从redis数据库 redis-server --port 6380 --slaveof 127.0.0.1 6379

redis实现复制的过程:
通过复制可以实现读写分离,以提高服务器的负载能力。
哨兵的作用
哨兵是一个独立的进程,可以使用多个哨兵进行监控任务以保证系统足够稳健,哨兵不仅会同时监视主数据库和从数据库,哨兵之间也会相互监督。
实现原理
1)一个哨兵进程启动时会读取配置文件的内容,找到需要监控的主数据库,一个哨兵可同时监视多个redis主从系统
sentinel monitor master-name ip redis-port quonum
2)和主数据库建立连接后会定时执行下面的操作