redis数据结构
-
String(字符串)
- string类型是Redis最基本的数据类型,一个键最大能存储512MB
- value其实不仅是String,也可以是数字,可做数值计算
- 常用命令: get、set、incr、decr mget
-
List(列表)
- list在底层实现上并不是数组,而是链表,按照插入顺序排序,可以在头部和尾部插入新元素
- 优点是插入快,缺点是查找慢
- 常用命令: lpush,rpush,lpop,rpop,lrange
- 应用场景:
- 消息队列
- 利用LRANGE还可以很方便的实现分页的功能
- 在博客系统中,每篇博文的评论也可以存入一个单独的list中
- 最新消息
- 排行榜
-
Set(集合)
- Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)
- 相关操作: 添加新元素、删除已有元素、取交集、取并集、取差集等
- 常用命令: sadd,srem,spop,sdiff ,smembers,sunion
- 应用场景:
- 利用集合的求交集、并集、差集等操作可以实现,共同好友、二度好友、共同关注等功能
- 利用唯一性,可以统计访问网站的所有独立IP
- 好友推荐的时候,根据 tag 求交集,大于某个 threshold 就可以推荐
- QQ有一个社交功能叫做“好友标签”,比如“大美女”、“土豪”、“欧巴”
-
zset(sorted set:有序集合)
- 不允许重复,有序集合中的每个元素都关联一个序号(score),这便是排序的依据
- 常用命令: zadd,zrange,zrem,zcard
- 实现方式: 内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单
- 应用场景: 带有权重的元素,比如一个游戏的用户得分排行榜
-
Hash(哈希)
-
它是一个string类型的field和value的映射表,hash特别适合用于存储对象
-
Redis的Hash实际是内部存储的Value为一个HashMap, 并提供了直接存取这个Map成员的接口, 如: hmset user:001 name “李三” age 18 birthday “20010101” 。也就是说,Key仍然是用户ID,value是一个Map,也就是通过 key(用户ID) + field(属性标签) 实现
-
Redis 的 Hash 结构可以像在数据库中 Update 一个属性一样只修改某一项属性值
-
常用命令: hget,hset,hgetall
-
实现方式: Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht
-
应用场景: 比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希
-
Redis的持久化
-
RDB(Redis DataBase)
- 是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法(非增量的)
- redis会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的
- 优点: 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效
- 缺点: 如果对数据的完整性非常敏感,那么RDB方式就不太适合,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失
-
AOF(Append Only File)
- AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单
- AOF持久化策略是每秒钟记录一次,在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据
- redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩
- 缺点: AOF文件要比RDB文件的体积大。而且,AOF方式的恢复速度也要慢于RDB方式
Redis的主从同步
- 像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构
- 主从同步原理:
- 从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中
- 在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器
- 即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个从服务器
- 在2.8版本之后,redis支持了效率更高的增量同步策略,即主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。如果出现网络瞬断,重新连接好以后,从服务器会请求增量数据,主服务器就会向从服务器发送增量内容
redis的事务处理
- 事务命令
- multi 用来组装一个事务;–标记开始!!!
- exec 用来执行一个事务;
- discard 用来取消一个事务;
- watch 用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。
- 原理
- redis是单线程处理所有client请求
- redis保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令
- 非事务情况下,redis在接收一个命令后会立即处理并返回处理结果;但在开启事务之后,命令并不立即执行,而是先放到一个队列中。当接收到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的执行结果打包到一起返回给client,然后事务结束
- redis从2.1支持乐观锁实现CAS(check and set)操作,可以显示使用watch对某个key加锁。watch的key对整个连接有效,如果连接断开,则监视和事务都会被自动清除。当被watch的key在multi命令之前被修改,则整个事务将跳出,exec命令将返回nil
redis的哨兵(Sentinel)监控
-
哨兵(sentinel):哨兵是一个分布式系统,你可以在一个架构中运行多个哨兵进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master
-
虽然哨兵(sentinel) 是出自一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel)
-
主观宕机: 每个哨兵会向其它哨兵、master、slave定时发送消息,以确认对方是否”活”着, 如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂
-
客观宕机: 若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡",通过一定的vote(选举)算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置
-
哨兵(sentinel)执行以下三个任务
- 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常
- 提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知
- 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master
跨jvm的id生成器
谈到这个话题,首先要知道redis-server端是单线程来处理client端的请求的。这样来实现一个id生成器就非常简单了,只要简单的调用jdeis.incr(key);就搞定了。你或许会问,incr是原子操作吗,能保证不会出现并发问题吗,前面说过,server端是单线程处理请求的