1. 过期删除策略
- 定期删除:在redis当中,默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。
- 惰性删除:redis在获取某个key的时候就会其进行检查,若这个key设置了过期时间,它就会进行判断是否过期,如果过期了此时就会删除,不会给你返回任何东西。
总结:定期删除是集中处理,惰性删除是零散处理。
2. 几种淘汰机制
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
- no-enviction(驱逐):禁止驱逐数据,这也是默认策略。
3. PHP实现lru算法(lru淘汰策略实现)
- ru算法是什么? 下图很好的解释了这一算法。
- 使用php实现lru策略
class LruNodeService
{
private $key;
//key对应的内容
private $data;
//结点右指针
private $next;
//结点左指针
private $previous;
/**
* Node constructor.
* @param $key
* @param $data
*/
public function __construct($key, $data)
{
$this->key = $key;
$this->data = $data;
}
/**
* Sets a new value for the node data
* @param string the new content of the node
*/
public function setData($data)
{
$this->data = $data;
}
/**
* Sets a node as the next node
* @param Node $next the next node
*/
public function setNext($next)
{
$this->next = $next;
}
/**
* Sets a node as the previous node
* @param Node $previous the previous node
*/
public function setPrevious($previous)
{
$this->previous = $previous;
}
/**
* Returns the node key
* @return string the key of the node
*/
public function getKey()
{
return $this->key;
}
/**
* Returns the node data
* @return mixed the content of the node
*/
public function getData()
{
return $this->data;
}
/**
* Returns the next node
* @return Node the next node of the node
*/
public function getNext()
{
return $this->next;
}
/**
* Returns the previous node
* @return Node the previous node of the node
*/
public function getPrevious()
{
return $this->previous;
}
}
class LruService
{
/*
* 头部节点
*/
private $head;
/*
* 尾部节点
*/
private $tail;
/*
* 最大容量,大于淘汰部位节点指向上一个元素
*/
private $capacity;
/*
* 存储key对应的节点
*/
private $hashmap;
public function __construct($capacity)
{
$this->capacity = $capacity;
$this->hashmap = array();
$this->head = new LruNodeService(null,null);
$this->tail = new LruNodeService(null,null);
$this->head->setNext($this->tail);
$this->tail->setPrevious($this->head);
}
/**
* 获取元素
* @param $key
* @return null
*/
public function get($key)
{
if (!isset($this->hashmap[$key])) {
return null;
}
$node = $this->hashmap[$key];
if (count($this->hashmap) == 1) {
return $node->getData();
}
//先删除已经存在的结点
$this->detach($node);
//重新将新结点插入到头结点之后
$this->attach($this->head, $node);
return $node->getData();
}
/**
* 设置key value
* @param $key
* @param $data
* @return bool
*/
public function put($key, $data)
{
if ($this->capacity <= 0) {
return false;
}
if (isset($this->hashmap[$key]) && !empty($this->hashmap[$key])) {
$node = $this->hashmap[$key];
//重置结点到头结点之后
$this->detach($node);
$this->attach($this->head, $node);
$node->setData($data);
} else {
$node = new LruNodeService($key, $data);
$this->hashmap[$key] = $node;
//添加节点到头部节点之后
$this->attach($this->head, $node);
//检测容量是否达到最大值
if (count($this->hashmap) > $this->capacity) {
//如果达到最大值 删除尾节点左指针指向的元素
$nodeToRemove = $this->tail->getPrevious();
$this->detach($nodeToRemove);
unset($this->hashmap[$nodeToRemove->getKey()]);
}
}
return true;
}
/**
* 删除key
* @param $key
* @return bool
*/
public function remove($key)
{
if (!isset($this->hashmap[$key])) {
return false;
}
$nodeToRemove = $this->hashmap[$key];
$this->detach($nodeToRemove);
unset($this->hashmap[$nodeToRemove->getKey()]);
return true;
}
/**
* 添加新结点到头结点之后
* @param $head
* @param $node
*/
private function attach($head, $node)
{
//双向链表插入一个元素到头结点之后
$node->setPrevious($head);
$node->setNext($head->getNext());
$node->getNext()->setPrevious($node);
$node->getPrevious()->setNext($node);
}
/**
* 删除结点
* @param $node
*/
private function detach($node)
{
$node->getPrevious()->setNext($node->getNext());
$node->getNext()->setPrevious($node->getPrevious());
}
}
4. 多级缓存
4.1 Openresty安装
yum install readline-devel pcre-devel openssl-devel
wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz # 下载
tar xzvf ngx_openresty-1.9.7.1.tar.gz # 解压
cd ngx_openresty-1.9.7.1/
./configure
make
make install
4.2 使用Openresty创建lua脚本
ngx.header.content_type="application/json;charset=utf8"
local cjson = require("cjson") --引入模块
local mysql = require("resty.mysql") --引入mysql模块
local uri_args = ngx.req.get_uri_args()
local product_id = uri_args['product_id']
local db = mysql:new() --初始化数据库
db:set_timeout(1000) --设置超时时间
local props = {
host = "192.168.59.100", --mysql ip地址
port = 3306, --mysql 端口
database = "shop", --mysql 数据库
user = "root", password = "root" --用户名密码
}
local res = db:connect(props) --获得mysql连接
local select_sql = "select id,store from fm_products where id ='"..product_id.."'"--一条查询语句
res = db:query(select_sql) db:close() --执行
local redis = require("resty.redis") --引入redis
local red = redis:new() red:set_timeout(2000) --初始化 设置超时时间
local ip ="192.168.59.108"
local port = 6379
red:connect(ip,port)
red:auth("root")
red:set("product_"..product_id,cjson.encode(res)) --存储到redis
red:close()
ngx.say("{flag:true}")
ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args()
local product_id = uri_args['product_id']
local cache_ngx = ngx.shared.dis_cache;
local adCache = cache_ngx:get('productcache'..product_id);
if adCache == "" or adCache == nil then
local redis = require("resty.redis") --引入redis
local red = redis:new() red:set_timeout(2000) --初始化 设置超时时间
local ip ="192.168.59.108"
local port = 6379
red:connect(ip,port)
red:auth("root")
local rescontent=red:get("product_"..product_id)
ngx.say(rescontent)
red:close()
cache_ngx:set('productcache'..product_id, rescontent, 10*60);
else
ngx.say(adCache)
end