يشير نموذج ذاكرة التخزين المؤقت TwoEqueues المذكورة في هذه المقالة إلى نموذج ذاكرة التخزين المؤقت للبيانات في الذاكرة.
بغض النظر عن اللغة ، قد تحتاج إلى وضع بعض البيانات في الذاكرة لتجنب العمليات المتكررة والقراءة. السيناريو الأكثر شيوعا هو محدد jQuery. اختيار بعض عناصر DOM يستغرق وقتًا طويلاً للغاية. نأمل في تخزين هذه البيانات دون الحاجة إلى إعادة تفعيل شجرة DOM في كل مرة ندعو فيها.
احفظه ، ولكن يجب أن يكون هناك كمية! لا يمكنك وضع جميع البيانات التاريخية في الذاكرة. بعد كل شيء ، لا تزال سعة الذاكرة الحالية تهدئة للغاية. حتى لو كانت الذاكرة كبيرة بما يكفي ، فإن الذاكرة المخصصة من قبل كل مؤشر ترابط لا تزال محدودة.
لذا فإن السؤال هو ، كيف يمكننا ذاكرة التخزين المؤقت بكفاءة بيانات مفيدة حقًا؟ يتضمن ذلك خوارزميات القضاء ، ويجب القضاء على البيانات غير المرغوب فيها من أجل الحفاظ على بيانات مفيدة.
هناك العديد من الأفكار الشائعة:
FIFO: إنه قائمة انتظار أول في أول ، أول بيانات مخزنة مؤقتًا ، وتم القضاء عليها أولاً. يتم استخدام هذا النموذج في إطار JQuery الشهير.
LRU: بنية قائمة مرتبطة مزدوجة ، في كل مرة يتم تخزين البيانات الجديدة ، يتم وضعها مباشرة في رأس القائمة المرتبطة ؛ يتم نقل البيانات التي يتم الوصول إليها في كل مرة إلى رأس القائمة المرتبطة. وبهذه الطريقة ، فإن البيانات في نهاية القائمة المرتبطة هي شيء لم يتم استخدامه مؤخرًا وسيتم القضاء عليه.
twoequeues: FIFO+ LRU. تقوم FIFO بشكل أساسي بتخزين البيانات المخزنة لأول مرة ، وتخزن LRU بيانات النقاط الساخنة التي تم استخدامها مرتين على الأقل. هذه الخوارزمية لها معدل ضرب مرتفع ، والقدرة القوية على التكيف ، والتعقيد المنخفض.
هناك العديد من خوارزميات القضاء الأخرى ، ولكن لا يوجد سوى نوعين منها يتم استخدامهما أكثر في الممارسة العملية. نظرًا لأن خوارزمياتها ليست معقدة ، وسهلة التنفيذ ، وكفاءة التنفيذ العالية ، ومعدل ضرب ذاكرة التخزين المؤقت مقبول في معظم المناسبات. بعد كل شيء ، تتطلب خوارزمية ذاكرة التخزين المؤقت استهلاك وحدة المعالجة المركزية. إذا كان معقدًا للغاية ، على الرغم من أنه تم تحسين معدل الإصابة ، فإنه لا يستحق الخسارة. فقط تخيل ، إذا جلبت البيانات من ذاكرة التخزين المؤقت ، فإن الأمر يستغرق وقتًا أطول من الجلب من الموقع الأصلي. ما هو استخدام ذاكرة التخزين المؤقت؟
لن أتحدث عن النظريات المحددة. هناك بعض عبر الإنترنت ، لكنني لا أفهمها كثيرًا. اليوم سوف أشارككم إصدار JavaScript من طراز ذاكرة التخزين المؤقت TwoEqueues.
دعنا نتحدث عن كيفية استخدامه أولاً ، الأمر بسيط للغاية.
طريقة الاستخدام الأساسية هي كما يلي:
[/شفرة]
var tq = initTwoqueues (10) ؛
tq.set ("key" ، "value") ؛
tq.get ("مفتاح") ؛
[/شفرة]
عند التهيئة ، فقط حدد سعة ذاكرة التخزين المؤقت. تجدر الإشارة إلى أنه نظرًا لأن FIFO+LRU يتم تنفيذها داخليًا ، فإن السعة الفعلية هي ضعف السعة المحددة. يحدد المثال أعلاه 10 (أزواج القيمة الرئيسية) ، والتي يمكن أن تخزن بالفعل 20.
يجب تحديد حجم السعة وفقًا لسيناريو التطبيق الفعلي. إذا كان صغيرًا جدًا ، يكون معدل الإصابة منخفضًا ، والكفاءة منخفضة ، والطرف هو عكس ذلك ، وتحتاج إلى قياسه بنفسك.
أثناء عملية التطوير ، من أجل مراجعة تأثير ذاكرة التخزين المؤقت ، يمكن تهيئة تجمع ذاكرة التخزين المؤقت إلى إصدار التطوير:
نسخة الكود كما يلي:
var tq = initTwoqueues (10 ، true) ؛
tq.hitratio () ؛
فقط أضف معلمة إلى النهاية وصحيح فقط. سيحسب تجمع ذاكرة التخزين المؤقت التي تم تهيئتها بهذه الطريقة تلقائيًا معدل الضغط ، ويمكن الحصول على معدل الضرب من خلال طريقة Hitratio. إذا لم تتم إضافة هذه المعلمة ، فإن معدل الضغط الذي تم الحصول عليه بواسطة طريقة HitRatio يكون دائمًا 0.
سوف يستهلك معدل الضربة الإحصائية بالتأكيد الموارد ، لذلك لا ينصح بتشغيل بيئات الإنتاج.
حان الوقت لمشاركة الكود:
نسخة الكود كما يلي:
(وظيفة (صادرات) {
/**
* فئة نقية للميراث
* constructor
*/
دالة fn () {}
fn.prototype = elemination.prototype ؛
/**
* فئة الوالدين من خوارزمية إزالة ذاكرة التخزين المؤقت بناءً على القائمة المرتبطة
* Param MaxLength Cache سعة
* constructor
*/
القضاء على الوظيفة (MaxLength) {
this.container = {} ؛
this.length = 0 ؛
this.maxlength = maxLength || 30 ؛
this.linkhead = this.buildnode ("" ، "") ؛
this.linkhead.head = true ؛
this.linktrail = this.buildnode ("" ، "") ؛
this.linktail.tail = true ؛
this.linkhead.next = this.linktail ؛
this.linktail.prev = this.linkhead ؛
}
levination.prototype.get = دالة (مفتاح) {
رمي خطأ جديد ("يجب تجاوز هذه الطريقة!") ؛
} ؛
exmination.prototype.set = دالة (مفتاح ، قيمة) {
رمي خطأ جديد ("يجب تجاوز هذه الطريقة!") ؛
} ؛
/**
* إنشاء عقد في القائمة المرتبطة
* بيانات param البيانات الواردة في العقدة ، أي قيمة البيانات المخزولة
* مفتاح param المعرف الفريد للعقدة ، أي المفتاح المخزونة
* returns {{}}
*/
exment.prototype.buildnode = function (البيانات ، المفتاح) {
var node = {} ؛
node.data = البيانات ؛
node.key = المفتاح ؛
node.use = 0 ؛
عقدة العودة.
} ؛
/**
* تنبثق عقدة من رأس القائمة المرتبطة
* returns {*}
*/
levination.prototype.shift = function () {
var node = null ؛
if (! this.linkhead.next.tail) {
العقدة = this.linkhead.next ؛
this.linkhead.next = node.next ؛
node.next.prev = this.linkhead ؛
حذف this.container [node.key] ؛
this.length-- ؛
}
عقدة العودة.
} ؛
/**
* أدخل عقدة من رأس القائمة المرتبطة
* كائن عقدة Param
* returns {*}
*/
exmination.prototype.unshift = function (node) {
node.next = this.linkhead.next ؛
this.linkhead.next.prev = node ؛
this.linkhead.next = node ؛
node.prev = this.linkhead ؛
this.container [node.key] = node ؛
this.length ++ ؛
عقدة العودة.
} ؛
/**
* أدخل عقدة من نهاية القائمة المرتبطة
* كائن عقدة Param
* returns {*}
*/
exmination.prototype.append = function (node) {
this.linktail.prev.next = node ؛
node.prev = this.linktail.prev ؛
node.next = this.linktail ؛
this.linktail.prev = node ؛
this.container [node.key] = node ؛
this.length ++ ؛
عقدة العودة.
} ؛
/**
* تنبثق عقدة من نهاية القائمة المرتبطة
* returns {*}
*/
exmination.prototype.pop = function () {
var node = null ؛
if (! this.linktail.prev.head) {
العقدة = this.linktail.prev ؛
node.prev.next = this.linktrail ؛
this.linktail.prev = node.prev ؛
حذف this.container [node.key] ؛
this.length-- ؛
}
عقدة العودة.
} ؛
/**
* قم بإزالة العقدة المحددة من القائمة المرتبطة
* كائن عقدة Param
* returns {*}
*/
exment.prototype.remove = function (node) {
node.prev.next = node.next ؛
node.next.prev = node.prev ؛
حذف this.container [node.key] ؛
this.length-- ؛
عقدة العودة.
} ؛
/**
* المعالجة المطلوبة عند الوصول إلى العقدة هي نقل العقدة إلى رأس القائمة المرتبطة
* Param Node
*/
exmination.prototype.use = function (node) {
this.remove (العقدة) ؛
this.unshift (العقدة) ؛
} ؛
/**
* تنفيذ خوارزمية القضاء على ذاكرة التخزين المؤقت LRU
* constructor
*/
وظيفة lru () {
القضاء. apply (هذا ، الحجج) ؛
}
lru.prototype = new fn () ؛
lru.prototype.get = دالة (مفتاح) {
var node = غير محدد ؛
العقدة = this.container [key] ؛
إذا (العقدة) {
this.use (العقدة) ؛
}
عقدة العودة.
} ؛
lru.prototype.set = دالة (مفتاح ، قيمة) {
var node = this.buildNode (value ، key) ؛
if (this.length === this.maxlength) {
this.pop () ؛
}
this.unshift (العقدة) ؛
} ؛
/**
* تطبيق خوارزمية القضاء على ذاكرة التخزين المؤقت FIFO
* constructor
*/
وظيفة FIFO () {
القضاء. apply (هذا ، الحجج) ؛
}
fifo.prototype = new fn () ؛
fifo.prototype.get = دالة (مفتاح) {
var node = غير محدد ؛
العقدة = this.container [key] ؛
عقدة العودة.
} ؛
fifo.prototype.set = function (المفتاح ، القيمة) {
var node = this.buildNode (value ، key) ؛
if (this.length === this.maxlength) {
this.shift () ؛
}
this.append (العقدة) ؛
} ؛
/**
* أصبحت تغليف خوارزمية LRU و FIFO خوارزمية القضاء على ذاكرة التخزين المؤقت الجديدة
* param maxlength
* constructor
*/
عامل الوظيفة (maxLength) {
this.getCount = 0 ؛
this.hitcount = 0 ؛
this.lir = new FIFO (maxLength) ؛
this.hir = new lru (maxLength) ؛
}
Agent.Prototype.get = دالة (مفتاح) {
var node = غير محدد ؛
العقدة = this.lir.get (مفتاح) ؛
إذا (العقدة) {
node.use ++ ؛
if (node.use> = 2) {
this.lir.remove (Node) ؛
this.hir.set (node.key ، node.data) ؛
}
}آخر{
العقدة = this.hir.get (مفتاح) ؛
}
عقدة العودة.
} ؛
Agent.Prototype.getx = function (key) {
var node = غير محدد ؛
this.getCount ++ ؛
العقدة = this.get (مفتاح) ؛
إذا (العقدة) {
this.hitcount ++ ؛
}
عقدة العودة.
} ؛
Agent.Prototype.set = دالة (مفتاح ، قيمة) {
var node = null ؛
Node = this.lir.container [key] || this.hir.container [key] ؛
إذا (العقدة) {
node.data = القيمة ؛
}آخر{
this.lir.set (المفتاح ، القيمة) ؛
}
} ؛
/**
* احصل على معدل ضرب
* returns {*}
*/
Agent.Prototype.hitratio = function () {
var ret = this.getCount ؛
إذا (ret) {
ret = this.hitcount / this.getCount ؛
}
العودة
} ؛
/**
* واجهة خارجية
* Param MaxLength Cache سعة
* param dev ما إذا كانت بيئة تنمية ، ستحسب بيئة التطوير معدل الضرب ، وإلا فلن تفعل ذلك
* returns {{get ، set: function ، hitratio: function}}
*/
التصدير.
var API = Agent New (MaxLength) ؛
يعود {
الحصول على: (وظيفة () {
إذا (dev) {
وظيفة الإرجاع (المفتاح) {
var ret = api.getx (مفتاح) ؛
إرجاع ret && ret.data ؛
} ؛
}آخر{
وظيفة الإرجاع (المفتاح) {
var ret = api.get (مفتاح) ؛
إرجاع ret && ret.data ؛
} ؛
}
} ()) ،
Set: function () {
api.set.apply (API ، الحجج) ؛
} ،
Hitratio: function () {
إرجاع api.hitratio.apply (API ، الحجج) ؛
}
} ؛
} ؛
}(هذا))؛
أخيرًا ، أود أن أذكرك مرة أخرى بأن خوارزمية ذاكرة التخزين المؤقت تحتاج إلى دمجها مع سيناريوهات التطبيق الفعلية. بدون خوارزمية عالمية ، الأفضل هو الأفضل!