Memcached คือระบบแคชอ็อบเจ็กต์หน่วยความจำแบบกระจายที่พัฒนาโดย danga.com (ทีมเทคนิคที่ดำเนินการ LiveJournal) เพื่อลดภาระของฐานข้อมูลและปรับปรุงประสิทธิภาพในระบบไดนามิก เกี่ยวกับสิ่งนี้ ฉันเชื่อว่าหลายคนได้ใช้มันแล้ว จุดประสงค์ของบทความนี้คือการได้รับความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับซอฟต์แวร์โอเพ่นซอร์สที่ยอดเยี่ยมนี้ผ่านการนำไปใช้และการวิเคราะห์โค้ดของ memcached และเพื่อเพิ่มประสิทธิภาพเพิ่มเติมตามความต้องการของเรา สุดท้ายนี้ จากการวิเคราะห์ส่วนขยาย BSM_Memcache เราจะเข้าใจการใช้ memcached ให้ลึกซึ้งยิ่งขึ้น
เนื้อหาบางส่วนในบทความนี้อาจต้องใช้พื้นฐานทางคณิตศาสตร์ที่ดีกว่าเพื่อเป็นตัวช่วย
◎Memcached คืออะไร
ก่อนที่จะอธิบายประเด็นนี้อย่างละเอียด เราต้องทำความเข้าใจก่อนว่า "ไม่ใช่" คืออะไร หลายๆ คนใช้เป็นผู้ให้บริการจัดเก็บข้อมูลเช่น SharedMemory แม้ว่า memcached จะใช้วิธี "Key=>Value" เดียวกันในการจัดระเบียบข้อมูล แต่ก็แตกต่างจากแคชในเครื่อง เช่น หน่วยความจำที่ใช้ร่วมกัน และ APC มีการกระจาย Memcached ซึ่งหมายความว่าไม่ใช่ในเครื่อง ให้บริการเสร็จสมบูรณ์ตามการเชื่อมต่อเครือข่าย (แน่นอนว่าสามารถใช้ localhost ได้) เป็นโปรแกรมที่ไม่ขึ้นอยู่กับแอปพลิเคชันหรือกระบวนการ daemon (โหมด Daemon)
Memcached ใช้ไลบรารี libevent เพื่อใช้บริการเชื่อมต่อเครือข่าย และในทางทฤษฎีสามารถจัดการการเชื่อมต่อได้ไม่จำกัดจำนวน อย่างไรก็ตาม ต่างจาก Apache ตรงที่มักมุ่งเน้นไปที่การเชื่อมต่อที่เสถียรอย่างต่อเนื่อง ดังนั้น ความสามารถในการทำงานพร้อมกันจริงจึงมีจำกัด ภายใต้สถานการณ์แบบอนุรักษ์นิยม จำนวนการเชื่อมต่อพร้อมกันสูงสุดสำหรับ memcached คือ 200 ซึ่งสัมพันธ์กับความสามารถของเธรด Linux สามารถปรับค่านี้ได้ สำหรับข้อมูลเกี่ยวกับ libevent โปรดดูเอกสารที่เกี่ยวข้อง การใช้หน่วยความจำ Memcached ก็แตกต่างจาก APC เช่นกัน APC ขึ้นอยู่กับหน่วยความจำที่ใช้ร่วมกันและ MMAP Memcachd มีอัลกอริธึมการจัดสรรหน่วยความจำและวิธีการจัดการของตัวเอง ซึ่งไม่เกี่ยวข้องกับหน่วยความจำที่ใช้ร่วมกัน และไม่มีข้อจำกัดเกี่ยวกับหน่วยความจำที่ใช้ร่วมกัน ต้องการพื้นที่มากขึ้น สามารถเพิ่มจำนวนกระบวนการได้
Memcached เหมาะสำหรับโอกาสใดบ้าง
ในหลายกรณี memcached ถูกนำไปใช้ในทางที่ผิด ซึ่งแน่นอนว่านำไปสู่การร้องเรียนเกี่ยวกับเรื่องนี้อย่างหลีกเลี่ยงไม่ได้ ฉันมักจะเห็นคนโพสต์ในฟอรั่มคล้ายกับ "วิธีปรับปรุงประสิทธิภาพ" และคำตอบคือ "ใช้ memcached" ส่วนวิธีใช้ ใช้ที่ไหน และใช้เพื่ออะไร ไม่มีประโยค Memcached ไม่ใช่ยาครอบจักรวาลและไม่เหมาะกับทุกสถานการณ์
Memcached เป็นระบบแคชออบเจ็กต์หน่วยความจำแบบ "กระจาย" กล่าวคือ สำหรับแอปพลิเคชันที่ไม่จำเป็นต้อง "กระจาย" ไม่จำเป็นต้องแชร์ หรือมีขนาดเล็กพอที่จะมีเซิร์ฟเวอร์เพียงเครื่องเดียว memcached จะไม่ นำมาซึ่งประโยชน์ใดๆ ก็ตาม ในทางกลับกัน ยังทำให้ประสิทธิภาพของระบบช้าลงเนื่องจากการเชื่อมต่อเครือข่ายยังต้องใช้ทรัพยากร แม้แต่การเชื่อมต่อภายในระบบ UNIX ข้อมูลการทดสอบก่อนหน้านี้ของฉันแสดงให้เห็นว่าความเร็วในการอ่านและเขียนภายใน memcached นั้นช้ากว่าอาร์เรย์หน่วยความจำ PHP โดยตรงหลายสิบเท่า ในขณะที่ APC และวิธีการหน่วยความจำที่ใช้ร่วมกันนั้นคล้ายคลึงกับอาร์เรย์โดยตรง จะเห็นได้ว่าหากเป็นเพียงแคชระดับท้องถิ่น การใช้ memcached นั้นไม่ประหยัดมากนัก
Memcached มักใช้เป็นแคชส่วนหน้าของฐานข้อมูล เนื่องจากมีการแยกวิเคราะห์ SQL การทำงานของดิสก์ และค่าใช้จ่ายอื่นๆ น้อยกว่าฐานข้อมูลมาก และใช้หน่วยความจำในการจัดการข้อมูล จึงให้ประสิทธิภาพที่ดีกว่าการอ่านฐานข้อมูลโดยตรง ในระบบขนาดใหญ่ การเข้าถึงข้อมูลเดียวกันนั้นทำได้ยากมาก บ่อยครั้ง memcached สามารถลดความกดดันของฐานข้อมูลและปรับปรุงประสิทธิภาพการดำเนินการของระบบได้อย่างมาก นอกจากนี้ Memcached มักถูกใช้เป็นสื่อจัดเก็บข้อมูลสำหรับการแชร์ข้อมูลระหว่างเซิร์ฟเวอร์ ตัวอย่างเช่น ข้อมูลที่บันทึกสถานะการลงชื่อเข้าใช้ครั้งเดียวของระบบในระบบ SSO สามารถบันทึกใน Memcached และแบ่งปันโดยแอปพลิเคชันหลายตัว
ควรสังเกตว่า memcached ใช้หน่วยความจำเพื่อจัดการข้อมูล ดังนั้นจึงมีความผันผวน เมื่อเซิร์ฟเวอร์รีสตาร์ทหรือกระบวนการ memcached สิ้นสุดลง ข้อมูลจะสูญหาย ดังนั้นจึงไม่สามารถใช้ memcached เพื่อคงข้อมูลได้ หลายๆ คนเข้าใจผิดว่าประสิทธิภาพของ memcached นั้นดีมาก พอๆ กับการเปรียบเทียบระหว่างหน่วยความจำกับฮาร์ดดิสก์ จริงๆ แล้ว memcached จะไม่ได้รับการปรับปรุงความเร็วในการอ่านและเขียนนับร้อยหรือนับพันโดยใช้หน่วยความจำ การเชื่อมต่อซึ่งเกี่ยวข้องกับการใช้หน่วยความจำ เมื่อเปรียบเทียบกับระบบฐานข้อมูลดิสก์ ข้อดีคือ "เบา" มาก เนื่องจากไม่มีค่าใช้จ่ายมากเกินไปและวิธีการอ่านและเขียนโดยตรง จึงสามารถรองรับข้อมูลจำนวนมากได้อย่างง่ายดาย ของการแลกเปลี่ยนข้อมูล ดังนั้นจึงมักจะมีแบนด์วิดธ์เครือข่ายกิกะบิตสองรายการ ซึ่งทั้งหมดถูกโหลดอย่างสมบูรณ์ และกระบวนการ memcached เองก็ไม่ได้ใช้ทรัพยากร CPU มากนัก
○วิธีการทำงานของ Memcached
ในส่วนต่อไปนี้ เป็นการดีที่สุดสำหรับผู้อ่านที่จะเตรียมสำเนาซอร์สโค้ดของ memcached
Memcached เป็นโปรแกรมบริการเครือข่ายแบบดั้งเดิม หากใช้พารามิเตอร์ -d เมื่อเริ่มต้น จะถูกดำเนินการเป็นกระบวนการ daemon การสร้างกระบวนการ daemon เสร็จสิ้นโดย daemon.c โปรแกรมนี้มีเพียงฟังก์ชัน daemon เดียวเท่านั้น ซึ่งง่ายมาก (หากไม่มีคำสั่งพิเศษใดๆ โค้ดจะต้องเป็นไปตาม 1.2.1):
รหัส:[คัดลอกไปยังคลิปบอร์ด]#include <fcntl.h>
#รวม <stdlib.h>
#include <unistd.h>
int
ภูต(nochdir, noclose)
อินท์ nochdir, noclose;
-
int fd;
สวิตช์ (ส้อม ()) {
กรณีที่ 1:
กลับ (-1);
กรณีที่ 0:
หยุดพัก;
ค่าเริ่มต้น:
_ออก(0);
}
ถ้า (setsid() == -1)
กลับ (-1);
ถ้า (!nochdir)
(เป็นโมฆะ)chdir("/");
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
(เป็นโมฆะ) dup2 (fd, STDIN_FILENO);
(เป็นโมฆะ) dup2 (fd, STDOUT_FILENO);
(เป็นโมฆะ) dup2 (fd, STDERR_FILENO);
ถ้า (fd > STDERR_FILENO)
(เป็นโมฆะ) ปิด (fd);
-
กลับ (0);
-
หลังจากที่ฟังก์ชันนี้แยกกระบวนการทั้งหมด กระบวนการหลักจะออก จากนั้นย้ายตำแหน่ง STDIN, STDOUT และ STDERR ไปยังอุปกรณ์ว่าง และสร้าง daemon สำเร็จแล้ว
กระบวนการเริ่มต้นของ Memcached นั้นในฟังก์ชันหลักของ memcached.c มีดังนี้:
1. เรียก settings_init() เพื่อตั้งค่าพารามิเตอร์การเริ่มต้น
2. อ่านพารามิเตอร์จากคำสั่งเริ่มต้นเพื่อตั้งค่าการตั้งค่า
3. ตั้งค่าพารามิเตอร์ LIMIT
4. เริ่มการตรวจสอบซ็อกเก็ตเครือข่าย (หากไม่มีเส้นทางซ็อกเก็ต) (รองรับโหมด UDP หลังจาก 1.2)
5. ตรวจสอบข้อมูลประจำตัวผู้ใช้ (Memcached ไม่อนุญาตให้เริ่มต้นข้อมูลระบุตัวตนของรูท)
6. หากมี socketpath ให้เปิดการเชื่อมต่อท้องถิ่นของ UNIX (Sock Pipe)
7. หากเริ่มต้นในโหมด -d ให้สร้างกระบวนการ daemon (เรียกใช้ฟังก์ชัน daemon ดังด้านบน)
8. เริ่มต้นรายการ เหตุการณ์ ข้อมูลสถานะ แฮช การเชื่อมต่อ แผ่นคอนกรีต
9. หากการจัดการมีผลในการตั้งค่า ให้สร้างอาร์เรย์บัคเก็ต
10. ตรวจสอบว่าหน้าหน่วยความจำจำเป็นต้องล็อคหรือไม่
11. เริ่มต้นสัญญาณ การเชื่อมต่อ ลบคิว
12. หากอยู่ในโหมด daemon ให้ประมวลผล ID กระบวนการ
13. เหตุการณ์เริ่มต้น กระบวนการเริ่มต้นสิ้นสุด และฟังก์ชันหลักเข้าสู่ลูป
ในโหมด daemon เนื่องจาก stderr ถูกส่งไปยังหลุมดำ จึงไม่มีข้อความแสดงข้อผิดพลาดที่มองเห็นได้ในระหว่างการดำเนินการจะถูกป้อนกลับ
ฟังก์ชันลูปหลักของ memcached.c คือ drive_machine พารามิเตอร์ขาเข้าคือตัวชี้โครงสร้างที่ชี้ไปยังการเชื่อมต่อปัจจุบัน และการดำเนินการจะพิจารณาจากสถานะของสมาชิกสถานะ
Memcached ใช้ชุดโปรโตคอลที่กำหนดเองเพื่อทำการแลกเปลี่ยนข้อมูลให้เสร็จสมบูรณ์ สามารถอ้างอิงเอกสารโปรโตคอลได้ที่: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
ใน API สัญลักษณ์ขึ้นบรรทัดใหม่ รวมเป็นหนึ่งเดียวเป็น rn
○วิธีการจัดการหน่วยความจำของ Memcached
Memcached มีวิธีการจัดการหน่วยความจำที่เป็นเอกลักษณ์มาก เพื่อปรับปรุงประสิทธิภาพ จึงใช้วิธีก่อนแอปพลิเคชันและการจัดกลุ่มเพื่อจัดการพื้นที่หน่วยความจำ แทนการใช้ malloc ทุกครั้งที่จำเป็นต้องเขียนข้อมูล . ปล่อยตัวชี้ให้ว่างเมื่อลบข้อมูล Memcached ใช้วิธีการจัดระเบียบแบบ slab->chunk เพื่อจัดการหน่วยความจำ
มีความแตกต่างบางประการในอัลกอริธึมการแบ่งพื้นที่แผ่นพื้นใน slabs.c ใน 1.1 และ 1.2 ซึ่งจะมีการแนะนำแยกกันในภายหลัง
Slab สามารถเข้าใจได้ว่าเป็นบล็อกหน่วยความจำ Slab เป็นหน่วยที่เล็กที่สุดสำหรับ memcached เพื่อนำไปใช้กับหน่วยความจำในครั้งเดียว ใน memcached ขนาดเริ่มต้นของ slab คือ 1048576 ไบต์ (1MB) ดังนั้น memcached จะใช้หน่วยความจำทั้งหมด แต่ละแผ่นแบ่งออกเป็นหลายชิ้น และแต่ละชิ้นจะจัดเก็บรายการหนึ่งรายการด้วย โครงสร้างรายการ คีย์ และค่า (โปรดทราบว่าค่าใน memcached เป็นเพียงสตริง) แผ่นคอนกรีตสร้างรายการที่เชื่อมโยงตาม ID ของตนเอง และรายการที่เชื่อมโยงเหล่านี้จะถูกแขวนไว้ในอาร์เรย์ slabclass ตาม ID โครงสร้างทั้งหมดดูเหมือนอาร์เรย์สองมิติเล็กน้อย ความยาวของคลาสพื้นคือ 21 ใน 1.1 และ 200 ใน 1.2
แผ่นคอนกรีตมีขนาดเริ่มต้นซึ่งก็คือ 1 ไบต์ใน 1.1 และ 80 ไบต์ใน 1.2 มีค่าตัวประกอบใน 1.2 ซึ่งเป็นค่าเริ่มต้นที่ 1.25
ใน 1.1 ขนาดก้อนจะแสดงเป็นขนาดเริ่มต้น * 2^n คือ classid นั่นคือ: แผ่นคอนกรีตที่มี id 0 มีขนาดชิ้น 1 ไบต์ แผ่นคอนกรีตที่มี id 1 มีขนาดชิ้น 2 ไบต์ แผ่นคอนกรีตที่มี id 2 มีขนาดชิ้น 4 ไบต์... แผ่นคอนกรีตที่มี id 20 มีขนาดชิ้น 4 ไบต์ ขนาดคือ 1MB ซึ่งหมายความว่ามีเพียงชิ้นเดียวในแผ่นคอนกรีตที่มี ID 20:
รหัส:[คัดลอกไปยังคลิปบอร์ด]เป็นโมฆะ slabs_init(จำกัด size_t) {
ฉัน;
ขนาด int=1;
mem_limit = ขีดจำกัด;
สำหรับ (i=0; i<=POWER_LARGEST; i++, ขนาด*=2) {
slabclass[i].size = ขนาด;
slabclass[i].perslab = POWER_BLOCK / ขนาด;
แผ่นคลาส[i].สล็อต = 0;
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
แผ่นพื้น [i] .end_page_ptr = 0;
แผ่นพื้น [i] .end_page_free = 0;
slabclass[i].slab_list = 0;
แผ่นพื้น [i] .list_size = 0;
แผ่นพื้น [i] .killing = 0;
}
/* สำหรับชุดทดสอบ: แกล้งทำเป็นว่าเราได้ malloc'd ไปแล้วเท่าไร */
-
ถ่าน *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
ถ้า (t_initial_malloc) {
mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC"));
-
}
/* จัดสรรแผ่นพื้นล่วงหน้าตามค่าเริ่มต้น เว้นแต่ตัวแปรสภาพแวดล้อม
สำหรับการทดสอบถูกตั้งค่าเป็นค่าที่ไม่ใช่ศูนย์ */
-
ถ่าน *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
ถ้า (!pre_alloc || atoi(pre_alloc)) {
slabs_preallocate (จำกัด / POWER_BLOCK);
-
-
-
ใน 1.2 ขนาดก้อนจะแสดงเป็นขนาดเริ่มต้น * f^n, f คือปัจจัย ซึ่งกำหนดไว้ใน memcached.c และ n คือ classid ในเวลาเดียวกัน ไม่จำเป็นต้องเตรียมใช้งานหัวทั้งหมด 201 อันเนื่องจากปัจจัย เป็นตัวแปรและการเริ่มต้นจะวนซ้ำไปที่ขนาดที่คำนวณได้ถึงครึ่งหนึ่งของขนาดแผ่นคอนกรีตและเริ่มต้นจาก id1 นั่นคือ: แผ่นพื้นที่มี id 1 ขนาดแต่ละชิ้นคือ 80 ไบต์ แผ่นพื้นที่มี id 2 ขนาดแต่ละชิ้นคือ 80* f, id คือ 3 แผ่น แต่ละขนาดคือ 80*f^2 และขนาดการเริ่มต้นมีค่าแก้ไข CHUNK_ALIGN_BYTES เพื่อให้แน่ใจว่าการจัดตำแหน่ง n-ไบต์ (รับประกันว่าผลลัพธ์จะเป็นผลคูณสำคัญของ CHUNK_ALIGN_BYTES) ด้วยวิธีนี้ ภายใต้สถานการณ์มาตรฐาน memcached1.2 จะถูกเตรียมใช้งานเป็น id40 ขนาดของแต่ละชิ้นในแผ่นคอนกรีตนี้คือ 504692 และมีสองชิ้นในแต่ละแผ่น สุดท้ายนี้ ฟังก์ชัน slab_init จะเพิ่ม id41 ต่อท้าย ซึ่งเป็นทั้งบล็อก กล่าวคือ มีเพียงชิ้นเดียวขนาด 1MB ในแผ่นคอนกรีตนี้:
รหัส:[คัดลอกไปยังคลิปบอร์ด]เป็นโมฆะ slabs_init(จำกัด size_t, ปัจจัยสองเท่า) {
int i = POWER_SMALLEST - 1;
int size ที่ไม่ได้ลงนาม = sizeof(item) + settings.chunk_size;
/* Factor ของ 2.0 หมายถึงใช้พฤติกรรม memcached เริ่มต้น */
ถ้า (ปัจจัย == 2.0 && ขนาด < 128)
ขนาด = 128;
mem_limit = ขีดจำกัด;
memset (slabclass, 0, ขนาดของ (slabclass));
ในขณะที่ (++ i < POWER_LARGEST && ขนาด <= POWER_BLOCK / 2) {
/* ตรวจสอบให้แน่ใจว่ารายการต่างๆ อยู่ในแนว n-byte เสมอ */
ถ้า (ขนาด % CHUNK_ALIGN_BYTES)
ขนาด += CHUNK_ALIGN_BYTES - (ขนาด % CHUNK_ALIGN_BYTES)
;
slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;
ขนาด *= ปัจจัย;
ถ้า (settings.verbose > 1) {
fprintf(stderr, "คลาสพื้น %3d: ขนาดก้อน %6d perslab %5dn",
ฉัน, slabclass[i].size, slabclass[i].perslab);
-
}
อำนาจ_ใหญ่ที่สุด = ฉัน;
slabclass [power_largest].size = POWER_BLOCK;
slabclass[power_largest].perslab = 1;
/* สำหรับชุดทดสอบ: แกล้งทำเป็นว่าเราได้ malloc'd ไปแล้วเท่าไร */
-
ถ่าน *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
ถ้า (t_initial_malloc) {
mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC"));
}
}
#ifndef DONT_PREALLOC_SLABS
-
ถ่าน *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
ถ้า (!pre_alloc || atoi(pre_alloc)) {
slabs_preallocate (จำกัด / POWER_BLOCK);
-
-
#เอ็นดิฟ
-
ดังที่เห็นได้จากด้านบน การจัดสรรหน่วยความจำของ memcached นั้นซ้ำซ้อน เมื่อแผ่นคอนกรีตไม่สามารถหารด้วยขนาดก้อนที่เป็นเจ้าของ พื้นที่ที่เหลือที่ส่วนท้ายของแผ่นคอนกรีตจะถูกละทิ้ง ตัวอย่างเช่น ใน id40 จะมีสองชิ้นครอบครอง 1009384 ไบต์ แผ่นนี้มีเนื้อที่ทั้งหมด 1MB ดังนั้น 39192 ไบต์จึงสูญเปล่า
Memcached ใช้วิธีการนี้เพื่อจัดสรรหน่วยความจำเพื่อค้นหา classid ของ slab อย่างรวดเร็วผ่านความยาวของรายการ ซึ่งจะคล้ายกับแฮชเล็กน้อย เนื่องจากสามารถคำนวณความยาวของรายการได้ ตัวอย่างเช่น ความยาวของรายการคือ 300 ไบต์ คุณสามารถรับได้ว่าควรเก็บไว้ในสแลบของ id7 เนื่องจากตามวิธีการคำนวณข้างต้น ขนาดก้อนของ id6 คือ 252 ไบต์ ขนาดก้อนของ id7 คือ 316 ไบต์ และขนาดก้อนของ id8 คือ 396 ไบต์ ซึ่งหมายความว่ารายการทั้งหมด 252 ถึง 316 ไบต์ทั้งหมดควรเก็บไว้ใน id7 ในทำนองเดียวกัน ใน 1.1 ยังสามารถคำนวณได้ว่าอยู่ระหว่าง 256 ถึง 512 และควรวางไว้ใน id9 โดยมี chunk_size เป็น 512 (ระบบ 32 บิต)
เมื่อเตรียมใช้งาน Memcached แผ่นคอนกรีตจะถูกเตรียมใช้งาน (ดังที่คุณเห็นก่อนหน้านี้ slabs_init() จะถูกเรียกในฟังก์ชันหลัก) มันจะตรวจสอบค่าคงที่ DONT_PREALLOC_SLABS ใน slabs_init() หากไม่ได้กำหนดไว้ แสดงว่าแผ่นคอนกรีตถูกเตรียมใช้งานโดยใช้หน่วยความจำที่จัดสรรไว้ล่วงหน้า เพื่อให้แผ่นคอนกรีตถูกสร้างขึ้นสำหรับแต่ละ ID จากคลาสแผ่นพื้นทั้งหมดที่ถูกกำหนดไว้ ซึ่งหมายความว่า 1.2 จะจัดสรรพื้นที่สแลบ 41MB หลังจากเริ่มกระบวนการในสภาพแวดล้อมเริ่มต้น ในระหว่างกระบวนการนี้ ความซ้ำซ้อนของหน่วยความจำที่สองของ memcached เกิดขึ้น เนื่องจากอาจเป็นไปได้ว่า id ไม่ได้ถูกใช้เลย แต่ก็เป็น A เช่นกัน แผ่นคอนกรีตจะถูกนำไปใช้ตามค่าเริ่มต้น และแต่ละแผ่นจะใช้หน่วยความจำ 1MB
เมื่อใช้แผ่นพื้นจนหมด และจำเป็นต้องแทรกรายการใหม่ด้วย ID นี้ แผ่นพื้นจะถูกนำไปใช้กับแผ่นใหม่อีกครั้ง slab ซึ่งเป็น ID ที่สอดคล้องกัน รายการที่เชื่อมโยงของ slab จะเพิ่มขึ้นแบบทวีคูณ ในฟังก์ชัน grow_slab_list ความยาวของสายโซ่นี้จะเปลี่ยนจาก 1 เป็น 2 จาก 2 เป็น 4 จาก 4 เป็น 8...:
รหัส: [คัดลอกไปยังคลิปบอร์ด] static int grow_slab_list (id int ที่ไม่ได้ลงนาม) {
slabclass_t *p = &slabclass[id];
ถ้า (p->แผ่นคอนกรีต == p->list_size) {
size_t new_size = p->list_size ? p->list_size * 2 : 16;
เป็นโมฆะ *new_list = realloc(p->slab_list, new_size*sizeof(void*));
ถ้า (new_list == 0) ส่งคืน 0;
p->list_size = new_size;
p->slab_list = new_list;
-
กลับ 1;
-
เมื่อค้นหารายการ ฟังก์ชัน slabs_clsid จะถูกใช้ พารามิเตอร์ขาเข้าคือขนาดรายการ และค่าที่ส่งคืนคือ classid จากกระบวนการนี้ จะเห็นได้ว่าความซ้ำซ้อนของหน่วยความจำครั้งที่ 3 ของ memcached เกิดขึ้นในกระบวนการบันทึกรายการ สินค้าจะเล็กกว่าหรือเท่ากับขนาดชิ้นเสมอ เมื่อสินค้ามีขนาดเล็กกว่าขนาดชิ้น จะเปลืองพื้นที่อีกครั้ง
อัลกอริธึม NewHash ของ Memcached
พื้นที่จัดเก็บรายการของ Memcached ขึ้นอยู่กับตารางแฮชขนาดใหญ่ ที่อยู่จริงของมันคือออฟเซ็ตก้อนในแผ่นพื้น แต่การวางตำแหน่งขึ้นอยู่กับผลลัพธ์ของการแฮชคีย์ ซึ่งพบได้ใน primary_hashtable การดำเนินการแฮชและรายการทั้งหมดถูกกำหนดไว้ใน assoc.c และ items.c
Memcached ใช้อัลกอริทึมที่เรียกว่า NewHash ซึ่งมีประสิทธิภาพและประสิทธิผลมาก มีความแตกต่างบางประการระหว่าง NewHash ใน 1.1 และ 1.2 วิธีการนำไปใช้งานหลักยังคงเหมือนเดิม ฟังก์ชันแฮชของ 1.2 ได้รับการจัดระเบียบและปรับให้เหมาะสม และความสามารถในการปรับตัวก็ดีขึ้น
การอ้างอิงต้นแบบ NewHash: http://burtleburtle.net/bob/hash/evahash.html นักคณิตศาสตร์มักจะแปลกนิดหน่อย ฮ่าๆ~
เพื่ออำนวยความสะดวกในการแปลง ข้อมูลสองประเภทคือ u4 และ u1 ถูกกำหนดไว้ u4 เป็นจำนวนเต็มยาวที่ไม่ได้ลงนาม และ u1 เป็นถ่านที่ไม่ได้ลงนาม (0-255)
สำหรับโค้ดเฉพาะ โปรดดูแพ็คเกจซอร์สโค้ด 1.1 และ 1.2
ใส่ใจกับความยาวแฮชที่นี่ นอกจากนี้ยังมีความแตกต่างระหว่าง 1.1 และ 1.2 ใน 1.1 ค่าคงที่ HASHPOWER ถูกกำหนดเป็น 20 และความยาวของตารางแฮชคือแฮชขนาด (HASHPOWER) ซึ่งก็คือ 4MB (แฮชขนาดคือมาโคร ซึ่งระบุ 1 นั้นถูกเลื่อนไปทางขวาด้วย n บิต) ใน 1.2 มันคือตัวแปร 16 นั่นคือความยาวของตารางแฮชคือ 65536:
รหัส:[คัดลอกไปยังคลิปบอร์ด]typedef unsigned long int ub4; /* ปริมาณ 4 ไบต์ที่ไม่ได้ลงนาม */
typedef ถ่านที่ไม่ได้ลงนาม ub1; /* ปริมาณ 1 ไบต์ที่ไม่ได้ลงนาม */
#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)
ใน assoc_init() primary_hashtable จะถูกเตรียมใช้งาน การดำเนินการแฮชที่เกี่ยวข้อง ได้แก่: assoc_find(), assoc_expand(), assoc_move_next_bucket(), assoc_insert(), assoc_delete() ซึ่งสอดคล้องกับการดำเนินการอ่านและเขียนของรายการ ในหมู่พวกเขา assoc_find() เป็นฟังก์ชันที่ค้นหาที่อยู่ของรายการที่เกี่ยวข้องตามความยาวของคีย์และคีย์ (โปรดทราบว่าในภาษา C หลายครั้งที่ความยาวสตริงและสตริงถูกส่งโดยตรงในเวลาเดียวกัน แทนที่จะทำ strlen ภายในฟังก์ชัน ) และสิ่งที่ส่งคืนคือตัวชี้โครงสร้างรายการ ที่อยู่ข้อมูลจะอยู่บนก้อนข้อมูลในแผ่นพื้น
items.c เป็นโปรแกรมการดำเนินการสำหรับรายการข้อมูล แต่ละรายการที่สมบูรณ์ประกอบด้วยหลายส่วน ซึ่งกำหนดไว้ใน item_make_header() เป็น:
คีย์: คีย์
nkey: ความยาวคีย์
flags: ธงที่ผู้ใช้กำหนด (อันที่จริง ธงนี้ไม่ได้เปิดใช้งานใน memcached)
nbytes: ความยาวของค่า (รวมถึงสัญลักษณ์ขึ้นบรรทัดใหม่ rn)
คำต่อท้าย: บัฟเฟอร์ต่อท้าย
nsuffix:
ความยาวต่อท้ายของไอเท็มที่สมบูรณ์คือความยาวคีย์ + ความยาวของค่า + ความยาวต่อท้าย + ขนาดโครงสร้างไอเท็ม (32 ไบต์) การดำเนินการของไอเท็มจะขึ้นอยู่กับความยาวนี้เพื่อคำนวณคลาสซิดของแผ่นคอนกรีต
แต่ละที่เก็บข้อมูลในแฮชเทเบิลจะถูกแขวนไว้ด้วยรายการเชื่อมโยงคู่ ระหว่าง item_init() อาร์เรย์ทั้งสามของส่วนหัว ส่วนท้าย และขนาดจะถูกเตรียมใช้งานเป็น 0 ขนาดของอาร์เรย์ทั้งสามนี้คือ LARGEST_ID คงที่ (ค่าเริ่มต้นคือ 255 ค่านี้ต้องมีการแก้ไขด้วยแฟกเตอร์) แต่ละครั้งที่เรียก item_assoc() จะพยายามรับชิ้นส่วนที่ว่างจากแผ่นคอนกรีตก่อน หากไม่มีชิ้นส่วนที่พร้อมใช้งาน ระบบจะสแกนรายการที่เชื่อมโยง 50 ครั้งเพื่อให้ได้ชิ้นส่วนที่มีอยู่ เริ่มต้นโดย LRU รายการ ยกเลิกการเชื่อมโยง จากนั้นแทรกรายการที่จะแทรกลงในรายการที่เชื่อมโยง
ให้ความสนใจกับสมาชิก refcount ของรายการ หลังจากยกเลิกการเชื่อมโยงแล้ว รายการดังกล่าวจะถูกลบออกจากรายการที่เชื่อมโยงเท่านั้น โดยจะไม่ถูกปล่อยไว้ในคิวการลบ (ฟังก์ชัน item_unlink_q())
รายการสอดคล้องกับการดำเนินการอ่านและเขียนบางอย่าง รวมถึงการลบ อัปเดต และแทนที่ แน่นอนว่าสิ่งที่สำคัญที่สุดคือการจัดสรร
คุณสมบัติอีกอย่างหนึ่งของรายการคือมีเวลาหมดอายุ ซึ่งเป็นคุณลักษณะที่มีประโยชน์มากของ memcached แอปพลิเคชันจำนวนมากอาศัยการหมดอายุของรายการของ memcached เช่น พื้นที่จัดเก็บเซสชัน การล็อกการดำเนินการ เป็นต้น ฟังก์ชัน item_flush_expired() จะสแกนรายการในตารางและดำเนินการยกเลิกการเชื่อมโยงกับรายการที่หมดอายุ แน่นอนว่านี่เป็นเพียงการดำเนินการรีไซเคิล ที่จริงแล้ว จำเป็นต้องมีการพิจารณาเวลาด้วยเมื่อได้รับ:
CODE:[คัดลอกไปยังคลิปบอร์ด]/* หมดอายุรายการล่าสุดกว่าการตั้งค่าเก่าที่สุด */
เป็นโมฆะ item_flush_expired() {
ฉัน;
รายการ *iter, *ถัดไป;
ถ้า (! settings.oldest_live)
กลับ;
สำหรับ (i = 0; i <LARGEST_ID; i++) {
/* LRU จัดเรียงตามลำดับเวลาที่ลดลง และการประทับเวลาของรายการ
* ไม่เคยใหม่กว่าเวลาเข้าถึงครั้งล่าสุดดังนั้นเราจึงต้องเดินเท่านั้น
* ย้อนกลับจนกว่าเราจะพบรายการที่เก่ากว่าเวลาที่เก่าที่สุด_live
* การตรวจสอบที่เก่าแก่ที่สุดจะหมดอายุรายการที่เหลือโดยอัตโนมัติ
-
สำหรับ (iter = หัว [i]; iter != NULL; iter = ถัดไป) {
ถ้า (iter -> เวลา >= settings.oldest_live) {
ถัดไป = iter->ถัดไป;
ถ้า ((iter->it_flags & ITEM_SLABBED) == 0) {
item_unlink(iter);
-
} อื่น {
/* เราไปถึงรายการเก่ารายการแรกแล้ว ไปต่อที่คิวถัดไป */
หยุดพัก;
-
-
-
-
CODE:[คัดลอกไปยังคลิปบอร์ด]/* wrapper รอบ assoc_find ซึ่งใช้ตรรกะการหมดอายุ/การลบแบบขี้เกียจ */
รายการ *get_item_notedeleted(ถ่าน *คีย์, size_t nkey, int *delete_locked) {
รายการ *it = assoc_find(key, nkey);
ถ้า (delete_locked) *delete_locked = 0;
ถ้า (มัน && (มัน->it_flags & ITEM_DELETED)) {
/* มันถูกตั้งค่าสถานะเป็นลบล็อค มาดูกันว่าเงื่อนไขนั้นคืออะไร
เลยกำหนดชำระไปแล้ว และตัวตั้งเวลา 5 วินาทีก็ยังไม่ครบกำหนด
ได้แล้ว... */
ถ้า (!item_delete_lock_over(มัน)) {
ถ้า (delete_locked) *delete_locked = 1;
มัน = 0;
-
-
ถ้า (มัน && settings.oldest_live && settings.oldest_live <= current_time &&
มัน -> เวลา <= settings.oldest_live) {
item_unlink(มัน);
มัน = 0;
-
ถ้า (มัน && มัน->Exptime && มัน->Exptime <= current_time) {
item_unlink(มัน);
มัน = 0;
-
ส่งคืน;
-
วิธีการจัดการหน่วยความจำของ Memcached นั้นซับซ้อนและมีประสิทธิภาพมาก โดยจะช่วยลดจำนวนการจัดสรรหน่วยความจำระบบโดยตรง ลดโอเวอร์เฮดของฟังก์ชัน และความน่าจะเป็นของการกระจายตัวของหน่วยความจำ แม้ว่าวิธีการนี้จะทำให้เกิดการสิ้นเปลืองที่ซ้ำซ้อน แต่การสิ้นเปลืองนี้ก็เป็นเรื่องเล็กน้อยในระบบขนาดใหญ่ การใช้งาน
วิธีการคำนวณพารามิเตอร์ทางทฤษฎีของ Memcached
มีหลายพารามิเตอร์ที่ส่งผลต่อการทำงานของ memcached:
ค่าคงที่ REALTIME_MAXDELTA 60*60*24*30
เวลาหมดอายุสูงสุด 30 วัน
freetotal (=200) ใน conn_init()
จำนวนสูงสุดของการเชื่อมต่อพร้อม
กันคงที่ KEY_MAX_LENGTH 250
ความยาวคีย์สูงสุด
ปัจจัย (=1.25)
factor จะส่งผลต่อขนาดขั้นตอนของ chunk
settings.maxconns (=1024)
การเชื่อมต่อแบบซอฟต์สูงสุด
.chunk_size (=48)
ความยาวคีย์+ค่าโดยประมาณแบบอนุรักษ์นิยม ใช้เพื่อสร้างความยาวชิ้น (1.2) ใน id1 ความยาวกลุ่มของ id1 เท่ากับค่านี้บวกกับความยาวของโครงสร้างรายการ (32) ซึ่งเป็นค่าเริ่มต้น 80 ไบต์
ค่าคงที่ POWER_SMALLEST 1
คลาสซิดขั้นต่ำ (1.2)
ค่าคงที่ POWER_LARGEST 200
ที่คลาสสูงสุด (1.2)
POWER_BLOCK 1048576
ขนาดแผ่นพื้นเริ่มต้น
คงที่ CHUNK_ALIGN_BYTES (ขนาดของ (โมฆะ *))
ตรวจสอบให้แน่ใจว่าขนาดก้อนเป็นจำนวนเต็มทวีคูณของค่านี้เพื่อป้องกันการอยู่นอกขอบเขต (ความยาวของโมฆะ * จะแตกต่างกันในระบบที่แตกต่างกัน ซึ่งเป็น 4 ในระบบ 32 บิตมาตรฐาน)
ค่าคงที่ ITEM_UPDATE_INTERVAL 60
ที่ช่วงการรีเฟรชคิว
LARGEST_ID 255
จำนวนรายการสูงสุดในรายการที่เชื่อมโยง (ค่านี้ต้องไม่เล็กกว่าคลาสซิดที่ใหญ่ที่สุด)
แฮชพาวเวอร์แบบแปรผัน (HASHPOWER ค่าคงที่ใน 1.1)
การกำหนดขนาดของ hashtable
ขึ้นอยู่กับการตั้งค่าเนื้อหาและพารามิเตอร์ที่แนะนำข้างต้น ผลลัพธ์บางอย่างสามารถคำนวณได้:
1. ไม่มีขีดจำกัดบนของซอฟต์แวร์สำหรับจำนวนรายการที่สามารถบันทึกใน memcached ผิด.
2. สมมติว่าอัลกอริทึม NewHash มีการชนกันที่สม่ำเสมอ จำนวนรอบในการค้นหารายการคือจำนวนรายการทั้งหมดหารด้วยขนาดแฮช (กำหนดโดย hashpower) ซึ่งเป็นเส้นตรง
3. Memcached จำกัดรายการสูงสุดที่ยอมรับได้ที่ 1MB และข้อมูลที่ใหญ่กว่า 1MB จะถูกละเว้น
4. การใช้พื้นที่ของ Memcached มีความสัมพันธ์ที่ดีกับคุณลักษณะของข้อมูล และยังเกี่ยวข้องกับค่าคงที่ DONT_PREALLOC_SLABS อีกด้วย ในกรณีที่เลวร้ายที่สุด แผ่นคอนกรีต 198 แผ่นจะสูญเปล่า (รายการทั้งหมดรวมอยู่ในแผ่นเดียว และ 199 รหัสทั้งหมดได้รับการจัดสรรอย่างเต็มที่)
○การเพิ่มประสิทธิภาพความยาวคงที่ของ Memcached
จากคำอธิบายในส่วนด้านบน ฉันมีความเข้าใจเชิงลึกเกี่ยวกับ memcached มากขึ้น สามารถปรับให้เหมาะสมได้โดยอาศัยความเข้าใจเชิงลึกเท่านั้น
Memcached ได้รับการออกแบบมาสำหรับข้อมูลที่มีความยาวผันแปรได้ ตามลักษณะของข้อมูล อาจกล่าวได้ว่าเป็นการออกแบบที่ "เน้นสาธารณะ" อย่างไรก็ตาม หลายครั้งข้อมูลของเราไม่ได้เป็น "สากล" มากนัก การกระจายแบบไม่สม่ำเสมอ กล่าวคือ ความยาวข้อมูลกระจุกตัวอยู่ในหลายพื้นที่ (เช่น การบันทึกเซสชันผู้ใช้) สถานะสุดขั้วอีกประการหนึ่งคือข้อมูลที่มีความยาวเท่ากัน (เช่น ค่าคีย์ที่มีความยาวคงที่ ข้อมูลที่มีความยาวคงที่เป็นส่วนใหญ่ เห็นได้จากการเข้าถึง สถิติออนไลน์ หรือการล็อคการดำเนินการ)
ที่นี่เราศึกษาโซลูชันการปรับให้เหมาะสมสำหรับข้อมูลที่มีความยาวคงที่เป็นหลัก (1.2) ข้อมูลที่มีความยาวผันแปรแบบรวมศูนย์มีไว้เพื่อการอ้างอิงเท่านั้นและง่ายต่อการนำไปใช้
ในการแก้ปัญหาข้อมูลที่มีความยาวคงที่ สิ่งแรกที่ต้องแก้ไขคือปัญหาการจัดสรรแผ่นคอนกรีต สิ่งแรกที่ต้องได้รับการยืนยันก็คือ เราไม่ต้องการแผ่นคอนกรีตจำนวนมากที่มีความยาวต่างกันออกไป ของทรัพยากร วิธีที่ดีที่สุดคือชิ้นและรายการมีความยาวเท่ากัน ดังนั้นก่อนอื่นให้คำนวณความยาวของรายการ
เคยมีอัลกอริธึมสำหรับคำนวณความยาวของรายการมาก่อน ควรสังเกตว่านอกเหนือจากความยาวสตริงแล้ว ต้องเพิ่มความยาวของโครงสร้างรายการ 32 ไบต์ด้วย
สมมติว่าเราคำนวณแล้วว่าเราต้องบันทึกข้อมูลที่มีความยาวเท่ากันจำนวน 200 ไบต์
ขั้นตอนต่อไปคือการปรับเปลี่ยนความสัมพันธ์ระหว่างการแบ่งชั้นของแผ่นพื้นและความยาวชิ้น ในเวอร์ชันดั้งเดิม มีความสัมพันธ์ที่สอดคล้องกันระหว่างความยาวของ chunk และ classid ตอนนี้ถ้า chunk ทั้งหมดถูกตั้งค่าเป็น 200 ไบต์ ความสัมพันธ์นี้ก็ไม่มีอยู่จริง วิธีหนึ่งคือการใช้เฉพาะ ID คงที่สำหรับโครงสร้างการจัดเก็บข้อมูลทั้งหมด นั่นคือ ใช้เพียง 1 ช่องจาก 199 ช่องเท่านั้น ภายใต้เงื่อนไขนี้ DONT_PREALLOC_SLABS จะต้องถูกกำหนดเพื่อหลีกเลี่ยงการสูญเสียการจัดสรรล่วงหน้าเพิ่มเติม อีกวิธีหนึ่งคือการสร้างความสัมพันธ์แฮชเพื่อกำหนดคลาสซิดจากรายการ คุณไม่สามารถใช้ความยาวเป็นคีย์ได้ คุณสามารถใช้ข้อมูลตัวแปร เช่น ผลลัพธ์ NewHash ของคีย์ หรือทำการแฮชโดยตรงตามคีย์ (the คีย์ของข้อมูลที่มีความยาวคงที่ต้องมีความยาวเท่ากัน) เพื่อความง่ายในที่นี้ เราเลือกวิธีแรก ข้อเสียของวิธีนี้คือใช้เพียง ID เดียว เมื่อปริมาณข้อมูลมีขนาดใหญ่มาก Slab Chain จะยาวมาก (เนื่องจากข้อมูลทั้งหมดอัดแน่นอยู่ในนั้น) โซ่เดียว) ค่าใช้จ่ายในการสำรวจค่อนข้างสูง
การซ้ำซ้อนของพื้นที่ทั้งสามประเภทถูกนำมาใช้ก่อนหน้านี้ การตั้งค่าความยาวชิ้นให้เท่ากับความยาวของรายการจะช่วยแก้ปัญหาการเสียพื้นที่ครั้งแรกได้ ) เพื่อแก้ปัญหานี้ คุณต้องแก้ไขค่าคงที่ POWER_BLOCK เพื่อให้ขนาดของแต่ละแผ่นเท่ากับจำนวนเต็มคูณด้วยความยาวชิ้น เพื่อให้แผ่นคอนกรีตสามารถแบ่งออกเป็น n ชิ้น ค่านี้ควรใกล้เคียงกับ 1MB หากมีขนาดใหญ่เกินไป ก็จะทำให้เกิดความซ้ำซ้อน ด้วยวิธีนี้ แผ่นคอนกรีตจะมีขนาด 1 ล้านไบต์ ไม่ใช่ 1048576 ปัญหาความซ้ำซ้อนทั้งสามได้รับการแก้ไขแล้ว และการใช้พื้นที่จะดีขึ้นอย่างมาก
แก้ไขฟังก์ชัน slabs_clsid เพื่อให้ส่งกลับค่าคงที่โดยตรง (เช่น 1)
รหัส: [คัดลอกไปยังคลิปบอร์ด] int slabs_clsid ที่ไม่ได้ลงนาม (ขนาด size_t) {
กลับ 1;
-
แก้ไขฟังก์ชัน slabs_init ลบส่วนที่วนซ้ำเพื่อสร้างแอตทริบิวต์ classid ทั้งหมด และเพิ่ม slabclass[1] โดยตรง:
รหัส:[คัดลอกไปยังคลิปบอร์ด]slabclass[1].size = 200; //200 ไบต์ต่ออัน
แผ่นพื้น [1] .perslab = 5,000; //1000000/200
◎ไคลเอนต์ Memcached
Memcached เป็นเซอร์วิสโปรแกรม เมื่อใช้งาน คุณสามารถเชื่อมต่อกับเซิร์ฟเวอร์ memcached ตามโปรโตคอล ส่งคำสั่งไปยังกระบวนการบริการ จากนั้นดำเนินการข้อมูลข้างต้น เพื่อความสะดวกในการใช้งาน memcached มีโปรแกรมไคลเอนต์มากมายให้เลือกใช้งาน ซึ่งสอดคล้องกับภาษาต่างๆ และมีไคลเอนต์ในภาษาต่างๆ มากมาย ภาษา C ได้แก่ libmemcache และ APR_Memcache ส่วนภาษา Cache::Memcached ยังรองรับ Python, Ruby, Java, C# และภาษาอื่นๆ อีกด้วย PHP มีไคลเอ็นต์มากที่สุด ไม่ใช่แค่ส่วนขยาย mcache และ PECL memcache เท่านั้น แต่ยังมีคลาสการห่อหุ้มจำนวนมากที่เขียนโดย PHP ต่อไปนี้เป็นคำแนะนำเกี่ยวกับวิธีใช้ memcached ใน PHP:
ส่วนขยาย mcache จะถูกห่อหุ้มใหม่โดยใช้ libmemcache . libmemcache ยังไม่ได้เปิดตัวเวอร์ชันที่เสถียร เวอร์ชันปัจจุบันคือ 1.4.0-rc2 ซึ่งสามารถพบได้ที่นี่ คุณลักษณะที่แย่มากของ libmemcache คือมันจะเขียนข้อความแสดงข้อผิดพลาดจำนวนมากไปที่ stderr โดยทั่วไปเมื่อใช้เป็น lib stderr มักจะถูกนำไปที่อื่น เช่น บันทึกข้อผิดพลาดของ Apache และ libmemcache จะฆ่าตัวตาย ซึ่งอาจทำให้เกิดความผิดปกติ แต่ประสิทธิภาพก็ยังดีมาก
ส่วนขยาย mcache ได้รับการอัปเดตล่าสุดเป็น 1.2.0-beta10 ผู้เขียนอาจลาออกไม่เพียงแต่เขาหยุดอัปเดตเท่านั้น แต่ยังไม่สามารถเปิดเว็บไซต์ได้ (~_~) เขาต้องไปที่อื่นเพื่อรับส่วนขยายที่ไม่รับผิดชอบนี้ . หลังจากคลายการบีบอัด วิธีการติดตั้งจะเป็นปกติ: phpize & configuration & make & make install อย่าลืมติดตั้ง libmemcache ก่อน การใช้ส่วนขยายนี้ทำได้ง่าย:
CODE:[คัดลอกไปยังคลิปบอร์ด]<?php
$mc = memcache(); // สร้างอ็อบเจ็กต์การเชื่อมต่อ memcache โปรดทราบว่า new ไม่ได้ใช้ที่นี่!
$mc->add_server('localhost', 11211); // เพิ่มกระบวนการบริการ
$mc->add_server('localhost', 11212); // เพิ่มกระบวนการบริการที่สอง
$mc->set('key1', 'Hello'); // เขียน key1 => สวัสดี
$mc->set('key2', 'World', 10); // เขียน key2 => World, หมดอายุใน 10 วินาที
$mc->set('arr1', array('Hello', 'World')); // เขียนอาร์เรย์
$key1 = $mc->get('key1'); // รับค่าของ 'key1' และกำหนดให้กับ $key1
$key2 = $mc->get('key2'); // รับค่าของ 'key2' และกำหนดให้กับ $key2 หากเกิน 10 วินาที จะไม่สามารถใช้ได้
$arr1 = $mc->get('arr1'); // รับอาร์เรย์ 'arr1'
$mc->delete('arr1'); // ลบ 'arr1'
$mc->flush_all(); // ลบข้อมูลทั้งหมด
$stats = $mc->stats(); // รับข้อมูลเซิร์ฟเวอร์
var_dump($stats); // ข้อมูลเซิร์ฟเวอร์เป็นอาร์เรย์
-
ข้อดีของส่วนขยายนี้คือสามารถใช้พื้นที่เก็บข้อมูลแบบกระจายและการทำโหลดบาลานซ์ได้อย่างง่ายดาย เนื่องจากสามารถเพิ่มที่อยู่บริการได้หลายรายการ ส่วนขยายนี้จะอยู่บนเซิร์ฟเวอร์บางตัวตามผลลัพธ์ของแฮช นี่เป็นคุณสมบัติของ libmemcache . libmemcache รองรับวิธีการแฮชแบบรวมศูนย์ รวมถึงแฮช CRC32, ELF และ Perl
PECL memcache เป็นส่วนขยายที่ออกโดย PECL เวอร์ชันล่าสุดคือ 2.1.0 ซึ่งสามารถรับได้จากเว็บไซต์ pecl การใช้งานส่วนขยาย memcache สามารถพบได้ในคู่มือ PHP รุ่นใหม่บางรุ่น มันคล้ายกับ mcache มาก ซึ่งคล้ายกันมาก:
รหัส:[คัดลอกไปยังคลิปบอร์ด]<?php
$memcache = Memcache ใหม่;
$memcache->connect('localhost', 11211) or die ("ไม่สามารถเชื่อมต่อได้");
$version = $memcache->getVersion();
echo "เวอร์ชันของเซิร์ฟเวอร์: ".$version"n";
$tmp_object = new stdClass;
$tmp_object->str_attr = 'ทดสอบ';
$tmp_object->int_attr = 123;
$memcache->set('key', $tmp_object, false, 10) or die ("ไม่สามารถบันทึกข้อมูลที่เซิร์ฟเวอร์");
echo "เก็บข้อมูลไว้ในแคช (ข้อมูลจะหมดอายุใน 10 วินาที)n";
$get_result = $memcache->get('key');
echo "ข้อมูลจากแคช:n";
var_dump($get_result)
;
ส่วนขยายนี้ใช้สตรีมของ PHP เพื่อเชื่อมต่อโดยตรงกับเซิร์ฟเวอร์ memcached และส่งคำสั่งผ่านซ็อกเก็ต แม้จะไม่สมบูรณ์เท่ากับ libmemcache และไม่รองรับการดำเนินการแบบกระจาย เช่น add_server แต่เนื่องจากไม่ได้อาศัยโปรแกรมภายนอกอื่นๆ จึงมีความเข้ากันได้ดีกว่าและค่อนข้างเสถียร ในด้านประสิทธิภาพนั้นมีความแตกต่างไม่มากนัก
นอกจากนี้ ยังมีคลาส PHP มากมาย เช่น MemcacheClient.inc.php และหลายคลาสสามารถพบได้ใน phpclasses.org โดยทั่วไปคลาสเหล่านี้จะถูกห่อหุ้มใหม่ของ Perl Client API และใช้ในลักษณะเดียวกัน
○BSM_Memcache
จากมุมมองของไคลเอนต์ C APR_Memcache เป็นโปรแกรมไคลเอนต์ที่มีความสมบูรณ์และเสถียรมาก ซึ่งรองรับการล็อคเธรดและการดำเนินการระดับอะตอมเพื่อให้มั่นใจถึงความเสถียรในการปฏิบัติงาน อย่างไรก็ตามมันขึ้นอยู่กับ APR (APR จะเปิดตัวในส่วนสุดท้าย) และไม่ได้มีแอพพลิเคชั่นที่หลากหลายเช่น Libmemcache เพราะมันไม่สามารถทำงานนอกสภาพแวดล้อม APR ได้ อย่างไรก็ตาม APR สามารถติดตั้งแยกต่างหากจาก Apache
BSM_MEMCACHE เป็นส่วนขยาย PHP ที่ใช้ APR_MEMCACHE ที่ฉันพัฒนาขึ้นในโครงการ BS.MAGIC โปรแกรมนี้ง่ายมากและไม่ได้ทำฟังก์ชั่นมากเกินไป
แตกต่างจากส่วนขยายของ McAche ที่รองรับการจัดเก็บแบบกระจายหลายเซิร์ฟเวอร์ BSM_MEMCACHE รองรับเซิร์ฟเวอร์หลายกลุ่ม การสำรองข้อมูลร้อนจะถูกนำไปใช้ แน่นอนว่าค่าใช้จ่ายในการใช้งานฟังก์ชั่นนี้คือการเสียสละของประสิทธิภาพ โดยปกติคุณจะได้รับในครั้งต่อไป
BSM_MEMCACHE รองรับฟังก์ชั่นเหล่านี้เท่านั้น:
รหัส: [คัดลอกไปยังคลิปบอร์ด] ZEND_FUNCTION_ENTRY BSM_MEMCACHE_FUNCTIONS [] = =
-
php_fe (mc_get, null)
php_fe (mc_set, null)
php_fe (mc_del, null)
php_fe (mc_add_group, null)
php_fe (mc_add_server, null)
php_fe (mc_shutdown, null)
{null, null, null}
-
ฟังก์ชั่น MC_ADD_GROUP ส่งคืนจำนวนเต็ม (จริง ๆ แล้วมันควรจะเป็นวัตถุฉันขี้เกียจ ~ _ ~) เป็น ID กลุ่ม addrort)
รหัส: [คัดลอกไปยังคลิปบอร์ด]/**
*เพิ่มกลุ่มเซิร์ฟเวอร์
-
php_function (mc_add_group)
-
APR_INT32_T Group_id;
APR_STATUS_T
RV;
-
ผิด _param_count;
return_null ();
}
group_id = free_group_id ();
if (-1 == Group_id)
-
return_false;
}
APR_MEMCACHE_T *MC;
RV = APR_MEMCACHE_CREATE (P, MAX_G_SERVER, 0, & MC
)
;
-
รหัส: [คัดลอกไปยังคลิปบอร์ด]/**
* เพิ่มเซิร์ฟเวอร์ลงในกลุ่ม
-
php_function (mc_add_server)
-
APR_STATUS_T RV;
APR_INT32_T Group_id;
double g;
ถ่าน *srv_str;
int srv_str_l
;
-
ผิด _param_count;
}
if (zend_parse_parameters (zend_num_args () tsrmls_cc, "ds", & g, & srv_str, & srv_str_l) == ความล้มเหลว)
-
return_false;
}
group_id = (APR_INT32_T) g
;
-
return_false;
}
char *โฮสต์, *ขอบเขต;
พอร์ต APR_PORT_T
;
ถ้า (APR_SUCCESS == RV)
-
// สร้างวัตถุเซิร์ฟเวอร์นี้
APR_MEMCACHE_SERVER_T *ST;
RV = APR_MEMCACHE_SERVER_CREATE (P, โฮสต์, พอร์ต, 0, 64, 1024, 600, & ST);
ถ้า (APR_SUCCESS == RV)
-
if (null == mc_groups [group_id])
-
return_false;
}
// เพิ่มเซิร์ฟเวอร์
APR_MEMCACHE_ADD_SERVER
(MC_GROUPS [Group_id], ST);
-
return_true;
-
-
}
return_false;
-
เมื่อตั้งค่าและลบข้อมูลลูปผ่านทุกกลุ่ม:
รหัส: [คัดลอกไปยังคลิปบอร์ด]/**
* จัดเก็บรายการในทุกกลุ่ม
-
php_function (mc_set)
-
ถ่าน *คีย์, *ค่า;
int key_l, value_l;
double ttl = 0;
double set_ct = 0
;
-
ผิด _param_count;
}
if (zend_parse_parameters (zend_num_args () tsrmls_cc, "ss | d", & key, & key_l, & value, & value_l, ttl) == ความล้มเหลว)
-
return_false;
}
// เขียนข้อมูลลงในทุกวัตถุ
APR_INT32_T I = 0;
ถ้า (ttl <0)
-
ttl = 0;
}
APR_STATUS_T RV
;
-
ถ้า (0 == is_validate_group (i))
-
// เขียนมัน!
RV = APR_MEMCACHE_ADD (MC_GROUPS [i], คีย์, ค่า, value_l, (APR_UINT32_T) TTL, 0);
ถ้า (APR_SUCCESS == RV)
-
set_ct ++;
-
-
}
return_double (set_ct);
-
ใน MC_GET คุณสุ่มเลือกกลุ่มก่อนจากนั้นเริ่มการสำรวจจากกลุ่มนี้:
รหัส: [คัดลอกไปยังคลิปบอร์ด]/**
* ดึงรายการจากกลุ่มสุ่ม
-
php_function (mc_get)
-
คีย์ char *, *value = null;
int key_l;
APR_SIZE_T VALUE_L
;
-
ผิด _param_count;
}
if (zend_parse_parameters (zend_num_args () tsrmls_cc, "s", & key, & key_l) == ความล้มเหลว)
-
return_mull ();
-
// ฉันจะลอง ...
// สุ่มอ่าน
APR_INT32_T CURR_GROUP_ID = RANDOR_GROUP ();
APR_INT32_T I = 0;
APR_INT32_T ลอง = 0;
APR_UINT32_T FLAG;
APR_MEMCACHE_T *oper;
APR_STATUS_T RV
;
-
ลอง = i + curr_group_id;
ลอง = ลอง % max_group;
ถ้า (0 == is_validate_group (ลอง))
-
// รับค่า
oper = mc_groups [ลอง];
RV = APR_MEMCACHE_GETP (MC_GROUPS [ลอง], P, (const Char *) คีย์, & value, & value_l, 0);
ถ้า (APR_SUCCESS == RV)
-
return_string (ค่า 1);
-
-
}
return_false;
-
รหัส: [คัดลอกไปยังคลิปบอร์ด]/**
* ID กลุ่มสุ่ม
* สำหรับ mc_get ()
-
APR_INT32_T RANDER_GROUP ()
-
โครงสร้าง Timeval TV;
โครงสร้างเขตเวลา TZ;
int
USEC
;
-
การใช้งานของ BSM_MEMCACHE นั้นคล้ายคลึงกับไคลเอนต์อื่น ๆ :
รหัส: [คัดลอกไปยังคลิปบอร์ด] <? php
$ g1 = mc_add_group (); // เพิ่มกลุ่มแรก
$ g2 = mc_add_group (); // เพิ่มกลุ่มที่สอง
mc_add_server ($ g1, 'localhost: 11211');
mc_add_server ($ g1, 'localhost: 11212');
MC_ADD_SERVER ($ g2, '10 .0.0.16: 11211 ');
MC_ADD_SERVER ($ g2, '10 .0.0.17: 11211 ')
;
$ key = mc_get ('key');
MC_DEL ('คีย์');
mc_shutdown ();
-
ข้อมูลที่เกี่ยวข้องเกี่ยวกับ APR_MEMCACHE สามารถดูได้ที่นี่และ BSM_MEMCACHE สามารถดาวน์โหลดได้จากเว็บไซต์นี้
◎เมษายนสภาพแวดล้อมบทนำ
ชื่อเต็มของ APR: Apache Portable Runtime มันเป็นชุดของไลบรารีภาษา C ข้ามแพลตฟอร์มที่สร้างและบำรุงรักษาโดย Apache Software Foundation มันถูกสกัดจาก Apache httpd1.x และเป็นอิสระจาก httpd APR มีอินเทอร์เฟซ API ที่สะดวกมากมายสำหรับการใช้งานรวมถึงฟังก์ชั่นการปฏิบัติเช่นพูลหน่วยความจำการดำเนินการสตริงเครือข่ายอาร์เรย์ตารางแฮช ฯลฯ การพัฒนาโมดูล Apache2 ต้องการการเปิดรับฟังก์ชั่น APR จำนวนมากแน่นอน APR สามารถติดตั้งและใช้งานได้อย่างอิสระ
◎ Postscript
นี่เป็นบทความสุดท้ายของฉันในปี Bingxu ของปฏิทินจันทรคติ (ปีเกิดของฉัน) ขอบคุณ Sina.com ที่ให้โอกาสในการวิจัยและเพื่อนร่วมงานในแผนกเพื่อขอความช่วยเหลือ
ดร. NP02-13-2007