redis基础
记录学习redis的一点基础东西
基础
介绍
Redis是一个开源的高性能的key-value存储系统,主要解决海量数据和高并发需求。具有以下特点:
- Redis是非关系型(NoSQL)数据库。本质上也是数据库,但MySQL关系型数据库存储时必须定义数据词典,而Redis则不需要,它是作为关系型数据库的补充。
- Redis是**
基于内存**的,所以比基于硬盘的MySQL要快很多,但非常吃内存 - Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
- Redis提供String,list,set,sorted set,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
- 内部采用单线程机制工作。
- Redis是远程的,有客户端和服务端,我们一般说的是服务端。
Redis优势:
1、性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
2、丰富的数据类型 – Redis支持二进制案例的 String, List, Hash, Set 及 Sorted Set 数据类型操作。
3、原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
4、丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性
Redis的应用:
- 为热点数据加速查询
- 任务队列:如秒杀,抢票
- 即时信息查询:如排行榜,在线人数信息
- 时效信息:如验证码,投票信息
- 分布式数据共享
命令行基本操作:
set/get、clear、help
数据类型
string
简单的 key-value 键值对,value 不仅可以是 String,也可以是数字。String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr, decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。
常用指令:
- SET key value //设置key=value
- GET key //获得key对应的value
- DEL key //删除
- mset key1 value1 key2 value2 //同时设置多个键值
- mget key1 key2 //同时获得多个值
- strlen key //获取key对应的value的字符串长度
- append key value //在key后追加上value,如果key不存在,就创建该键值对
- incr key //如果key对应的value是个数,就加一,否则报错
- incrby key increment //value增加increment
- Incrbyfloat key increment //用于增加小数
- decr key //value减一
- decrby key increment
hash
Key对应的Value内部实际就是一个HashMap,Value就是多对field和value,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,当成员数量增大时会自动转成真正的HashMap
使用hgetall时如果field过多,速度会非常慢
最多含有2^32 - 1个键值对
操作
- hset key field value //设置一个key中的hash表
- hget key field
- hgetall key
- hdel key field1 [field2] //删除key对应的value中的一个(或多个)键值对
- hmset、hmget //对应的一次添加、删除多个键值对
- hlen key //获取key对应的hash中字段数量
- hexists key field //key中是否存在field字段
- hkeys key、hvals key //获取key对应的hash表的所有键,获取key对应的hash表的所以值
- hincrby key field increment //增加指定字段的值
list
是简单的字符串列表,可以类比到C++中的std::list,简单的说就是一个链表或者说是一个队列。可以从头部或尾部向Redis列表添加元素。列表的最大长度为2^32 - 1,也即每个列表支持超过40亿个元素。
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。获取全部列表时可以将结束索引设置为-1
应用:
Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表、粉丝列表等都可以用Redis的list结构来实现,再比如有的应用使用Redis的list类型实现一个简单的轻量级消息队列,生产者push,消费者pop/bpop。
操作:
- lpush key value1 [value2]、rpush key value //左插入,右插入
- lrange key start stop //获取一段范围内的
- lindex key index //获取指定位置的
- llen key //获取key对应的列表的长度
- lpop key、rpop key //删除
- blpop key1 [key2] value timeout //阻塞删除,在规定时间内删除,即在规定时间内只要队列中放入值就可以删除
- lrem key count value //移除key对应的列表中count个value值
set
可以理解为一堆值不重复的列表,类似数学领域中的集合概念,且Redis也提供了针对集合的求交集、并集、差集等操作。
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
操作:
- sadd key member1 [member2] //添加数据
- smembers key //获取key对应的set
- srem key member1 [member2] //删除
- scard key //获取key对应的set中元素的个数
- sismember key member //判断member是否是key对应的set中的值
- srandmember key count //随机获取key对应的set中的count个元素
- spop key //随机获取元素并删除
- sinter key1 [key2]、sinterstore destination key1 [key2] //求交集(并存到destinatio中)
- sunion key1 [key2]、sinterstore destination key1 [key2] //求并集(并存到destinatio中)
- sdiff key1 [key2]、sinterstore destination key1 [key2] //求差集(并存到destinatio中)
- smove source destination member //将member从一个set移到另一个
sorted_set
有序集合类似Redis集合,不同的是增加了一个功能,即集合是有序的。一个有序集合的每个成员带有分数,用于进行排序。
Redis有序集合添加、删除和测试的时间复杂度均为O(1)(固定时间,无论里面包含的元素集合的数量)。列表的最大长度为2^32- 1元素(4294967295,超过40亿每个元素的集合)。
Redis sorted set的内部使用**HashMap和跳跃表(SkipList)**来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
使用场景:
又比如用户的积分排行榜需求就可以通过有序集合实现。还有上面介绍的使用List实现轻量级的消息队列,其实也可以通过Sorted Set实现有优先级或按权重的队列。
操作:
- zadd key score1 member1 [score2 member2] //添加数据
- zrange key start stop [withscores] //获取正序排列中指定个数的值
- zrevrange key start stop [withscores] //获取反序排列中指定个数的值
- zrem key member1 [member2] //删除key对应的有序set中指定的member
- zrangebyscore key min max [withscores] [limit] //获取正序set中指定范围的
- zremrangebyrank key start stop //删除指定个数的值
- zremrangebyscore key min max //删除指定范围的值
- zcard key //获取key对应的sorted_set中的个数
- zcount key min max //获取指定范围中的个数
- zinterstore destination numkeys key1 [key2] //获得并存储numkeys个set的交集
- zunionstore destination numkeys key1 [key2] //获得并存储numkeys个set的并集
- zrank key member、zrevrank key member //获取member在集合中的排名
- zscore key member //获取key对应的set中member的值
- zincrby key increment member //member的score增加increment
通用指令
key通用指令
key是一个字符串,查询所用到的键,对于key可以设计关于自身状态、有效性、快速查询相关指令
1 | del key //删除key |
数据库通用指令
redis为每个服务提供16个数据库,从0到15,每个数据库之间相互独立
1 | select index // 切换数据库 |
Jedis
Jedis是redis的java版本的客户端实现,提供了连接池管理。一般不直接使用jedis,而是在其上在封装一层,作为业务的使用。
jedis里方法和redis指令一样的
1 | public JedisPool(GenericObjectPoolConfig poolConfig, |
高级
持久化
什么是持久化
Redis 的数据 全部存储 在 内存 中,如果 突然宕机,数据就会全部丢失,因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的 持久化机制,它会将内存中的数据库状态 保存到磁盘 中:
- 客户端向数据库 发送写命令 (数据在客户端的内存中)
- 数据库 接收 到客户端的 写请求 (数据在服务器的内存中)
- 数据库 调用系统 API 将数据写入磁盘 (数据在内核缓冲区中)
- 操作系统将 写缓冲区 传输到 磁盘控控制器 (数据在磁盘缓存中)
- 操作系统的磁盘控制器将数据 写入实际的物理媒介 中 (数据在磁盘中)
一般有两种持久化方法,保存当前数据状态——快照和保存操作过程——日志:
RDB
快照,直接将当前数据库中数据全部保存下来放在一个.rdb文件中,有两种执行方式:
- 通过save指令手动保存,可以在配置文件中设置保存路径等参数。因为redis是单线程的,在save时就可能造成阻塞
- 通过bgsave指令手动保存,该指令是在后台fork一个子进程来创建rdb文件
在配置文件中通过save second changes 来自动执行bgsave指令,在规定时间内达到规定的变化次数就进行保存。
注:save只有在数据真正发送变化时才会进行保存,比如get指令没有改变数据,所以就不计入changes中。
在重新启动redis时通过配置文件就会自动恢复数据了。
优点
- rdb是二进制文件,所以保存的文件很小
- 保存的是某个时间的快照,所以适合数据的备份
- 恢复数据速度较快
缺点
- 不能实时持久化,比如断电时就可能会丢失数据
- bgsave要fork子进程,牺牲性能
- redis不同版本的rdb文件格式不统一,可能会出现不同版本数据不兼容的问题
AOF
append only file,以独立日志方式记录每次写命令,服务启动时执行所有aof文件中的指令来恢复数据,实时性较强,是目前redis持久化主流方式。
过程:
同步到AOF中时有三种条件:
- always:每执行一个写入操作保存一次(零误差,性能低)
- everysec:每秒保存一次(误差较小,性能高)
- no:由系统控制保存周期,不可控
需要在配置文件中设置:
1 | appendonly yes|no //打开或关闭AOF |
AOF重写
随着写命令越来越多,文件越来越大,redis通过重写来压缩文件体积,同时也可以加快持久化速度和恢复速度。重写就是将对一个数据的若干操作指令转化成一个最终操作指令
重写规则:
- 忽略无效的命令,只保留最终数据写入的命令
- 进程内已超时的数据不再写入
- 对同一数据的多条写命令合并为一条命令
配置文件:
1 | auto-aof-rewrite-min-size size //当前缓冲的数据量达到规定值时重写 |
重写工作原理:
RDB和AOF对比:
| RDB | AOF | |
|---|---|---|
| 占用储存空间 | 小(压缩数据) | 大(保存指令) |
| 存储速度 | 慢 | 快 |
| 恢复速度 | 快 | 慢 |
| 数据安全性 | 会丢失数据 | 以策略决定 |
| 资源消耗 | 高 | 低 |
| 启动优先级 | 低 | 高 |
Redis 4.0 混合持久化
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是 自持久化开始到持久化结束 的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小,于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
事务
一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。 事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为流水线,它可以减少客户端与服务器之间的网络通信次数从而提升性能。 Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。用discard可以取消事务
事务流程:
锁
watch key1 [key2] 对key添加一个监视锁,在执行exec前如果key发生变化,则终止事务。必须在事务开始前监视
unwatch 取消对所有key的监视
setnx lock-key value 在操作一个key前先把它锁住
del lock-key 删除这个锁
expire lock-key second 为锁增加一个期限,到期后自动解锁
删除策略
过期删除
针对的是设置了有效期的数据,通过ttl指令可以得到其状态:
- xxxx: 表示还能存活xxxx时间
- -1:表示永久有效
- -2:表示已经过期或者不存在或者被删除了
redis服务器中使用了redisDb数据结构,其中有一个过期字典(expires): 保存数据库中所有键的过期时间,过期时间用UNIX时间戳表示,且值为long long整数
有三种删除策略:
- 定时删除:创建一个定时器,当过期时,定时器立刻对过期的key删除(时间换空间)
- 惰性删除:过期后不立刻进行删除,而是当下次访问该key时再删除(空间换时间)
- 定期删除:周期性轮询访问redis数据库中的数据的有效性,随机抽取删除
RDB和AOF中对过期键的处理:
生成RDB文件程序会数据库中的键进行检查,已过期的键不会保存到新创建的RDB文件中
AOF文件写入:当过期键被删除后,会在AOF文件增加一条DEL命令,来显式地记录该键已被删除。
AOF重写:已过期的键不会保存到重写的AOF文件中
淘汰删除
reids会设置一个最大存储值maxmemory ,表示占用物理空间的比例,每次存储时检查空间是否足够,当redis空间不足时进行淘汰删除,有三种策略
优先删除易失数据
1
2
3
4volatile-lru:挑选最久未使用的数据
volatile-lfu:挑选最少使用的数据
volatile-ttl:挑选快过期的数据
volatile-random:随机挑选数据一视同仁,检查全库
1
2
3allkeys-lru
allkeys-lfu
allkeys-randomno-enviction:禁止驱逐,就是不淘汰数据getbit key offset //获取key对应偏移位上的bit值 setbit key offset value //设置key对应偏移位上bit值,0或11
2
3
4
5
6
7
8
9
10
11
12
13
通过在配置文件中进行设置:`maxmemory-policy volatile-lru `
## 高级数据类型
### Bitmaps
当只想储存一个数据的状态时,比如有或无,是或否这种,用bitmaps可以节省空间。
它是一个字符串,但是只有0或1,每一位代表一个数据的状态,提供下面一些接口来操作:
bitop op destKey key1 [key2] //对指定的多个key进行位操作,op有or and xor not四种
bitcount key [start end] //统计key中指定范围内的bit为1的个数
1 |
|
pfadd key element [element2] //添加
pfcount key [key] //统计数据
pfmerge destKey key1 [key2] //合并key对应的数据
1 |
|
geoadd key longitude latitude member //添加坐标点,注意是在一个key中添加多个点,不同key中的点无法进行计算
geopos key member //获取坐标
geodist key member1 member2 //计算距离
georadius key longitude latitude radius m|km|mi //根据坐标求范围内的数据
georadiusbymember key member radius m|km|mi //根据点求范围内的数据
geohash key member //获取指定点对应的坐标hash值
1 |
|
cluster-enable yes
cluster-config-file nodes-port.conf
cluster-nodes-timeout 10000 //超时等待时间
1 |
|
redis-cli -c //启动集群中的客户端
集群中master断了后会自动选取一个slave顶上,如果之前的master连上了,会成为slave