1 多级缓存架构介绍
任何项目中我们都有一些频繁的查询,而那些频繁的查询数据基本都是相同的,比如项目中查看用户个人信息,购物狂欢查询活动信息,这些功能点使用频率非常高,我们一般需要对他们做特殊处理。
我们以狂欢活动信息为例,当双十一的时候,很多人会去查看今天优惠活动有哪些,对这些活动信息我们可以采用多
级缓存架构来抗住高并发。
2 Java常用缓存设计
基于Java版的缓存架构实现流程如下:
1、用户请求经过Nginx
2、Nginx检查是否有缓存,如果Nginx有缓存,直接响应用户数据
3、Nginx如果没有缓存,则将请求路由给后端Java服务
4、Java服务查询Redis缓存,如果有数据,则将数据直接响应给Nginx,并将数据存入缓存,Nginx将数据响应给用户
5、如果Redis没有缓存,则使用Java程序查询MySQL,并将数据存入到Reids,再将数据存入到Nginx中
优点:
1、采用了Nginx缓存,减少了数据加载的路径,从而提升站点数据加载效率
2、多级缓存有效防止了缓存击穿、缓存穿透问题
缺点:
Tomcat并发量偏低,导致缓存同步并发量失衡,缓存首次加载效率偏低,Tomcat 大规模集群占用资源高
3 Lua版多级缓存架构改进
优点:
1、采用了Nginx缓存,减少了数据加载的路径,从而提升站点数据加载效率
2、多级缓存有效防止了缓存击穿、缓存穿透问题
3、使用了Nginx+Lua集成,无论是哪次缓存加载,效率都高
4、Nginx并发量高,Nginx+Lua集成,大幅提升了并发能力
4 Nginx+Lua多级缓存实战
Lua预备知识
我们以双十一活动信息加载为例,通过多级缓存架构来加载双十一活动信息,双十一活动信息表结构如下:
CREATE TABLE `activitt_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL COMMENT '活动名称', `desc` varchar(3000) DEFAULT NULL COMMENT '活动介绍', `starttime` datetime DEFAULT NULL, `endtime` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
4.1 链接MySQL封装
创建一个lua脚本 mysql.lua ,用于提供根据SQL语句执行查询操作,代码如下:
--引入依赖库mysql local mysql = require "resty.mysql" --配置数据库链接信息 local props = { host = "192.168.211.141", port = 3306, database = "redpackage", user = "root", password = "123456" } --创建一个对象 local mysqlobj = {} --根据SQL语句查询 function mysqlobj.query(sql) --创建链接、设置超时时间、编码格式 local db = mysql:new() db:set_timeout(10000) db:connect(props) db:query("SET NAMES utf8") --执行SQL语句 local result = db:query(sql) --关闭链接 db:close() --返回结果集 return result end return mysqlobj
4.2 链接Redis集群封装
我们需要安装 lua-resty-redis-cluster ,用该依赖库实现Redis集群的操作,这里提供github地址,大家下载后按操
作配置即可,下载地址: <https://github.com/cuiweixie/lua-resty-redis-cluster>,下载该文件配置后即可
实现Redis集群操作。
接下来我们实现Redis集群操作,创建一个脚本 redis.lua ,脚本中实现Redis中数据的查询和添加操作,代码如
下:
4.3 多级缓存操作
配置创建Nginx缓存共享空间
lua_shared_dict act_cache 128m;
创建多级缓存操作脚本 activity.lua ,代码如下:
--多级缓存流程操作 --1)Lua脚本查询Nginx缓存 --2)Nginx如果没有缓存 --2.1)Lua脚本查询Redis --2.1.1)Redis如果有数据,则将数据存入到Nginx缓存,并响应用户 --2.1.2)Redis没有数据,Lua脚本查询MySQL -- MySQL有数据,则将数据存入到Redis、Nginx缓存[需要额外定义],响应用户 --3)Nginx如果有缓存,则直接将缓存响应给用户 --响应数据为JSON类型 ngx.header.content_type="application/json;charset=utf8" --引入依赖库 --cjson:对象转JSON或者JSON转对象 local cjson = require("cjson") local mysql = require("mysql") local lrredis = require("redis") --获取请求参数ID http://192.168.211.141/act?id=1 local id = ngx.req.get_uri_args()["id"]; --加载本地缓存 local cache_ngx = ngx.shared.act_cache; --组装本地缓存的key,并获取nginx本地缓存 local ngx_key = 'ngx_act_cache_'..id local actCache = cache_ngx:get(ngx_key) --如果nginx中没有缓存,则查询Redis集群缓存 if actCache == "" or actCache == nil then --从Redis集群中加载数据 local redis_key = 'redis_act_'..id local result = lrredis.get(redis_key) --Redis中数据为空,查询数据库 if result[1]==nil or result[1]==ngx.null then --组装SQL语句 local sql = "select * from activity_info where id ="..id --执行查询 result = mysql.query(sql) --数据不为空,则添加到Redis中 if result[1]==nil or result[1]==ngx.null then ngx.say("no data") else --数据添加到Nginx缓存和Redis缓存 lrredis.set(redis_key,cjson.encode(result)) cache_ngx:set(ngx_key, cjson.encode(result), 2*60); ngx.say(cjson.encode(result)) end else --将数据添加到Nginx缓存中 cache_ngx:set(ngx_key, result, 2*60); --直接输出 ngx.say(result) end else --输出缓存数据 ngx.say(actCache) end
4.4 Nginx配置
#活动查询
location /act { content_by_lua_file /usr/local/openresty/nginx/lua/activity.lua; }