目录
最近在学习redis,觉得redis确实是分布式系统中的一个利器,于是看了很多官方文档,带着一些问题,结合平时项目中使用情况作了一些总结,本文不适合redis初学者,初学者可以查看Redis 命令参考先学习下redis。
以下来自Stack Overflow的一个问答memcached-vs-redis,已经说的十分清楚了:
redis比memcached更强大、更流行、更受支持。Memcached只能做Redis能做的一小部分事情。即使在它们重叠的一些地方,redis也能做的更好。
以下是详细对比:
有时候我们需要批量导入一些数据,这时候可以通过将命令写入文本中,然后通过管道导入redis,文本中的命令不需要显示的分隔符结尾,内容如下:
set key1 value1
set key2 value2
set key3 value3
导入命令如下:cat command.txt|redis-cli -h 10.10.23.15
也可以加上--pipe
批量执行命令
redis的哨兵是官方作为redis高可用的解决方案,针对的是redis的主从故障转移,没有分片的功能。如果你的数据一个redis实例能够完全存放时,那么redis Sentinel是一种不错的方案,能够监视redis集群,并提供故障转移的功能。但是如果一台redis不能完全放下你的数据时,你必须选择扩展redis,有很多解决方案,早期很多采用了客户端分片或者代理的形式,在redis3.0官方支持集群,这种方案是服务端分片,一开始不理解哨兵和集群的关系,以为哨兵是用来做高可用,集群只是分片,但是其实集群本身就是自治的,并且已经有了高可用的功能,在配置集群的时候,可以配置主节点和相应的从节点,集群会相互探测节点的存活,在主节点下线时进行故障转移,等于说,搭建集群事并不需要也不应该才让哨兵介入。
这里我看到了一篇十分不错的文章:Redis性能问题排查解决手册,内容十分不错,在排查redis性能问题的时候可以当一个手册来看,我主要关注的点如下:
这里想说下zset的两个命令: ZRANGE key start stop [WITHSCORES]
和ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
,zrange的复杂度O(log(N)+M),N为有序集的基数,M为被结果集的基数没有太多疑问。但是ZRANGEBYSCORE这个命令的服务度,如果没有后面的[LIMIT offset count]那么他和zange的复杂度是一样的,但是如果加上后面的分页参数他的复杂度实际是O(log(N)+M+offset),试想这样的场景:zset有200000个元素,且分数一样(这样的场景是存在的,见:redis实战),查找排名199990到200000的元素,两者的复杂度天差地别,实现的分页功能确实一样的。所以,一共要小心这种隐藏的坑。
在实现一个需要加锁的操作时,redis提供了三种方案:
redis中lua脚本十分简单,可以把它理解为redis中的存储过程。在数据库中,尤其是mysql,很多公司都禁止使用存储过程,因为不好维护,有很多人喜欢把逻辑写在存储过程(其实我觉得如果存储过程中只写简单的sql,并且有统一的公司规范也是比较容易维护的,主要是有些人写在代码中,有些人写存储过程,而且逻辑写经常写在存储过程,所以直接禁止比较好)。
对于redis的lua来说,我觉得在有些情况下是很有必要的,因为redis单线程处理客户端命令的这一个特点,lua脚本的执行也是原子性的,在很多的需要事务的场景都可以采用lua来代替,比如:获取key a的值,如果a>10,那么设置b为0。采用lua可以减少网络的往返次数,在对性能有极致要求的情况下,采用lua提高几倍的吞吐量是非常有用的手段。
但是我认为在大多数情况下都不应该使用lua,尽量采用redis本身支持的事务和流水线等功能来提高吞吐量,理由如下:
我的建议还是尽量不使用lua,如果有必要使用lua可以作为后期性能瓶颈的优化方案。
另外在说说事务和lua,事务在某些场景还是lua不能代替的,如:在事务watch之后,我需要检查下内存中的变量a,如果a大于0,那么执行事务。lua的上下文是在redis中,它能看到的只是整个redis,所以lua并不能完全代替事务。
很多人都知道redis的事务概念,也知道数据库中的事务,但是这两个事务是没有什么关联的。redis的事务保证了操作的原子性,在操作执行的过程中,操作不会被打断,例如操作 1,2,3 即使1失败了,2和3照样会执行。redis事务的原子性和数据库事务的原子性也没有太大关系,如果说非要对应那么更加对应的是数据库事务的隔离性,数据库事务的原子性和隔离性定义如下(摘自360百科):
原子性: 整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
redis没有回滚,如果失败也继续执行
隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
redis单线程保证了执行过程中不会被另一个事务打扰
在使用的过程中觉得zset的功能实在是强大,因为他有序性的特点,能提供很多功能。有必要好好学习底层的实现(hashmap+skiplist),非常有用的数据结构。
memcache默认是基于lru驱逐,而redis的默认是关闭的,如果内存满了,redis会拒绝执行写命令。在编写程序的时候需要知道redis有没有配置lru,如果配置了那么你的key都是不可靠的,有可能会丢失,这就是缓存的典型场景。对于缓存来说,最好给你的每个key都设置上过期时间。
关于redis的部署方案有很多,需要针对具体的应用场景,主要考虑的是成本,一个备份节点没用到,成本很高,另外如果作为数据库必须持久化
方案无非是 持久化、主从高可用、读写分离、集群等组合,需要根据实际的业务来部署,建议在微服务架构下不要采用一套方案,而是多个方案部署,比如缓存部署一套,内存数据库方案部署一套,针对高并发场景单一致性要求不高的部署读写分离。每个服务之间用到的数据库在最好隔开,如果使用同一个缓存就选择不同的db,有利于后期迁移数据。关于部署方案可以参考阿里云redis文档,非常值得学习。
这里有一点疑问,我在网上看到很多都说主从采用链式复制,包括阿里云的读写分离也采用了这样一个方案,好处显而易见,但是不知道自己搭建的话怎么实现,我看官网文档的sentinel都是星式复制,找不到相关的部署方法,如果有知道的,望大神告知!
原文:https://www.cnblogs.com/chenfangzhi/p/10018213.html