Redis 学习

模式

redis支持单点,cluster,proxy和sharding四种模式,memcached支持单点,proxy和sharding三种模式。比如Twitter 的 temproxy 模式,水平扩展

优势

redis这么火,它运行有多快?一台普通的笔记本电脑,可以在1秒钟内完成十万(500K)次的读写操作。

1.	Redis 原生支持主从复制,可以实现一主多从的场景,提高了可用性
2.	Redis 原生支持 RDB 和 AOF 两种持久化方式。前者是将内存中的数据整体落地,后者是将数据的更新落地,类似于 MySQL 中的 binlog。Memcached 原生并不支持持久化
3.	Redis 支持事务
4.	Redis 支持键值对的过期时间设置
5.	Redis 3.0 中已经开始支持 Redis 集群了

redis单线程

Redis基本是内存操作,在IO和网络操作的时候,多线程的程序可以很好的利用CPU时间。那在基本是内存操作的情况下,单线程程序应该可以充分利用CPU时间了。

我们知道Redis是用”单线程-多路复用IO模型”来实现高性能的内存数据服务的,这种机制避免了使用锁,但是同时这种机制在进行集合求并集之类的比较耗时的命令时会使Redis的并发下降。

而单一线程也只能用到一个CPU核心,所以,可以在同一个多核的服务器中,可以启动多个实例(单机多端口),组成master-master或者master-slave的形式,耗时的读命令可以完全在slave进行。

memcached就是多线程+锁的模型,两者看起来差别也没有太过夸张。虽然结果是redis好一些。

数据结构

字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(SortedSet)

•	String——字符串

String 数据结构是简单的 key-value 类型,value 不仅可以是 String,也可以是数字。除了提供与 Memcached 一样的 get、set、incr、decr 等操作外,Redis 还提供了下面一些操作:
• LEN niushuai:O(1)获取字符串长度
• APPEND niushuai redis:往字符串 append 内容,而且采用智能分配内存(每次2倍)
• 设置和获取字符串的某一段内容
• 设置及获取字符串的某一位(bit)
• 批量设置一系列字符串的内容
• 原子计数器
• GETSET 命令的妙用,请于清空旧值的同时设置一个新值,配合原子计数器使用
–string 不要超过2k的字节
–set和sorted的elements不要超过5000个

•	Hash——字典(散列)

而 Redis 的 Hash 结构可以使你像在数据库中 Update 一个属性一样只修改某一项属性值,不用像memcache,反序列化后修改再序列化存储。存储、读取、修改用户属性。
数据结构优先采用hash,由于小的Hash类型数据占用的空间相对较少,因此我们在实际应用时应该尽可能的考虑使用Hash类型。省内存的原因是新建一个hash对象时开始是用ziplist来存储。如果field或者value的大小超出一定限制后,redis会在内部自动将ziplist替换成正常的hash实现。

•	List——列表

List 说白了就是链表(redis 使用双端链表实现的 List),我们可以轻松地实现最新消息排行等功能(比如新浪微博的 TimeLine )。List 的另一个应用就是消息队列,可以利用 List 的 *PUSH 操作,将任务存在 List 中,然后工作线程再用 POP 操作将任务取出进行执行。Redis 还提供了操作 List 中某一段元素的 API,你可以直接查询,删除 List 中某一段的元素。

•	Set——集合

集合的概念就是一堆不重复值的组合。利用 Redis 提供的 Set 数据结构,可以存储一些集合性的数据。比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。因为 Redis 非常人性化的为集合提供了求交集、并集、差集等操作,那么就可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。利用唯一性,可以统计访问网站的所有独立 IP

•	Sorted Set——有序集合zset

Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,比如一个存储全班同学成绩的 Sorted Sets,其集合 value 可以是同学的学号,而 score 就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。另外还可以用 Sorted Sets 来做带权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。让重要的任务优先执行。
元素数量小于128个,所有member的长度都小于64字节

skiplist编码的有序集合底层是一个命名为zset的结构体,而一个zset结构同时包含一个字典和一个跳跃表。跳跃表按score从小到大保存所有集合元素。而字典则保存着从member到score的映射,这样就可以用O(1)的复杂度来查找member对应的score值。虽然同时使用两种结构,但它们会通过指针来共享相同元素的member和score,因此不会浪费额外的内存。
zset 参考:https://www.jianshu.com/p/cc379427ef9d

redis 其他功能使用场景

  1. 订阅-发布系统
    Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在 Redis 中,你可以设定对某一个 key 值进行消息发布及消息订阅,当一个 key 值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

  2. 事务——Transactions
    虽然 Redis 的 Transactions 提供的并不是严格的 ACID 的事务(比如一串用 EXEC 提交执行的命令,在执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行),但是这个 Transactions 还是提供了基本的命令打包执行的功能

  3. 分布式锁:zsetn

redis持久化

Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件):

  1. RDB持久化方式会在一个特定的间隔保存那个时间点的一个数据快照。(故宕机时会丢失最后一次数据)

  2. AOF持久化方式则会记录每一个服务器收到的写操作。在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据。写操作命令记录的格式跟Redis协议一致,以追加的方式进行保存。(有点像MySQL的binlog)

redis性能测试

1k 基本是 Redis 性能的一个拐点,存储数据不要太大
但 Redis 的单线程设计机制只能利用一个核,所以可以在一个机器开多个redis实例
Redis 的性能幻想与残酷现实

redis和memcache的比较

Memcache 是设计为多线程的,所以在多核机器上单实例对 CPU 的利用更有效,所以它的性能天花板也更高。要达到同样的效果,对于一个 32 核机器,你可能需要部署 32 个 Redis 实例,对运维也是一种负担。

Memcached 在这方面较 Redis 逊色,只能做简单的 key/value 存储。

Redis 还有个 10k 问题,当缓存数据大于 10k(用作静态页面的缓存,就可能超过这个大小)延迟会明显增加,这也是单线程机制带来的问题。

如果你的应用业务量离 Redis 的性能天花板还比较远而且也没有 10k 需求,那么用 Redis 作缓存也是合理的,可以让应用减少多依赖一种外部技术栈。最后,搞清楚现阶段你的应用到底需要什么,是多样的数据结构和功能、更好的扩展能力还是更敏感的性能需求,然后再来选择合适的工具吧。

总结

-根据数据性质把 Redis 集群分类;我的经验是分三类:cache、buffer 和 db

  • cache:临时缓存数据,加分片扩容容易,一般无持久化需要。
  • buffer:用作缓冲区,平滑后端数据库的写操作,根据数据重要性可能有持久化需求。 (用作消息队列)
  • db:替代数据库的用法,有持久化需求。
  • 规避在单实例上放热点 key。
  • 同一系统下的不同子应用或服务使用的 Redis 也要隔离开

线上使用方式

采用Twemproxy实现redis集群以及redis-cluster两种方式。单个redis,支持多少qps看使用的命令和数据结构设计,一般100字节的吞吐量在6w qps;

  • twemproxy方式:(新的不再支持部署)
    • 缺点是:lvs+twemproxy+redis的三层方式,机器成本和管理成本都比较高,各个层次都可能发生问题和性能瓶颈。
    • 优点是:兼容redis协议和大部分命令,在服务器端完成分片,完全对开发透明。twemproxy方式可以极大提高开发效率。
  • redis-cluster:
    也是服务器端完成分片,而且提供了基于gossip和投票协议的HA方案;
    缺点是,应用程序的稳定性和健壮性依赖客户端驱动的实现(smart client)。目前jedis和phpredis都不够成熟,对目前cluster生产上线存在很大的挑战性。目前仅在大数据部门应用。

大促方案:提前一天,全量缓存,禁止审核操作,不再显示最新评论或口碑。

注意事项

  1. 减少key的个数
    合理减少key。比如,把key-value数据聚合,放到map、list里面。一个集合结构里面就可以包含很多个小数据。
  2. 由于redis是单线程,杜绝使用keys(),hkeys(),hvals(),hgetall()等函数获取大数据阻塞线程。
  3. 不使用mget(),mset(),select()等Twemproxy或Redis cluster不支持的函数。
    使用不当命令,一下子获取大量数据,会导致cpu100%的使用率。
    (1)惊群现象:一个key失效,前端高并发请求db,并把数据load到缓存。db和cache的压力急剧上升。
    (2)在连接异常情况下,回源db读取到的数据不应该回写cache。
    redis 一般在纳秒和微秒级别,大于 10 ms (毫秒),为慢查询。

扩展 Twemproxy

Twemproxy是一个代理服务器,可以通过它减少Memcached或Redis服务器所打开的连接数。并且利用它来作分片。twemproxy用了pipeline. 首先redis是支持使用pipeline批处理的。twemproxy与每个redis服务器都会建立一个连接,每个连接实现了两个FIFO的队列,通 过这两个队列实现对redis的pipeline访问。将多个客户端的访问合并到一个连接,这样既减少了redis服务器的连接数,又提高了访问性能。

优势

1.	对外暴露一个访问节点
2.	请求分片(sharding)
3.	分片要合理(分片均匀,相同的请求要分配到同样的redis节点)
4.	通过代理的方式减少缓存服务器的连接数
5.	自动在多台缓存服务器间共享数据
6.	通过不同的策略与散列函数支持一致性散列
7.	通过配置的方式禁用失败的结点
8.	运行在多个实例上,客户端可以连接到首个可用的代理服务器
9.	支持请求的流式与批处理,因而能够降低来回的消耗

劣势

twemproxy把数据分布到多个后端的redis实例中,twemproxy并不能支持所有的redis命令。另外,使用twemproxy,所有数据必须在db0,禁止select X 切换不同的逻辑db
而且twemproxy(redis数据中间层)也不支持select操作,故不允许使用多个db。
以下的命令是Twemproxy不支持的,Pub/Sub、Transactions、Scripting、Connection等模块的命令的一个都不支持。

为什么分区

存储不够?计算资源不够?带宽不够?我们都可以通过增加机器来解决这些问题。
redis集群一般是在服务端用一致性hash算法路由key

LRU和过期键删除

LRU 现在每次随机五条记录出来,插入到一个长度为十六的按空闲时间排序的队列里,然后把排头的那条删掉,然后再找五条出来,继续尝试插入队列

Memcached实现的是再标准不过的LRU算法,专门使用了一个教科书式的双向链表来存储slab内的LRU关系,分配内存超限时,很自然就会从LRU的队尾开始清理。

所以有了惰性检查,就是每次元素被访问时,才去检查它是否已经超时了,这个各家都一样。但如果那个元素后来都没再被访问呢,会永远占着位子吗?所以各家都再提供了一个定期主动删除的方式。

常见问题

如何解决Redis雪崩、穿透、并发等5大难题:https://zhuanlan.zhihu.com/p/58331707

参考资料

Redis 数据结构使用场景
redis 干货

你可能感兴趣的:(中间件,redis)