概要
私は、MySQLの知識ポイントから学ぶこの記事のアイデア:MySQLのInnoDBのエンジン処理バッファ。そこに、そのソースコードを達成するために何も慎重な研究はありませんが、そのデザインは、まだ私にインスピレーションを与えているが。
本論文では、LRUの問題、キャッシュ汚染を回避または軽減するために強化されたアルゴリズムを考え、主なアプローチは老いも若きも、2つの領域に分け、元LRUスペースで、ヒット数(または時間のブロック)によって制御され、0.37で古い係数の割合が大きさを所定の。
次のセクションでは、Java用の実装コードを分割されます。
1.LRU基本概念
2.LRU設計上の問題とLRUG
3.LRUGの詳細な説明
4.完全なサンプルコード
1.LRU基本概念
LRU歴史的な記録データに基づいてデータアクセスを排除する(最小最近使用し、最低使用)アルゴリズム。いくつかは、一般的にバッファー交換、ページ置換および他の処理のために使用します。
次のように典型的なLRU二重にリンクされたリスト+ HashMapのは、次のとおりです。
デザインLRUGに問題があります2.LRU
LRUの問題は、キャッシュデータを汚染し、突然の熱雑音データを避けることができません。いくつかは、二つ以上のキューを維持することにより、キャッシュのうち制御データを更新するために、LRU-K、2Q、MQなど、このLRU、の変異体。私は、ここで私は、コードに名前だけを書きたいときにのみ、LRUGと呼ばれる議論アルゴリズム。
若い古い部分は、そこに若いセグメントに新しいデータがあるが、古いへの最初のときLRUG利用のHashMapと二重リンクリストは、他の保守キューが、二重にリンクされたリストに若い分割され、古い領域は、期間の直前に挿入されることはありませんデータ数が一定量を超えるヒットし続ける場合セクション、挿入操作ヘッダの後に(いくつかの時間のためにロックすることができます)。境界のような2つの37%、全容量の37%まで古いフルセグメントのサイズすなわち。(図1)
(図1)
3.LRUG詳細
3.1まず、hitNumヒット数である二重リンクリストノード構造、:
private static class Node<K,V>{
int hitNum;
K key;
V value;
Node<K,V> prev;
Node<K,V> next;
Node(K key,V value){
this.key=key;
this.value=value;
hitNum=0;
}
}
3.2在加载阶段,数据以先后顺序加入链表,半满载时,young段已满,新数据以插入方式加入到old段,如图2所示。注意半满载时,也可能有madeYoung操作,把old区的数据提到young头。
(图2)
public void put(K key,V value){
Node<K,V> node=caches.get(key);
if(node==null){
if(caches.size()>=capcity){
caches.remove(last.key);
removeLast();
}
node=new Node(key,value);
if(caches.size()>=pointBorder){
madeOld(node);
}else{
madeYoung(node);
}
}else {
node.value=value;
if(++node.hitNum>BLOCK_HIT_NUM){
madeYoung(node);
}
}
caches.put(key,node);
}
3.3当数据命中时,如果位于young区,命中数+1后进行常规的madeYoung操作,把该项提到链表首部。如图3
(图3)
如果命中项位于old区,对命中数+1后与BLOCK_HIT_NUM设置的值做判断,超过设定值说明该项数据可能不是突发数据,进行madeYoung操作提到链表首部,否则不做处理。
特别的,如果命中项正好是point,则point应该往后退一项,指向原point的下一项,此时young区膨胀了一项,而old区缩小了一项。极端情况下,ponit项持续被命中并进行madeYoung,point不断后退直到尾巴,此时young区占有100%容量,而old区为0,设置point指向last,意味着新数据项加入时,淘汰掉young区的末尾,而新数据项放在末尾成为old区。如图4
(图4)
public void madeYoung(Node node){
if(first==node){
return;
}
if(node==point){
point=node.next;
if(point==null) {
point=last;
}
}
if(node.next!=null){
node.next.prev=node.prev;
}
if(node.prev!=null){
node.prev.next=node.next;
}
if(node==last){
last=node.prev;
}
if(first==null||last==null){
first=last=node;
point=null;
return;
}
node.next=first;
first.prev=node;
first=node;
}
public void madeOld(Node node){
if(point.prev!=null){
point.prev.next=node;
node.prev=point.prev;
}
if(point.next!=null){
node.next=point.next;
point.next.prev=node;
}
point=node;
}
3.4需要一个清理的方法。也可以设置一些监测方法,如一段时间内的命中数(监测命中率)等,这与本篇主要内容无关就不写在这了。
public void removeLast(){
if(last!=null){
if(last==point) {
point=null;
}
last=last.prev;
if(last==null) {
first=null;
}else{
last.next=null;
}
}
}
4.示例代码
主要代码如下,时间仓促,可能一些地方会考虑不周,读者如发现,欢迎指出。
package com.company;
import java.util.HashMap;
public class LRUNum<K,V> {
private HashMap<K,Node> caches;
private Node first;
private Node last;
private Node point;
private int size;
private int capcity;
private static final int BLOCK_HIT_NUM=2;
private static final float MID_POINT=0.37f;
private int pointBorder;
public LRUNum(int capcity){
this.size=0;
this.capcity=capcity;
this.caches=new HashMap<K,Node>(capcity);
this.pointBorder=this.capcity-(int)(this.capcity*this.MID_POINT);
}
public void put(K key,V value){
Node<K,V> node=caches.get(key);
if(node==null){
if(caches.size()>=capcity){
caches.remove(last.key);
removeLast();
}
node=new Node(key,value);
if(caches.size()>=pointBorder){
madeOld(node);
}else{
madeYoung(node);
}
}else {
node.value=value;
if(++node.hitNum>BLOCK_HIT_NUM){
madeYoung(node);
}
}
caches.put(key,node);
}
public V get(K key){
Node<K,V> node =caches.get(key);
if(node==null){
return null;
}
if(++node.hitNum>BLOCK_HIT_NUM){
madeYoung(node);
}
return node.value;
}
public Object remove(K key){
Node<K,V> node =caches.get(key);
if(node!=null){
if(node.prev!=null){
node.prev.next=node.next;
}
if(node.next!=null){
node.next.prev=node.prev;
}
if(node==first){
first=node.next;
}
if(node==last){
last=node.prev;
}
}
return caches.remove(key);
}
public void removeLast(){
if(last!=null){
if(last==point) {
point=null;
}
last=last.prev;
if(last==null) {
first=null;
}else{
last.next=null;
}
}
}
public void clear(){
first=null;
last=null;
point=null;
caches.clear();
}
public void madeYoung(Node node){
if(first==node){
return;
}
if(node==point){
point=node.next;
if(point==null) {
point=last;
}
}
if(node.next!=null){
node.next.prev=node.prev;
}
if(node.prev!=null){
node.prev.next=node.next;
}
if(node==last){
last=node.prev;
}
if(first==null||last==null){
first=last=node;
point=null;
return;
}
node.next=first;
first.prev=node;
first=node;
}
public void madeOld(Node node){
if(point.prev!=null){
point.prev.next=node;
node.prev=point.prev;
}
if(point.next!=null){
node.next=point.next;
point.next.prev=node;
}
point=node;
}
private static class Node<K,V>{
int hitNum;
K key;
V value;
Node<K,V> prev;
Node<K,V> next;
Node(K key,V value){
this.key=key;
this.value=value;
hitNum=0;
}
}
}