缓存利器、Lua模块下的共享内存
上一节讲到了worker进程的共享内存,它利用丰富的指令使数据的缓存操作变得非常简单,但它也存在一些缺点。
1.worker进程之间会有锁竞争,在高并发的情况下会增加性能开销。
2.只支持Lua布尔值、数字、字符串和nil类型的数据,无法支持table类型的数据。
3.在读取数据时有反序列化操作,会增加CPU开销。
共享内存在Ngx_Lua中作为缓存工具还是非常出色的。笔者在生产环境中,曾多次使用lua_shared_dict的各种特性,并未感受到存在明显的性能问题。但如果读者还是介意这些缺点或需要缓存更复杂的数据的话,可以使用lua-resty-lrucache。
10.2.1 安装lua-resty-lrucache
lua-resty-lrucache是基于Ngx_Lua的缓存利器,它拥有如下优点。
1.支持更丰富的数据类型,可以把table存放在value中,这对数据结构复杂的业务非常有用。
2.可以预先分配key的数量,不用设置固定的内存空间,在内存的使用上更为灵活。
3.每个worker进程独立缓存,所以当worker进程同时读取同一个key 时不存在锁竞争。
但它与lua_shared_dict相比也有一些缺点。
1.因为数据不在worker之间共享,所以无法保证在更新数据时,数据在同一时间的不同worker进程上完全一致。
2.虽然可以支持复杂的数据结构,但可使用的指令却很少,如不支持消息队列功能。
3.重载Nginx配置时,缓存数据会丢失。如果使用lua_shared_dict,则不会如此。
有利就有弊,读者在使用时可以根据自身需求进行选择。lua-resty-lrucache的安装方式和其他的lua-resty模块一样,如下所示:
# git clone https://github.com/openresty/lua-resty-lrucache.git
# cp -r lua-resty-lrucache/lib/resty/lrucache* \
/usr/local/nginx_1.12.2/conf/lua_modules/resty/
10.2.2 使用lua-resty-lrucache进行缓存的方法
通过下面的例子来了解一下lua-resty-lrucache的使用方式,首先需要对模块进行加载,方法如下:
local lrucache = require "resty.lrucache"
local lrucache = require "resty.lrucache.pureffi"
读者在加载lua-resty-lrucache时,需要把上面的2个文件复制到lua_package_path所设置的路径上。它们的作用是一样的,但性能有所区别:resty.lrucache适合用来缓存命中率高或读操作远远大于写操作的缓存业务;resty.lrucache.pureffi适合用来缓存命中率低或需要对key进行频繁增、删操作的缓存业务。请根据业务需求进行选择。
然后,将下面的代码写入test_m.lua中,并将此文件放到lua_package_path的路径下,代码如下:
local _M = {}
local lrucache = require "resty.lrucache"
--在缓存上声明1个1000个key的列表
local cache, err = lrucache.new(1000)
if not cache then
return error("failed to create the cache: " .. (err or "unknown"))
end
--此函数用来往缓存中存储key/value的值
local function mem_set()
--set()中的内容从左到右顺序依次是key、value、有效期(2s)
cache:set("a", 19, 2)
cache:set("b", {"1","2","3"},0.001) --支持插入table类型的数据
return
end
--此函数用来获取缓存里的value。 a即value的值,如果a为nil,则表示value不存在或已过期;如果stale_data有值,也说明value已过期
local function mem_get(key)
local a,stale_data = cache:get(key)
return a,stale_data
end
function _M. fromcache ()
--获取a的值
local a,stale_data = mem_get("a")
--如果a存在,就输出a的值
if a then
ngx.say("a: ", a)
--如果a不存在且stale_data有值,就输出过期的value,并重新执行存储操作,然后再次输出value
elseif stale_data then
ngx.say("a 已经过期: " , stale_data)
mem_set()
local a_again = mem_get("a")
ngx.say("a: ", a_again )
--如果a 和 stale_data都不存在,则执行存储操作后再输出value
else
ngx.say("no found a")
mem_set()
local a_again = mem_get("a")
ngx.say("a: ", a_again )
end
end
return _M
修改nginx.conf文件,代码如下:
location / {
content_by_lua_block {
--加载模块,执行数据的读取操作
require("test_m").fromcache()
}
}
重载Nginx配置,执行结果如下:
# curl 'http://testnginx.com/'
no found a
a: 19
[root@testnginx ~]# curl 'http://testnginx.com/'
a: 19
[root@testnginx ~]# curl 'http://testnginx.com/'
a: 19
[root@testnginx ~]# curl 'http://testnginx.com/'
a 已经过期: 19
a: 19
从执行结果可以看出:
1.第1次请求,因为a没有值,所以先输出“no found a”,然后又执行了存储操作。
2.第2次请求,因为有缓存值,直接输出value。
3.第3次请求,仍然有缓存值,直接输出value。
4.第4次请求,因为为缓存数据设置的有效期很短,此时已经过期,所以输出了过期的value,并再次执行存储操作,又输出了value。
如果尝试重载Nginx配置,会发现每次重启(restart)后a都没有值,因为在重载配置的过程中,缓存数据会丢失。
下面将对lua-resty-lrucache的常见指令进行说明。
new
语法:cache, err = lrucache.new(max_items [, load_factor])
含义:创建1个缓存实例。如果创建失败会返回nil,并将错误信息返回给err。
max_items用来声明缓存key的数量,从这个设置可以看出它虽然没有规定内存的使用大小,但规定了key的数量。
load_factor参数是加载resty.lrucache.pureffi模块时才会用到的,它基于FFI(Foreign Function Interface,外部功能接口)的hash表的负载因子,值的区间在0.1~1之间,默认值是0.5。负载因子与hash数据的读取时间和对内存空间大小的权衡有关,有兴趣的读者可以自行查询相关信息。
set
语法:cache:set(key, value, ttl)
含义:把key/value存储到缓存中。ttl是缓存的有效期,以秒为单位,默认值是0,表示不会过期;支持设置为0.001s。
get
语法:data, stale_data = cache:get(key)
含义:获取指定key的值,如果key不存在或已过期,就返回nil;如果存在过期数据,过期的值会赋值给stale_data。
delete
语法:cache:delete(key)
含义:从缓存中移除指定的key。
flush_all
语法:cache:flush_all(key)
含义:刷新整个缓存区域的数据,等于清空内存中的数据。这种方式比创建新的缓存实例要快得多。
缓存利器、Lua模块下的共享内存的更多相关文章
- win32下进程间通信——共享内存
一.引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换 ...
- UNIX环境下的共享内存
好久没更新博客了,最近几个月一直在忙项目,现在终于有时间进一步学习了.这次记录的是unix环境中共享内存的使用方法. 在我理解,共享内存就是在内存中开辟一段空间,各个毫不相干的进程就可以通过访问这段 ...
- [转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile
http://blog.csdn.net/stpeace/article/details/39534361 进程间的通信方式有很多种, 上次我们说了最傻瓜的“共享外存/文件”的方法. 那么, 在本文中 ...
- Linux下进程间通信--共享内存:最快的进程间通信方式
共享内存: 一.概念: 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间. 进程A可以即时看到进程B ...
- Delphi与Qt在Windows下使用共享内存进程间通信
Delphi部分 type TGuardInfo=record Lock: Integer; end; PGuardInfo = ^TGuardInfo; TGuardShareMem=c ...
- Boost:shared_memory_object --- 共享内存
什么是共享内存 共享内存是最快速的进程间通信机制.操作系统在几个进程的地址空间上映射一段内存,然后这几个进程可以在不需要调用操作系统函数的情况下在那段内存上进行读/写操作.但是,在进程读写共享内存时, ...
- Nginx之共享内存与slab机制
1. 共享内存 在 Nginx 里,一块完整的共享内存以结构体 ngx_shm_zone_t 来封装,如下: typedef struct ngx_shm_zone_s ngx_shm_zone_t; ...
- system v和posix的共享内存对比 & 共享内存位置
参考 http://www.startos.com/linux/tips/2011012822078.html 1)Linux和所有的UNIX操作系统都允许通过共享内存在应用程序之间共享存储空间. 2 ...
- Linux IPC之共享内存
System V共享内存机制: shmget shmat shmdt shmctl 原理及实现: system V IPC机制下的共享内存本质是一段特殊的内存区域,进程间需要共享的数据被放在该共 ...
随机推荐
- Nginx详细介绍
1.Nginx是什么? Nginx就是反向代理服务器. 首先我们先来看看什么是代理服务器,代理服务器一般是指局域网内部的机器通过代理服务发送请求到互联网上的服务器,代理服务器一般作用于客户端.比如Go ...
- 策略模式、策略模式与Spring的碰撞
策略模式是GoF23种设计模式中比较简单的了,也是常用的设计模式之一,今天我们就来看看策略模式. 实际案例 我工作第三年的时候,重构旅游路线的机票查询模块,旅游路线分为四种情况: 如果A地-B地往返都 ...
- 洛谷 P1194 【买礼物】
这道题其实就是转化一个模型就可以了. 买了一个另外一个又优惠,其实就相当于在优惠的时候连一条边,因为不可能多买,所以就是建一棵最小生成树.最后因为肯定买了一件物品,要加上最初的单价. 代码: #inc ...
- sql语句-根据动态参数去拼sql
1.查询 我们有的时候会有根据参数当条件去查找sql,但是参数有的需要有的不需要应该怎么办呢? 就比如这种的 这时候我们可以遍历传进来的参数 request.form会获取他所填写的参数 show = ...
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作
相关 (1) 相关博文地址: SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y- ...
- day11 文件操作(上)
目录 一.什么是文件 二.为何要用文件 三.如何使用文件 3.1文件操作的基本流程 3.2资源回收with上下文管理 3.3指定操作文本的字符编码 四.文件的操作模式 4.1控制文件读写操作的模式(t ...
- numpy基础用法学习
numpy get started 导入numpy库,并查看numpy版本 import numpy as np np.__version__ '1.14.0' 一.创建ndarray 1. 使用np ...
- python技巧 namedtuple
python的namedtuple可以创建一个带字段名的元祖和一个带名字的类 In [1]: from collections import namedtuple ...: ...: nginx=na ...
- 1731: [Usaco2005 dec]Layout 排队布局*
1731: [Usaco2005 dec]Layout 排队布局 题意: n头奶牛在数轴上,不同奶牛可以在同个位置处,编号小的奶牛必须在前面.m条关系,一种是两头奶牛距离必须超过d,一种是两头奶牛距离 ...
- JavaScript 基础 学习(三)
JavaScript 基础 学习(三) 事件三要素 1.事件源: 绑定在谁身上的事件(和谁约定好) 2.事件类型: 绑定一个什么事件 3.事件处理函数: 当行为发生的时候,要执行哪一个函数 ...