【介绍】Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
以上是从Redis中文网上抄来的。根据自己的理解来说,Redis是一款Nosql(非关系型)数据库,通过Key-Value形式在内存中存储数据。Redis的Value类型多样,除了String以外可以是List、Set、Hash、SortedSet等数据结构进行存储。支持分片、事务、LRU、Master-Slave等高级特性。
Redis数据结构
- string,二进制安全的字符串。值得长度不能超过512MB。
- lists,按插入顺序排序的字符串元素集合,类似Java中的链表。
- hashes,由field和关联的value组成的map。field和value都是字符串。
- sets,类似Java中的Set,无序不重复的字符串集合。
- sorted set,类似sets,有个权重值score,通过score进行排序。
- bit arrays(simply bitmaps),位图不是一种实际的数据类型,而是在字符串类型上定义的一些列面向位的操作。
- hyperloglogs,用于估计set中元素数量的概率性数据结构。
- geo,存储给定的地理位置信息。
Redis String
对于Redis String的操作类似于Memcache。区别是Memcache只保存在内存中,Redis可以持久化到本地;Memcache在读取数据特别是读取批量数据的性能上更好一些。写入性能相差不大。
Redis String同时支持一些特殊的操作,比如原子性操作,获取多个key的值,设置数据存活时间等
Redis Lists
Redis的Lists是由链表(Linked List)实现的。每个ListNode节点存放的是ziplist(ziplist本身也是一能维持数据项先后顺序的列表,而且是一个内存紧缩的列表。),意味着在头部和尾部插入数据很快,但是通过索引查询比较慢。
Redis Lists支持阻塞式访问操作BRPOP、BLPOP,可以设置阻塞等待时间。
Lists的key是自动创建和删除的,只需要push、pop即可。
Redis Hashes
可以将Hashes当做对象操作,以key-value的形式。
Redis Sets
Redis Sets是String的无序不重复队列。提供取交集、并集等操作。
Redis Sorted Sets
通过score可以进行范围取值的Sets。
Redis内部有个skiplist (跳表,简单来说就是除了一层原始链表,还会生成若干层稀疏的链表,在最高层开始依次查找–>向下–>查找)数据结构,Redis通过skiplist、ziplist、dict实现Sorted Sets。
Redis Bitmaps
Bitmaps可以说不是一个数据结构,是对值的位操作。可以方便快速的做一些数据统计的工作。
Redis Hyperloglogs
这是一个基于基数估算的算法,只能比较准确的估算出基数,可以使用少量固定的内存去存储并识别集合中的唯一元素。而且这个估算的基数并不一定准确,是一个带有 0.81% 标准错误(standard error)的近似值。
个人理解Hyperloglogs更适用于进行数据的校验,例如Bloom Filter。
Redis Geo
基于坐标的数据结构,可以通过给定的坐标信息进行查找等。
Redis Keys
Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值。空字符串也是有效key值。
- 应该避免太长的键值,既消耗内存又增加在数据中查找键值的计算成本。
- 尽量让键值有意义。
- 在同一项目中尽量同一键值格式
Redis I/O
Redis是单线程的,那么Redis是通过什么来保证效率的呢?
- Redis将数据存储到内存中,数据在内存中操作性能是很高的。
- Redis使用了多路复用Reactor I/O模型。
- Redis单线程操作,减少了上下文切换。
Redis 持久化
Redis持久化选择:
- RDB,在指定的时间间隔对数据进行快照存储。
- AOF,记录每次对Redis的操作。
- RDB&AOF同时进行。
- 取消持久化。
RDB
RDB在备份的时候fork出一个子进程,备份的完成都是通过子进程完成的。
优点:RDB是保存了某个时间点的数据集,非常适合数据备份;因RDB文件单一,相比更容易容灾恢复。
缺点:出现意外中断时会丢失自上次备份完成后发生的数据;当某一时间段有大批量数据时有可能会影响Redis的整体效率。
AOF(Append Only File)
AOF文件是一个只进行追加的日志文件。当文件过大可自动重写(新建AOF文件)
优点:AOF的日志追加间隔可以设置成每次操作,这样可以避免数据的丢失。
缺点:AOF文件体积比RDB要大,在fsync策略上速度会比RDB慢。
LRU(Least Recently Used)
Redis内存淘汰策略,
- noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
- allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
- volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
- allkeys-random: 回收随机的键使得新添加的数据有空间存放。
- volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
- volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
Redis 分区(cluster)
Redis集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令。
Redis集群没有使用一致性hash,而是引入了哈希槽的概念。Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来觉得放置那个槽。每个节点负责一部分哈希槽。
Redis Sentinel
Redis的Sentinel系统用于管理多个Redis服务器,该系统执行以下三个操作:
- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器
Sentinel支持集群,增加了Master的健壮性。
Redis 一致性
Redis集群使用的异步的是赋值,所以Redis并不能保证数据的强一致性,在某些情况下也会丢失数据。
Redis 可能遇到的问题
Redis和数据库双写一致性问题?
对于数据量较小的,写入不频繁的数据可以在数据库成功写入后删除掉Redis中的数据,在下一次读取时读取数据库中数据并放入Redis。
对于数据量较小的,写入频繁,且要求一致性高的数据建议不使用Redis,每次都从数据库中读取。
对于数据量较大的,在写入数据库后更新Redis数据,更新失败可以做补偿措施,比如消息重试。
Redis穿透&雪崩
穿透
面对穿透需要对前端数据进行必要的校验,尽量避免穿透Redis请求数据库,造成数据库收到大量请求发生异常。
更新缓存时做好同步锁,在缓存失效后相同的缓存只在数据库中请求一次,其他的线程等待重试。
雪崩
尽量避免在相同时间有大量缓存失效,根据情况设置失效时间。
缓存备份,使用两个数据失效时间不同的两个缓存。
总结
Redis通常作为一个缓存中间件存在于系统中,可以根据需要存放数据来减少对数据库的访问,可以作为分布式锁。
- Redis采用多路复用Reactor I/O模型,单线程性能依然很好。
- Redis支持多种数据结构,支持原子性操作。
- Redis支持数据持久化,可以进行备份容灾。
- Redis支持分区、集群。
Redis的使用还是要根据使用量、类型的不同来找出适合自己的方式。