最近开始拜读付磊的redis开发与运维一书,作为起点正式地开始接触redis,这篇博客记录一下笔记
第一章
1. redis使用场景
1) 缓存
在大型网站中使用,之前在公司也一直有听到使用redis作为缓存来降低后端数据源的压力,不过购物车和下单线业务都不需要缓存所以在工作中一直都没有用上,残念
2)排行榜系统
应该说是按照复杂维度计算出的排行榜,大学的时候做过数独游戏的排行榜,当时做的demo为了应付老师实在是太简陋,如果更新到现在应该会用上redis吧(笑)
3)计数器应用
第一个反应是小破站每个视频的播放量,公司app上商品的浏览数,保证数据的实时性
4)社交网络
社交网络访问量确实大,微博都常崩
5)消息队列系统
消息队列拥有业务解耦/非实时业务削峰/异步处理/日志处理特性
2. 安装redis
书上推荐的方法是源码的方式安装3.0.7版本:
wget http://download.redis.io/releases/redis-3.0.7.tar.gz
tar xzf redis-3.0.7.tar.gz
ln -s redis-3.0.7 redis
cd redis
make
make test
make install
注意:
make编译前要先安装好g++
3. 启动服务
启动服务端:
redis-server
打开后发现不能操作了,应该要放到后台运行,找到redis.conf 并修改 daemonize no 为 daemonize yes ,这样就可以默认启动就后台运行
~/redis-3.0.7$ redis-server redis.conf
启动客户端(两种方式):
zinan_ji@Jzn:~/redis-3.0.7$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
zinan_ji@Jzn:~/redis-3.0.7$ redis-cli -h 127.0.0.1 -p 6379 get hello
"world"
4. 停止服务
zinan_ji@Jzn:~/redis-3.0.7$ redis-cli shutdown
第二章.API的理解和使用
1.预备
1.1 全局命令
1) 查看所有命令 keys *
插入键值对用set set hello world hello是key,world是value
keys *会将所有的键输出
2)键总数dbsize
插入列表类型的键值对(值为多个元素组成)
127.0.0.1:6379> rpush mylist a b c d e f g
(integer) 7
dbsize会返回当前数据库键的总数。
127.0.0.1:6379> dbsize
(integer) 4
注意:dbsize直接获取的是一个总数变量,所以时间复杂度为O(1),keys会遍历所有键,所以复杂度是O(n),线上环境应该禁止使用,如果redis存储了大量键的时候。
3)检查键是否存在
exists keyname
存在返回1,不存在返回 0
127.0.0.1:6379> exists hello
(integer) 1
127.0.0.1:6379> exists helloworld
(integer) 0
4) 删除键 del key[key...]
del命令是通用命令什么数据结构类型都能删
成功返回删除key的个数,删除不存在的key则返回0
127.0.0.1:6379> del java
(integer) 1
127.0.0.1:6379> del mylist
(integer) 1
127.0.0.1:6379> exists java
(integer) 0
127.0.0.1:6379> exists mylist
(integer) 0
127.0.0.1:6379> del java
(integer) 1
127.0.0.1:6379> del mylist
(integer) 1
127.0.0.1:6379> exists java
(integer) 0
127.0.0.1:6379> exists mylist
(integer) 0
5) 键过期
expire key seconds 对键添加过期时间
ttl key 可以观察键的剩余过期时间(返回值-1是键没有设置过期时间,-2是键不存在)
6) 键的数据结构类型 type key
键不存在返回none
1.2 redis 对外支持stirng /list/set/zset/hash五种数据结构。每种都有自己底层的内部编码实现。
可以通过object encoding查询内部编码
每种数据结构都有两种以上的内部编码实现,redis会在合适的场景选择合适的内部编码。
好处: 方便改建和适应不同场景。
1.3 单线程架构
单线程架构和I/O多路复用模型来实现高性能的内存数据库服务
单线程避免了并发问题,客户端的命令都会进入到队列中逐个执行,但是每次只能有一个命令被执行
为什么还这么快?
1. 纯内存访问
2. 非阻塞I/O
redis 使用epoll作为I/O多路复用技术的实现,再加上redis自身的事件处理模型将epoll中的连接/读写/关闭都转换为事件,不会在网络I/O上浪费过多时间
3. 避免了线程切换和竞态产生的消耗
单线程还有好处:
1. 简化数据结构和算法实现
2. 避免了线程切换和竞态产生的消耗
但是有问题就是,对每个命令的执行时间是有要求的,命令过长会导致其他命令阻塞,所以redis是面向快速执行场景的数据库。
2 字符串
最基础的数据结构,value可以是字符串/数字/二进制。但是值最大不能超过512MB
2.1 命令
1)设置值set key value [ex seconds] [px milliseconds] [nx|xx]
nx:键必须不存在,用作添加
xx:值必须存在,用作更新
setex key seconds value
setnx key value
setnx是作为分布式锁的一种实现方式。单线程下多个客户端执行setnx只有一个客户端能设置成功
2)获取值get key
3)批量设置值 mset key value key value...
4) 批量获取值 mget key key...
5) 计数 incr key 用于对值自增
还有自减decr/自增指定数字incrby/自减指定数字decrby/自增浮点数incrbyfloat
6)不常用命令
append key value 字符串末尾追加值
strlen key 字符串值长度
getset key value 设置并返回原值
setrange key offset value 设置指定位置的字符
getrange key start end 获取部分字符串
2.2 内部编码
int/embstr/raw分别为8字节长整型,小于等于39字节字符串,大于39字节的字符串
2.3 使用场景
1)缓存
2)计数
3)共享session
4)限速(限制每分钟获取短信验证码的频率,或者限制ip访问次数
记录手机号和incr计数实现
3 哈希
key,value={{field,value},{field,value}...}
3.1 命令
设值 | hset key field value |
获取值 | hget key field |
删除field | hdel key field [field...] |
计算field个数 | hlen key |
批量设置或获取field-value | hmset key field value [field value...] hmget key field [field...] |
判断field是否存在 | hexists key field |
获取所有field | hkeys key |
获取所有value | hvals key |
获取所有的field-value | hgetall key/ hscan key |
自增 | hincrby key field hincrbyfloat key field |
计算value的字符串长度 | hstrlen key field |
3.2 内部编码
ziplist:哈希类型元素个数小于hash-max-ziplist-entries配置(默认512)个数,同时值小于hash-max-ziplist-value配置字节时使用,结构更紧凑实现多个元素连续存储.
hashtable:无法满足ziplist条件时使用作为内部实现
3.3 使用场景
缓存用户信息,即呈现关系型数据库的行信息
但与关系型数据库的不同:
哈希类型是稀疏的,而关系型数据库是完全结构化的。
关系型数据库可以做复杂关系查询,redis来模拟维护成本太高了。
缓存用户信息三种方法:
1)原生字符串类型
2)序列化字符串类型
3)哈希类型
各自优缺点(待补充)
4 列表
存储字符串的双端队列,有序可重复
4.1 命令
向右插入元素 | rpush key value[value...] |
从左插入元素 | lpush key value[value...] |
某个元素前后插入 | linsert key before/after pivot value |
获取指定范围内的元素列表 | lrange key start end |
获取列表指定索引下标的元素 | lindex key index |
获取列表长度 | llen key |
从列表左侧弹出元素 | lpop key |
从列表右侧弹出元素 | rpop key |
删除指定元素 | lrem key count value count >0 删除从左到右count个元素 count <0 删除从右到左|count|个元素 count =0 删除所有 |
按照索引范围修剪列表 | ltrim key start end |
修改指定索引下标的元素 | lset key index newValue |
阻塞式弹出 | blpop key [key...] timeout brpop key [key...] timeout |
4.2 内部编码
ziplist:列表的元素个数小于list-max-ziplist-entries配置(默认512)个数,同时列表中每的元素值小于lisst-max-ziplist-value配置(默认64)字节时使用,减少内存的使用
linkedlist:无法满足ziplist条件时使用作为内部实现
redis3.2提供了一个quicklist的内部编码,用linkedlist存储ziplist的节点
4.3 使用场景
消息队列lpush+brpop
生产者客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop阻塞式抢占列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性
文章列表。
5 集合
5.1 命令
集合内操作: | |
添加元素 | sadd key element[element...] |
删除元素 | srem key element[element...] |
计算元素个数 | scard key |
判断元素是否在集合中 | sismember key element |
随机从集合返回指定个数元素 | srandmember key [count] |
从集合随机弹出元素 | spop key |
获取所有元素 | smembers key |
集合间操作: | |
求多个集合的交集 | sinter key [key...] |
求多个集合的并集 | sunion key [key...] |
求多个集合的差集 | sdiff key [key...] |
将交集/并集/差集的结果保存 | sinterstore destination key[key...] sunionstore destination key[key...] sdiffstore destination key[key...] |
5.2 内部编码
intset:集合中的元素都是整数且元素个数小于set-max-inset-entries配置(默认512)个数,redis会选用它来减少内存的使用
hashtable:无法满足intset条件时使用作为内部实现
5.3 使用场景
标签:sadd
用户和标签的关系维护应该放一个事务中进行
生成随机数:spop/srandmember
社交需求(social graph):sadd+sinter
6 有序集合
有序集合给每个元素设置一个分值(score)来作为排序依据。不允许集合元素重复,但是score可以重复。可以做排行榜系统/社交等
6.1 命令
集合内操作: | |
添加成员 | zadd key score member[score member...] 有nx/xx/ch/incr四个选项 |
计算成员个数 | zcard key |
计算某个成员分数 | zcount key member |
计算成员排名 | zrank key member(从低到高) zrevrank key member(从高到低) |
删除成员 | srandmember key [count] |
增加成员分数 | zincrby key increment member |
返回指定排名范围的成员 | zrange key start end [withscores] zrevrange key start end [withscores] |
返回指定分数范围的成员 | zrangebyscore key min max [withscores] [limit offset count] zrevrangebyscore key min max [withscores] [limit offset count] |
返回指定分数范围的成员个数 |
zcount key min max |
删除指定排名内的升序元素 | zremrangebyrank key start end |
删除指定分数范围的成员 | zremrangebyscore key min max |
集合间操作: |
|
交集 | zinterstore destinattion numkeys [key...] [weights weight [weight...]] [aggregate sum|min|max] |
并集 | zunionstore destinattion numkeys [key...] [weights weight [weight...]] [aggregate sum|min|max] |
交集 | zinterstore destinattion numkeys [key...] [weights weight [weight...]] [aggregate sum|min|max] |
6.2 内部编码
ziplist:有序集合的元素个数小于zset-max-ziplist-entries配置(默认128)个数,同时列表中每的元素值小于zset-max-ziplist-value配置(默认64)字节时使用,减少内存的使用
skiplisst:无法满足ziplist条件时使用作为内部实现
6.3 使用场景
排行榜系统:
添加用户赞数:zadd zincrby
取消用户赞数:zrem
展示获赞最多的用户:zrevrangebyrank
展示用户信息以及用户分数:zscore zrank