rails3项目解析之3——redis

2023-12-09 03:32
文章标签 项目 redis 解析 rails3

本文主要是介绍rails3项目解析之3——redis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在国家的正确指引和坚强领导下,在国内经济突飞猛进一片光明的大好形势下,随着互联网的飞速发展,即使是普通互联网应用的用户数量也呈线性上升趋势,更不用说国外那些大型的广受欢迎的诸多“并不存在”的网站们指数级的用户增长。而且网站内的数据关系也随着SNS等应用的兴起发生了很大的变化。传统的关系型数据库已经慢慢地对这些新时代的新特性显得有些力不从心。如何提高单台数据库服务器负载能力,如何更高效更迅速地处理简单类型的数据关系,成了摆在我们面前的亟待解决的问题。 

时隔多年又过了把写论文的瘾。下面说点有用的。 

当初在设计这个项目的架构时,就准备引入nosql作为主要组成部分。一是从网站的预期流量上,单台mysql要撑起来还真有点费劲,mysql的扩展方案又不是很优雅方便。二是当前凡是有点活力的场所,张口闭口都是nosql,现在搞个项目要是还在跟sql语句死抠较劲,你都不好意思跟人家打招呼。所以经过一番论证和可行性分析,最终我们选择了redis和mongodb来各负其责。这次先简单说说redis。 

1、选择理由  

喜欢一个人是没有理由的,但选择一个组件,却一定是要有理由的。这关系到日(名词)后有没有扩展空间,在项目中好不好用,大家写起代码来会不会暗地里大骂当初那个选型的人。 

redis是一款内存型的key-value数据库,它允许把所有的数据都保留在内存里,保证了数据存取的速度。又有持久化和日志机制,保证了断电时数据的完整性。redis支持hash、list、(sorted) set等数据类型,作为绝大多数的应用来说已经足够。而且redis的更新非常快,开发者们都很敬业努力,这也是选择一个开源组件的很重要的一个方面。 

因为这个系列不是专门讲解redis开发的,所以更详细的使用特性和开发手册,请微移莲步至 官方网站 。 

2、适用场景  

项目中使用redis的场景主要有以下几处: 

2.1 rails默认缓存。 凡是rails需要使用缓存的地方,比如页面片段缓存等,都会用到指定的默认缓存系统。这个配置起来很简单,只需要一行代码即可,而且也不必关心rails具体在redis上是怎么实现的,自有 redis_store 来完成这一切。 

Ruby代码   收藏代码
  1. config.cache_store = :redis_store$config.redis[:server]  


2.2 自定义缓存。 主要是以对象缓存的形式,保存在开发中认为有必要进行快速存取的数据。自定义缓存需要自己写一个类,通过redis store调用 redis client 的命令,来实现数据的存取。比如首页上需要调用的某些资讯数据,就不再每次都从mysql中获取,而是由后台任务定时从mysql中读取或在内容更新时读取并保存至redis缓存中。 

其中要注意一点,redis保存的value值,只接受字符串格式,所以如果要通过自定义缓存保存非字符串型的数据,就需要使用Marshal进行序列化和反序列化。 

2.3 任务队列。 执行异步和定时任务的resque和resque-scheduler组件,使用redis作为任务队列服务器。同样,按照resque的配置说明,一行代码即可搞定。 

Ruby代码   收藏代码
  1. Resque.redis = Redis.new($config.redis[:server])  


3、扩展redis缓存  

redis_store只是按照ActiveSupport::Cache的规范实现了诸如read、write、increment、decrement、delete等通用的存取接口,而作为redis一大亮点的hash、set等数据结构则在默认的规范中没有用武之地。而且在项目中,很有可能会有存取hash类型缓存的需求。 

作为金融资讯网站,当天的股票行情信息是非常重要的,访问率非常高,而且要求访问速度很快,如果每次访问都要去oracle实时查询,则无法满足速度的要求。因此,当天所有的股票行情数据,我们从oracle中取出之后,都要保存redis的高速缓存中。 

国内的股票一共有2000多支,每支股票的行情数据要按照不低于每分钟一次的频率进行实时刷新。如果每支股票的数据都存为一个key-value键值对,那么在进行每分钟更新时,要同时取出2000个键值对,反序列化,对每支股票依次插入最新的行情数据,再依次序列化保存。经过实际测试,循环2000次序列化和反序列化所用时间极长,想在1分钟内完成这个任务是不可能的。 

因此这就是一个典型的hash类型缓存存取的需求。我们把这2000支股票数据作为一个hash来进行保存,key是:stocks,field就是每支股票的代码,这样就不需要循环2000次存取数据,而只需一个redis命令就能完成所有2000多支股票数据的保存和读取,满足了在一分钟内实时刷新行情数据的要求。而且如果要读取某一支股票的数据,也只需指定key和field,就可迅速取出数据。 

实现方法是扩展redis_store的RedisStore::Cache::Store类。具体代码就很简单了,这也显示出了redis的功能强大和ruby编程的便利。 

Ruby代码   收藏代码
  1. def hwrite(key, hash)  
  2.   @data.hmset(key, *hash.map{|k, v| [k, Marshal.dump(v)]}.flatten(1))  
  3. end  
  4.   
  5. def hread(key, field = nil)  
  6.   field.nil? ? Hash[*@data.hgetall(key).map{|k, v| [k, Marshal.load(v)]}.flatten(1)] :  
  7.                Marshal.load(@data.hget(key, field))  
  8. rescue TypeError  
  9. end  


其中@data是Redis::Factory创建的一个Redis::Store实例,负责调用redis client执行redis命令。 

同样,如果在项目中需要list和set等数据类型的缓存,也可按此思路一并处理。 

4、redis高可用  

因为redis不仅作为缓存使用,而且也是resque执行异步和定时任务的消息队列,因此对于可用性的要求就比较高,一旦挂掉,所有后台任务就会全部停止,严重影响网站的功能和体验。 

但是redis原生的cluster解决方案迟迟不出,去年看redis官网的时候,说是直到今年5月份才可能会有rc放出,所以没办法,只能自己做一个山寨的高可用方案勉强支撑一段时间。 

PS:今年5月份的时候我再看,却又拖到“不早于夏末”了。原来不只是XXX说话不算数的。 

redis双机高可用的基础,是redis的主备复制机制。指定主备角色,是用slaveof命令。 

指定本机为master 
Ruby代码   收藏代码
  1. slaveof NO ONE  


指定本机为192.168.1.10的slave 
Ruby代码   收藏代码
  1. slaveof 192.168.1.10 6379  


本来一开始我也想如同mysql的master-master机制那样,分别在配置文件中指定本机为对方的slave,不过后来发现这个方法行不通。如果配置文件中都设置slaveof x.x.x.x,那么这两个redis启动之后不提供服务,客户端无法连接,类似于服务死锁的状态。 

经过多次实验发现,如果两个服务按照master-slave的方式启动,然后给master发送slaveof命令,指定其为另一个的slave,则此时双方都为slave,数据可以进行双向同步。基于这个原理,设计了一个redis双机互备的机制。 

在自定义的配置文件中,做如下配置: 

Ruby代码   收藏代码
  1. redis:  
  2.   server: redis://192.168.1.1:6379  
  3.   cluster:  
  4.     master: redis://192.168.1.10:6379  
  5.     slave: redis://192.168.1.20:6379  


192.168.1.1是keepalived的virtual ip,应用程序只使用这个ip地址来存取redis。 

其核心的实现方式如下: 

4.1 两台redis服务器,配合keepalived。初始状态,是在master(192.168.1.10)上绑定keepalived的virtual ip 192.168.1.1。 
4.2 启动一个监控脚本,每秒钟对两个redis服务进行一次扫描。 
4.3 如果两台redis处于正常master-slave状态,则不进行操作。 
4.4 如果master挂掉,监控脚本对在线的slave(192.168.1.20)发送slaveof NO ONE命令,设置其为临时的主机temp-master,同时由于原来的master服务器挂掉,virtual ip 192.168.1.1自动转移至temp-master,不影响应用程序对redis的存取。此时应用程序新产生的数据都保存到temp-master(192.168.1.20)上。 
4.5 脚本监测到原来的master(192.168.1.10)在挂掉后重新启动加入集群,则向master发送slaveof 192.168.1.20 6379命令,设置其为temp-slave,从temp-master(192.168.1.20)复制在自己挂掉期间丢失的数据。同时virtual ip自动跳回temp-slave(192.168.1.10)向应用程序提供服务。 
4.6 延时30秒钟,确保数据复制完毕,对调temp-master和temp-slave的角色,恢复默认的master-slave体系。 

我知道延时30秒钟确保数据复制完毕这种方式很不好,但我确实在redis的info命令响应中没有找到指示复制完毕的字段。如果有消息能够明确指出数据复制完毕的状态会更好。 

这样,两台redis服务器中的任何一台挂掉,都会由另一台继续提供服务,不会对网站形成可察觉的影响,也不会丢失数据。 

5、redis配置  

redis的配置也比较灵活强大,使得redis的使用也方便了不少。 

5.1 持久化频率。 配置save a b,指定在a秒内如果有b次key的改变,就执行硬盘持久化。此频率根据服务器状态进行设定,最好不要太过频繁。 

5.2 内存限制。 使用maxmemory,限制最大使用内存,如数据超出这个大小,则按照LRU把最不常用的移出redis。这个特性对于使用内存有限的VPS时比较适合,免得内存超出之后造成宕机或天量收费。 

5.3 虚拟内存。 设置vm-enabled,可指定redis能够使用的最大物理内存,当存储数据大于此内存值时,按照LRU算法把最不常使用的value移出到硬盘的虚拟内存文件中。不过所有的key都是保存在内存中的,这个不可设置。 

5.4 二进制日志。 当然,redis可以设置5.1所述的save参数,但如果存盘动作太密集,则会占用很多的资源,速度一慢也就失去了内存数据库的主要优点。为此redis设计了日志机制。通过设置appendonly,可以开启日志选项,每一个发送到redis执行的命令,都会被立刻追加到硬盘的日志文件中,如果redis意外宕机,则在重新启动的时候,redis会读取日志里的内容,恢复内存中尚未持久化的数据。 

不过因为appendonly是所有数据的累积,所以文件大小增长非常快,在我们的项目中,差不多每一个小时就会增长6个G。虽然appendonly是另开进程操作的,但文件太大也会影响效率,更何况还有塞满硬盘的危险。为此我们使用定时任务,每半个小时向redis发送bgrewriteaof命令,使redis按照当前数据快照重写日志,重写后的日志大小与内存数据大小在同一个数量级上。 

这篇关于rails3项目解析之3——redis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/472309

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Redis 的 SUBSCRIBE命令详解

《Redis的SUBSCRIBE命令详解》Redis的SUBSCRIBE命令用于订阅一个或多个频道,以便接收发送到这些频道的消息,本文给大家介绍Redis的SUBSCRIBE命令,感兴趣的朋友跟随... 目录基本语法工作原理示例消息格式相关命令python 示例Redis 的 SUBSCRIBE 命令用于订

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署