ฉันกำลังพัฒนาเซิร์ฟเวอร์ฐานข้อมูลที่คล้ายกับ Cassandra
การพัฒนาเริ่มต้นใน C แต่สิ่งต่าง ๆ ก็ซับซ้อนมากโดยไม่ต้องเรียน
ขณะนี้ฉันได้ย้ายทุกอย่างใน C ++ 11 แต่ฉันยังคงเรียนรู้ C ++ ที่ทันสมัยและยังมีข้อสงสัยมากมาย
ฐานข้อมูลจะทำงานกับคู่ของคีย์ / ค่า ทุกคู่มีข้อมูลเพิ่มเติม - เมื่อถูกสร้างขึ้นด้วยเมื่อมันจะหมดอายุ (0 ถ้าไม่หมดอายุ) แต่ละคู่นั้นไม่เปลี่ยนรูป
Key คือสตริง C ค่าเป็นโมฆะ * แต่อย่างน้อยตอนนี้ฉันทำงานด้วยค่าเป็นสตริง C เช่นกัน
มีIList
ระดับนามธรรม มันสืบทอดมาจากสามชั้น
VectorList
- C dynamic array - คล้ายกับ std :: vector แต่ใช้realloc
LinkList
- ทำเพื่อการตรวจสอบและเปรียบเทียบประสิทธิภาพSkipList
- คลาสที่ใช้ในที่สุด
ในอนาคตฉันอาจทำRed Black
ต้นไม้ด้วย
แต่ละคนIList
มีศูนย์หรือมากกว่าตัวชี้ไปยังคู่เรียงตามคีย์
หากIList
ยาวเกินไปก็สามารถบันทึกลงดิสก์ในไฟล์พิเศษได้ read only list
แฟ้มพิเศษนี้เป็นชนิดของ
หากคุณต้องการค้นหากุญแจ
- ครั้งแรกในความทรงจำ
IList
จะค้นหา (SkipList
,SkipList
หรือLinkList
) - จากนั้นการค้นหาจะถูกส่งไปยังไฟล์ที่เรียงลำดับตามวันที่
(ไฟล์ใหม่สุดก่อน, ไฟล์ที่เก่าที่สุด - สุดท้าย)
ไฟล์ทั้งหมดเหล่านี้เป็น mmap-ed ในหน่วยความจำ - หากไม่พบสิ่งใดแสดงว่าไม่พบรหัส
ฉันไม่มีข้อสงสัยเกี่ยวกับการนำIList
สิ่งต่าง ๆ มาใช้
สิ่งที่ทำให้ฉันสับสนคือกำลังติดตาม:
ทั้งคู่มีขนาดแตกต่างกันพวกเขาจัดสรรโดยnew()
และพวกเขาstd::shared_ptr
ชี้ไปที่พวกเขา
class Pair{
public:
// several methods...
private:
struct Blob;
std::shared_ptr<const Blob> _blob;
};
struct Pair::Blob{
uint64_t created;
uint32_t expires;
uint32_t vallen;
uint16_t keylen;
uint8_t checksum;
char buffer[2];
};
ตัวแปรสมาชิก "buffer" เป็นตัวแปรที่มีขนาดต่างกัน มันเก็บคีย์ + ค่า
เช่นถ้าคีย์คือ 10 อักขระและค่าเป็น 10 ไบต์อีกทั้งวัตถุจะเป็นsizeof(Pair::Blob) + 20
(บัฟเฟอร์มีขนาดเริ่มต้นเท่ากับ 2 เนื่องจากมีสองไบต์ที่สิ้นสุดด้วยค่า null)
เลย์เอาต์เดียวกันนี้ใช้กับดิสก์ด้วยดังนั้นฉันสามารถทำสิ่งนี้:
// get the blob
Pair::Blob *blob = (Pair::Blob *) & mmaped_array[pos];
// create the pair, true makes std::shared_ptr not to delete the memory,
// since it does not own it.
Pair p = Pair(blob, true);
// however if I want the Pair to own the memory,
// I can copy it, but this is slower operation.
Pair p2 = Pair(blob);
อย่างไรก็ตามขนาดที่แตกต่างกันนี้เป็นปัญหาในหลาย ๆ ที่ด้วยรหัส C ++
std::make_shared()
ตัวอย่างเช่นฉันไม่สามารถใช้ สิ่งนี้สำคัญสำหรับฉันเพราะถ้าฉันมี 1M Pairs ฉันจะมีการจัดสรร 2M
จากอีกด้านหนึ่งถ้าฉันทำ "บัฟเฟอร์" เป็นอาร์เรย์แบบไดนามิก (เช่นอักขระใหม่ [123]) ฉันจะสูญเสีย "mmap" เคล็ดลับ "ฉันจะทำสอง dereferences ถ้าฉันต้องการตรวจสอบคีย์และฉันจะเพิ่มตัวชี้เดียว - 8 ไบต์ถึงคลาส
ฉันยังพยายามที่จะ "ดึง" สมาชิกทั้งหมดPair::Blob
เข้ามาPair
เพื่อPair::Blob
เป็นเพียงบัฟเฟอร์ แต่เมื่อฉันทดสอบมันช้ามากอาจเป็นเพราะการคัดลอกข้อมูลวัตถุรอบ ๆ
การเปลี่ยนแปลงอีกอย่างที่ฉันคิดคือการลบPair
คลาสและแทนที่ด้วยstd::shared_ptr
และเพื่อ "ดัน" วิธีการทั้งหมดกลับไปPair::Blob
แต่สิ่งนี้จะไม่ช่วยฉันในPair::Blob
คลาสขนาดที่ผันแปรได้
ฉันสงสัยว่าฉันจะปรับปรุงการออกแบบวัตถุให้เป็นมิตรกับ C ++ ได้อย่างไร
ซอร์สโค้ดแบบเต็มอยู่ที่นี่:
https://github.com/nmmmnu/HM3
IList::remove
หรือเมื่อ IList ถูกทำลาย ใช้เวลานาน แต่ฉันจะทำในหัวข้อแยก มันจะง่ายเพราะ IList จะเป็นstd::unique_ptr<IList>
เช่นนั้นต่อไป ดังนั้นฉันจะสามารถ "สลับ" ด้วยรายการใหม่และเก็บวัตถุเก่าไว้ที่ไหนสักแห่งที่ฉันสามารถเรียก d-tor
C string
และข้อมูลมักจะเป็นบัฟเฟอร์void *
หรือchar *
ดังนั้นคุณสามารถส่งผ่านอาร์เรย์ถ่าน คุณสามารถค้นหาที่คล้ายกันในหรือredis
memcached
ในบางจุดฉันสามารถตัดสินใจที่จะใช้std::string
หรือแก้ไข char char สำหรับคีย์ แต่ขีดเส้นใต้มันจะยังคงเป็นสตริง C
std::map
หรือstd::unordered_map
? ค่าทำไม (ที่เกี่ยวข้องกับคีย์) บางvoid*
? คุณอาจจะต้องทำลายพวกเขาในบางจุด; อย่างไรและเมื่อไหร่? ทำไมคุณไม่ใช้เทมเพลต?