หน้าที่ตอบกลับภายใน 200 ms กับ Redis ในเครื่องใช้เวลาสี่วินาทีเมื่อไคลเอนต์อยู่หลัง VPN ขณะที่ตัวนับของเซิร์ฟเวอร์ว่างอยู่ Slow Log ไม่แสดงอะไรที่เกินหนึ่งมิลลิวินาที กราฟ CPU แบนราบ และอินสแตนซ์ให้บริการไคลเอนต์อื่นได้โดยไม่มีข้อร้องเรียน สิ่งที่เปลี่ยนไปคือระยะทาง และสิ่งที่คูณระยะทางคือ จำนวนครั้งที่โค้ดของคุณหยุดและรอ. เดินตามเส้นทางของคำขอจากไคลเอนต์ออกไป และการแก้ไขเกือบทุกครั้งคือการรอแบบต่อเนื่องที่น้อยลง ไม่ใช่เซิร์ฟเวอร์ที่เร็วขึ้น

เริ่มจากไคลเอนต์ ไม่ใช่จากเมตริกของเซิร์ฟเวอร์

ดูโปรเซสของไคลเอนต์ระหว่างคำสั่งแรกถึงคำสั่งสุดท้าย หากร่องรอยแสดงการตอบกลับ การหยุดชั่วครู่ แล้วจึงเป็นคำสั่งถัดไป คุณกำลังจ่ายรอบไปกลับเต็มหนึ่งรอบต่อคำสั่ง และการปรับแต่งเซิร์ฟเวอร์ใดก็ไม่เปลี่ยนสิ่งนั้น Slow Log เป็นเครื่องมือที่ผิดที่นี่ เพราะมันบันทึกเวลาดำเนินการและจงใจตัด I/O ของการสื่อสารออก Slow Log ที่สะอาดข้าง endpoint ที่ช้าคือลายเซ็นของปัญหารอบไปกลับ

วัดเส้นทางเองก่อนแตะโค้ด redis-cli -h redis.example.internal --latency จะส่ง PING ซ้ำๆ และพิมพ์เวลาเฉลี่ยของคำขอและการตอบกลับ ซึ่งเป็นต้นทุนต่อการแลกเปลี่ยนหนึ่งครั้งของคุณ คูณด้วยจำนวนคำสั่งต่อเนื่องในหนึ่งการกระทำของผู้ใช้ แล้วคุณจะได้พื้นล่างของ endpoint

นับจำนวนรอบไปกลับที่หนึ่งการกระทำของผู้ใช้ต้องใช้จริง

เลือก endpoint หนึ่งตัวแล้วเขียนทุกการเรียก Redis ตามลำดับ รวมถึงที่ซ่อนอยู่ใน ORM, session middleware หรือการค้นหา feature flag endpoint ที่ช้าส่วนใหญ่เรียกยี่สิบหรือสามสิบครั้งในที่ที่สามครั้งก็พอ และตัวที่ซ่อนอยู่มักเป็นเสียงส่วนใหญ่

ห่วงโซ่การพึ่งพาไม่อาจยุบรวมได้: ถ้าคำสั่งที่สองต้องใช้คีย์ที่คำสั่งแรกคืนมา การรอคือของจริง และทางเลือกเดียวคือแบบจำลองข้อมูลอื่นหรือการดำเนินการฝั่งเซิร์ฟเวอร์ที่แสดงทั้งขั้นตอนในครั้งเดียว การอ่านที่เป็นอิสระต่อกันเป็นอีกกรณี เพราะไม่มีอะไรบังคับให้คุณรอการตอบกลับแรกก่อนส่งครั้งที่สอง ทำเครื่องหมายทุกการเรียกว่าเป็นแบบพึ่งพาหรืออิสระก่อนแก้ไขอะไรก็ตาม แล้วรูปร่างของการแก้ไขจะชัดเจน

ตัวเลขที่สำคัญคือการรอแบบต่อเนื่อง ไม่ใช่จำนวนคำสั่งทั้งหมด สามสิบคำสั่งใน batch เดียวยังคงเสียค่าการรอเพียงครั้งเดียว

ทำ batch เฉพาะคำสั่งที่ไม่ขึ้นต่อกัน

pipeline จัดคำสั่งที่เป็นอิสระเข้าคิวและเก็บการตอบกลับตามลำดับ เปลี่ยน N การรอให้เหลือหนึ่งครั้ง ไคลเอนต์ส่วนใหญ่เปิดเผยมันเป็นบล็อก callback หรือ context manager ไม่ใช่โหมดการเชื่อมต่อแยก ดังนั้นตรวจสอบ API ที่เวอร์ชันที่คุณติดตั้งมีให้ กำไรขยายตามเวลารอบไปกลับ ซึ่งเป็นเหตุผลว่าทำไมมันจึงสำคัญบน VPN และแทบไม่รู้สึกบน localhost

import redis

r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
    pipe.get(key)
print(pipe.execute())

ส่ง transaction=False เมื่อคุณต้องการเฉพาะการทำ batch ด้วยค่าเริ่มต้น ไคลเอนต์จะห่อ batch ไว้ใน MULTI และ EXEC ซึ่งทำให้เป็นอะตอมและเปลี่ยนสิ่งที่ไคลเอนต์อื่นสังเกตได้ระหว่างทาง ความเป็นอะตอมเป็นการตัดสินใจเรื่องความถูกต้อง ไม่ใช่ปุ่มประสิทธิภาพ และ pipeline ไม่ใช่ทรานแซกชันเพียงเพราะทั้งสองลดรอบไปกลับ

อ่านทุกการตอบกลับ ไม่ใช่แค่การส่ง batch คำสั่งในคิวอาจคืนข้อผิดพลาดขณะที่ batch ทั้งหมดสำเร็จ และไคลเอนต์ที่ทิ้งรายการตอบกลับจะเปลี่ยนการเขียนที่ล้มเหลวให้เป็นการเขียนที่เงียบ เปรียบเทียบความยาวของรายการตอบกลับกับจำนวนคำสั่งที่คุณเข้าคิว

รักษาการเชื่อมต่อให้มีชีวิตระหว่างการกระทำ

ทุกการเชื่อมต่อใหม่ต้องจ่ายค่าการแปลงชื่อ TCP handshake, TLS handshake และ AUTH และบน VPN แต่ละอย่างคือรอบไปกลับที่คุณทำ batch ไม่ได้ สร้างไคลเอนต์ครั้งเดียวต่อโปรเซสแล้วใช้ซ้ำ หรือใช้ pool ที่ไลบรารีของคุณมีให้โดยกำหนดขนาดจำกัด การเชื่อมต่อใหม่ทุกคำขอเป็นสาเหตุที่พบบ่อยเป็นอันดับสองของอาการนี้รองจากการรอทุกคำสั่ง

เก็บ hostname ส่วนตัวไว้ในการกำหนดค่า ไม่ใช่ที่อยู่ตามตัวอักษร การตรวจสอบใบรับรองและตัวตนของบริการยังใช้บนเครือข่ายส่วนตัว และการปักหมุด IP จะพังทันทีที่ผู้ให้บริการกำหนดหมายเลข subnet ใหม่ หากการแปลงชื่อเป็นส่วนที่ช้า ให้แก้เส้นทางของ resolver ไม่ใช่สตริงการเชื่อมต่อ

ตั้ง socket timeout ไว้อย่างชัดเจนเพื่อให้อุโมงค์ที่ค้างล้มเหลวอย่างรวดเร็วแทนที่จะแขวน worker จากนั้นยืนยันว่า pool ถูกใช้ซ้ำจริงโดยบันทึกการสร้างการเชื่อมต่อในไคลเอนต์

กำหนดขนาด batch ก่อนที่มันจะเป็นปัญหาหน่วยความจำ

Redis ถือการตอบกลับที่เข้าคิวไว้ตลอดระยะเวลาของ batch และไคลเอนต์ถือผลลัพธ์ที่แยกวิเคราะห์แล้วในภายหลัง pipeline ที่มีค่าขนาดใหญ่หนึ่งหมื่นค่าจะแลกการรอเครือข่ายกับการกระโดดของหน่วยความจำและการหยุดยาวที่ปลายทั้งสองข้าง

เลือกขนาด batch ตามปริมาณการตอบกลับ ไม่ใช่จำนวนคำสั่ง คำสั่งเล็กๆ ไม่กี่ร้อยคำสั่งใน batch เดียวมักสบาย แต่ payload ขนาดหลายกิโลไบต์หนึ่งร้อยรายการไม่ใช่ แบ่งตามไบต์และตามจำนวนด้วย และเก็บแต่ละ batch ให้อยู่ในงบความหน่วงที่ endpoint มีจริง

วัดตั้งแต่ต้นจนจบหลังการเปลี่ยนแปลง: redis-cli -h redis.example.internal --latency-history จะบอกว่าเส้นทางเองเสถียรหรือไม่ และตัวจับเวลาคำขอของคุณเองจะบอกว่า endpoint ดีขึ้นหรือไม่ หาก endpoint ยังช้าอยู่ในขณะที่ประวัติความหน่วงแบนราบ ต้นทุนที่เหลือคืองานฝั่งเซิร์ฟเวอร์หรือการทำ serialization ของคุณเอง และก้าวต่อไปคือโปรไฟล์ของ handler ไม่ใช่การเปลี่ยน batch อีกครั้ง

บทความที่เกี่ยวข้อง