Redis-内存淘汰策略

本文探讨了Redis的过期删除策略(定期与惰性)和淘汰机制(volatile、allkeys、LRU和TTL),并展示了如何用PHP实现LRU算法。还介绍了OpenResty多级缓存示例,涉及lua脚本和Redis与MySQL的配合使用。

1. 过期删除策略

  1. 定期删除:在redis当中,默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。
  2. 惰性删除: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
Redis 内存淘汰策略的配置是优化其性能和资源管理的重要环节。配置内存淘汰策略主要通过修改 Redis 的配置文件 `redis.conf` 来实现。以下是具体的配置方法和相关说明: ### 配置 Redis 内存淘汰策略 1. **设置最大内存限制** 在 `redis.conf` 文件中,找到 `maxmemory` 参数并设置一个合适的值。该值决定了 Redis 可以使用的最大内存容量。例如: ```conf maxmemory 100mb ``` 如果未设置 `maxmemory`,Redis 会使用系统内存的物理限制,但通常建议显式设置以避免内存溢出问题。 2. **选择内存淘汰策略** Redis 提供了多种内存淘汰策略(Eviction Policy),可以通过 `maxmemory-policy` 参数进行配置。常见的策略包括: - `noeviction`:当内存不足时,拒绝所有写入操作并返回错误。 - `allkeys-lru`:淘汰最近最少使用的键,无论是热数据还是冷数据。 - `volatile-lru`:仅淘汰设置了过期时间的键,并优先淘汰最近最少使用的键。 - `allkeys-random`:随机淘汰任意键。 - `volatile-random`:随机淘汰设置了过期时间的键。 - `volatile-ttl`:优先淘汰剩余时间较短的键。 在 `redis.conf` 文件中,设置合适的策略。例如,选择 `allkeys-lru`: ```conf maxmemory-policy allkeys-lru ``` 3. **保存并重启 Redis 服务** 修改完配置文件后,需要重启 Redis 服务以使新的配置生效。重启命令通常为: ```bash redis-server /path/to/redis.conf ``` ### 适用场景与最佳实践 - **`allkeys-lru`** 策略适用于缓存场景,当所有键都可能被访问且需要保留热点数据时,该策略能够有效保留频繁访问的数据。 - **`volatile-ttl`** 策略适用于临时数据存储场景,优先淘汰剩余时间较短的键,可以更高效地释放内存资源。 - **`noeviction`** 策略适用于严格的数据完整性要求场景,当内存不足时直接拒绝写入操作,避免数据丢失风险。 ### 注意事项 - **性能影响**:某些淘汰策略(如 `allkeys-lru` 和 `volatile-lru`)在实现时会引入额外的计算开销,需根据实际硬件性能和业务负载进行权衡。 - **键的分布特征**:根据键的访问频率和生命周期选择合适的淘汰策略。例如,对于临时键较多的场景,推荐使用 `volatile-ttl`。 - **监控与调整**:定期监控 Redis内存使用情况和淘汰率,必要时调整 `maxmemory` 和淘汰策略以优化性能。 通过上述配置步骤和最佳实践,可以灵活调整 Redis内存管理行为,以适应不同的业务需求和资源限制。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值