欢迎关注头条号:java小野猫
1.简介
![eb643e7cd1d0b135870bfe9318d34c08.png](https://i-blog.csdnimg.cn/blog_migrate/6c518af16924f8c4df845a3752f98905.jpeg)
Redis是一个基于内存的Key-Value非关系型数据库,由C语言进行编写。
Redis一般作为分布式缓存框架、分布式下的SESSION分离、分布式锁的实现等等。
Redis速度快的原因:基于内存、单线程、多路复用。
2.Redis数据结构
Redis中提供了五种数据结构,分别是String、Hash、List、Set、ZSet,每种数据结构底层都是通过字符串来进行实现。
2.1 String
Key对应的Value是一个字符串类型。
#设置字符串类型的Keyset key value#仅当Key不存在时设置字符串类型的Keysetnx key value#设置字符串类型的Key并添加过期时间setex key second value#获取Key对应的Valueget key#让Key对应的Value值递增1incr key#让Key对应的Value值递减1decr key#让Key对应的Value递增指定的数值incrby key num#让Key对应的Value递减指定的数值decrby key num#往Key对应的Value中追加字符串append key str
在使用set、append命令时,如果Key不存在则创建,否则替换Key对应的Value值。
在使用incr、incrby、decr、decrby命令时,如果Key不存在则初始化为0后再进行操作,如果Key对应的Value不是数值类型字符串,那么将会报错。
2.2 Hash
Key对应的Value是一个哈希类型,每个哈希类型中都包含若干个键值对(属性名与属性值)
#往Hash中添加一个属性hset key field value#仅当Key不存在时往Hash中添加一个属性hsetnx key field value#往Hash中添加多个属性hmset key field1 value1 field2 value2#获取Hash中指定的一个属性hget key field#获取Hash中指定的多个属性hmget key field1 field2#获取Hash中所有的属性hgetall key#让Hash中指定的属性递增指定的数值(属性值必须是数值类型)hincrby key field num#判断Hash中指定的属性是否存在hexists key field#获取Hash中属性的个数hlen key#获取Hash中所有的属性名hkeys key#获取Hash中所有的属性值hvals key#删除Hash中指定的多个属性hdel key field1 field2
在使用hset、hmset命令时,如果Key不存在则创建,否则往Key对应的Hash中添加属性,如果属性名相同则替换属性值。
Hash中只有hincryby命令,没有类似String的incr、decr、decrby命令。
当使用hdel命令删除了Hash中的所有属性,那么此时Key也会被删除。
2.3 List
Key对应的Value是一个List类型(有序可重复),Redis中的List使用链表的方式进行实现,因此其在插入和删除操作时性能要比顺序表的高(头指针指向链表的第一个结点)
#从链表的左侧添加元素lpush key value#从链表的右侧添加元素rpush key value#仅当Key存在时从链表的左侧添加元素lpushx key value#仅当Key存在时从链表的右侧添加元素rpushx key value#获取链表中指定索引范围的元素(从链表的左侧开始遍历,包括begin和end的位置,如果end为-1表示倒数第一个元素)lrange key begin end#从链表的左侧弹出一个元素lpop key#从链表的右侧弹出一个元素rpop key#获取链表中元素的个数llen key#删除链表中指定个数个Value(若count为正数,则从链表的左侧开始删除指定个数个Value,若count为负数,则从链表的右侧开始删除指定个数个Value,若count为0,则删除链表中所有指定的Value)lrem key count value#设置链表中指定索引的值lset key index value#从链表的右侧弹出元素并将其放入到其他链表的左侧(一般用在消息队列的备份)rpoplpush key otherKey
当使用lpush、rpush命令时,如果Key不存在则创建,否则往Key对应的链表中追加元素。
通过Redis的List可以实现队列和栈结构,当遵循lpush、rpop时,此时为队列结构,当遵循lpush、lpop时,此时为栈结构。
2.4 Set
Key对应的Value是一个Set(无序不可重复)
#往Set中添加元素sadd key value#删除Set中指定的元素srem key value#查看Set中的元素smembers key#判断Set中是否包含某个元素sismemeber key value#返回Set中元素的个数scard set#返回两个Set的交集sinter set1 set2#返回两个Set的并集sunion set1 set2#返回Set1中Set2没有的元素(补集)sdiff set1 set2#将Set1和Set2的交集放入到新的Set中sinterstore destSet set1 set2#将Set1和Set2的并集放入到新的Set中sunionstore destSet set1 set2#将Set1中Set2没有的元素放入到新的Set中sdiffstore destSet set1 set2
在使用sadd命令时,如果Key不存在则创建,否则往Key对应的Set中追加元素。
2.5 ZSet
Key对应的Value是一个带分数的Set(有序不可重复),其中分数可以相同但Value不能相同。
#往ZSet中添加元素zadd key score value#获取ZSet中指定Value的分数zscore key value#返回ZSet中元素的个数zcard key#获取ZSet中指定索引范围的元素(包括begin和end的位置,end为-1时表示倒数第一个元素)zrange key begin end#获取ZSet中指定索引范围的元素以及分数,返回的元素按照分数从小到大排序zrange key begin end withscores#获取ZSet中指定索引范围的元素以及分数,返回的元素按照分数从大到小进行排序zrevrange key begin end withscores#获取ZSet中指定分数范围的元素(包括begin和end的位置)zrangebyscore key begin end#获取ZSet中指定分数范围的元素并限制返回的个数zrangebyscore key begin end limit num#返回ZSet中指定分数范围元素的个数zcount key begin end#删除ZSet中指定的元素zrem key value#删除ZSet中指定分数范围的元素(包括begin和end的位置)zremrangebyscore key begin end#让ZSet中指定元素的分数递增指定的值zincrby key score value
当使用zadd命令时,如果Key不存在则创建,否则往Key对应的ZSet中添加元素。
Set、ZSet中没有类似String、Hash、List的,当Key不存在或存在时才进行操作的命令。
2.6 通用命令
#查看Redis中的Key(支持通配符,*代表任意个字符,?代表任意一个字符)keys pattern#删除Keydel key#判断Key是否存在exists key#对Key进行重命名rename oldKey newKey#对Key设置过期时间expire key seconds#查看Key的有效时间(若Key没有设置过期时间则返回-1)ttl key#查看Key的类型type key
3.Redis事务
Redis中提供了事务的功能,Redis会将事务中的所有命令当做一个原子执行,即一个事务在执行时不会被其他命令所干扰。
#开启事务multi#执行事务exec#放弃事务discard
在Shell中事务与客户端进行绑定,当一个客户端开启了事务后,该客户端事务之后的命令都将放入一个新的队列中,使用exec命令执行事务,其他客户端的命令并不会进入此事务,在Jedis中通过Transaction实例封装一个事务,不受影响。
4.Redis持久化
由于Redis是基于内存的Key-Value非关系型数据库,因此当Redis服务挂掉后由于内存被释放会导致数据丢失,此时可以使用Redis的持久化功能。
4.1 RDB持久化方式
RDB持久化方式即Redis每隔一定时间,就会将当前内存中的所有Key-Value写入到磁盘文件中(全量写入),当Redis服务重启时,读取RDB文件自动进行数据的恢复。
RDB持久化方式是默认开启的,可以通过redis.conf配置文件中修改相关配置。
#在900秒内如果至少有1个Key发生变化,那么执行一次写入操作save 900 1#在300秒内如果至少有10个Key发生改变,那么执行一次写入操作save 300 10#在60秒内如果至少有10000个Key发生改变,那么执行一次写入操作save 60 10000#rdb文件的保存路径(相对于redis.conf文件)dir ./#rdb文件的名称dbfilename dump.rdb
使用RDB持久化方式有很大可能会发生Key的修改未来得及写入到磁盘中服务器就宕机了(可以调整默认的同步策略)
RDB持久化方式由Redis进程执行fork操作创建子进程来完成,因此阻塞只会发生在fork阶段,客户端可以通过save、bgsave命令手动触发RDB操作,其中save命令会阻塞redis进程直到RDB持久化完成,而bgsave命令由redis进程fork子进程进行完成。
4.2 AOF持久化方式
AOF持久化方式即Redis将所有的Key-Value操作都写入到日志文件中(追加,增量写入),当Redis服务重启时,读取AOF文件自动进行数据的恢复。
AOF持久化方式提供了三种同步策略:每修改同步、每秒同步、不同步。
#开启AOF方式appendonly yes#AOF文件名appendfilename "appendonly.aof"#AOF同步策略#每修改同步appendsync always#每秒同步appendsync everysec#不同步appendsync no
AOF持久化方式也是由Redis进程执行fork操作创建子进程来完成,并且当日志文件达到一定大小时Redis会对其压缩(重写)
当同时使用RDB和AOF持久化方式时,数据的恢复将固定使用AOF的。
RDB持久化方式与AOF持久化方式的对比
1.文件大小:AOF持久化方式所产生的日志文件要比RDB持久化方式所产生的rdb文件要大。
2.安全性:AOF持久化方式数据丢失的可能性要比RDB持久化方式低。
3.效率:AOF持久化方式在进行数据恢复时的效率要比RDB持久化方式的低。
5.Redis过期策略以及淘汰机制
5.1 过期策略
Redis中可以对Key设置过期时间,当Key已经到达过期时间时,并不会立即被删除,而是通过Redis的过期策略进行处理。
Redis中使用惰性删除和主动删除两种过期策略:
惰性删除:当访问一个已经过期的Key时,将该Key删除。
主动删除:Redis定时删除缓存中部分过期的Key,通过抽样的方式保证Redis中过期的Key在低于25%以下。
*Redis定时删除缓存中部分过期的Key是通过Redis常规任务处理的,常规任务还包含其他一些任务处理,可以通过修改redis.conf配置文件中的hz参数来调整Redis常规任务的执行频率,默认值是10,表示每秒执行10次,其取值为1~500,但Redis不建议该值超过100,否则会影响其他的业务请求,造成延时。
5.2 淘汰机制
当Redis中使用的内存已经超过所允许的最大值时(maxmemory),会根据预先设置的淘汰策略主动删除缓存中部分Key。
Redis中提供了六种淘汰策略:
1.allkeys-lru(推荐):删除部分最近最少使用的Key。
2.volatile-lru:在设置了过期时间的Key中,删除部分最近最少使用的Key。
3.allkeys-random:随机删除部分Key。
4.volatile-random:随机删除部分设置了过期时间的Key。
5.volatile-ttl:删除部分即将过期的Key。
6.noeviction:不清除,对于写请求直接返回错误。
*可以通过redis.conf配置文件中的maxmemory-policy参数设置Redis的淘汰策略。
6.Redis其他的常用应用场景
Redis除了作为缓存以外,还有很多其他的常用应用场景。
6.1 基于Redis实现分布式锁
一般通过Redis中的一个Key来实现分布式锁,当Key已经存在时,表示当前锁已经被其它线程锁持有,当Key不存在时,设置Key,表示获取锁资源。
分布式锁可能面临的问题:
1.当系统宕机时如何保证锁能够被释放?
*通过对Key设置过期时间,当Key已经过期并再次访问时,通过惰性删除过期策略删除该Key。
2.怎样保证获取锁时的同步问题?
*通过使用Redis提供的set(String key, String value, String nxxx, String expx, long time)命令,将判断Key是否存在以及设置Key作为一个原子操作。
3.怎样保证释放的锁是当前线程的?
*给Key设置一个代表当前线程的Value,当删除该Key时判断Value是否属于当前线程的。
/** * 获取锁 */public boolean getLock(String key, String value, int second) { return "OK".equalsIgnoreCase(jedis.set(key, value, "nx