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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动