ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ฉันขอแนะนำให้คุณอ่านสำเนารายงานตั้งแต่ต้นปี 2016 โดย Andrey Salnikov “ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การบวมใน postgresql”

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ฉันใช้ฐานข้อมูลทดสอบซึ่งมีสองตาราง จานหนึ่งมีบัญชีลูกค้า อีกจานมีธุรกรรมในบัญชีเหล่านี้ และบ่อยครั้งที่เราอัปเดตยอดคงเหลือในบัญชีเหล่านี้

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ข้อมูลเริ่มต้นของเพลต: มีขนาดค่อนข้างเล็ก 2 MB เวลาตอบสนองสำหรับฐานข้อมูลและสัญญาณโดยเฉพาะก็ดีมากเช่นกัน และโหลดที่ค่อนข้างดี - 2 การทำงานต่อวินาทีตามแผ่น

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

และในสถานการณ์นี้ เราจะเห็นว่าเรามีสัญญาณเล็กๆ จริงๆ ดัชนีมีขนาดเล็กที่ 2 MB นี่คือกราฟแรกทางด้านซ้าย

เวลาตอบสนองโดยเฉลี่ยบนเซิร์ฟเวอร์ยังเสถียรและสั้นอีกด้วย นี่คือกราฟมุมขวาบน

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และตอนนี้เรามีโศกนาฏกรรม ด้วยเหตุผลบางประการ จึงมีธุรกรรมที่ถูกลืมไปนานแล้ว เหตุผลมักจะเป็นเรื่องซ้ำซาก:

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

สิ่งเหล่านี้นำไปสู่ที่ไหน?

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

เกิดอะไรขึ้นกับสัญญาณของเรา? เหมือน. เวลาตอบสนองโดยเฉลี่ยของเราตามสัญลักษณ์ได้เพิ่มขึ้นหลายระดับ โดยเฉพาะในแง่ของทรัพยากรที่ใช้ไป เราเห็นว่าภาระของโปรเซสเซอร์เพิ่มขึ้นอย่างมาก นี่คือกราฟมุมขวาบน และมันเพิ่มขึ้นเนื่องจากโปรเซสเซอร์ต้องเรียงลำดับบรรทัดที่ไม่มีประโยชน์เพื่อค้นหาบรรทัดที่ต้องการ นี่คือกราฟมุมขวาล่าง และเป็นผลให้จำนวนการโทรต่อวินาทีของเราเริ่มลดลงอย่างมาก เนื่องจากฐานข้อมูลไม่มีเวลาในการประมวลผลคำขอในจำนวนเท่าเดิม

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และคำถาม: “เกิดอะไรขึ้นกับฐานทัพในขณะนี้?” และสถานการณ์ต่อไปนี้ก็เกิดขึ้นกับฐาน บนแผนภูมิธุรกรรม คุณจะเห็นว่าหยุดแล้วและไม่มีธุรกรรมระยะยาวจริงๆ แต่ขนาดของป้ายเพิ่มขึ้นอย่างมากในระหว่างเกิดอุบัติเหตุ และตั้งแต่นั้นมาพวกเขาก็ไม่ได้ลดลงเลย เวลาเฉลี่ยบนฐานมีเสถียรภาพแล้ว และคำตอบดูเหมือนจะมาอย่างเพียงพอด้วยความเร็วที่เรายอมรับได้ เครื่องดูดอัตโนมัติเริ่มมีการเคลื่อนไหวมากขึ้นและเริ่มทำอะไรบางอย่างกับสัญลักษณ์นี้ เนื่องจากจำเป็นต้องกรองข้อมูลเพิ่มเติม

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

โดยเฉพาะอย่างยิ่ง ตามแผ่นทดสอบที่มีบัญชี ซึ่งเราเปลี่ยนยอดคงเหลือ: ดูเหมือนว่าเวลาตอบสนองสำหรับคำขอจะกลับมาเป็นปกติ แต่ในความเป็นจริงมันสูงกว่าหนึ่งเท่าครึ่ง

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

โดยรวมดีแต่สถานการณ์แย่ลงกว่าเดิม ล้างความเสื่อมโทรมของฐานข้อมูลอันเป็นผลมาจากแอปพลิเคชันของเราที่ทำงานร่วมกับฐานข้อมูลนี้

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และเพื่อทำความเข้าใจว่าเกิดอะไรขึ้น หากคุณไม่ได้อยู่ที่รายงานครั้งก่อน ตอนนี้เรามาดูทฤษฎีกันสักหน่อย ทฤษฎีเกี่ยวกับกระบวนการภายใน ทำไมต้องดูดฝุ่นในรถยนต์และทำหน้าที่อะไร?

สั้นๆ เพื่อทำความเข้าใจครับ สักพักเราก็มีโต๊ะ เรามีแถวในตาราง บรรทัดเหล่านี้สามารถใช้งานได้ มีชีวิตชีวา และเป็นสิ่งที่เราต้องการในตอนนี้ มีการทำเครื่องหมายด้วยสีเขียวในภาพ และมีเส้นตายที่ได้รับการแก้ไขแล้ว ได้รับการอัปเดต และมีรายการใหม่ปรากฏขึ้น และมีการทำเครื่องหมายว่าไม่น่าสนใจสำหรับฐานข้อมูลอีกต่อไป แต่พวกเขาอยู่ในตารางเนื่องจากฟีเจอร์ Postgres

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

เกิดอะไรขึ้นระหว่างเกิดอุบัติเหตุ? กระบวนการนี้เกิดขึ้นที่นั่นได้อย่างไร?

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

แต่ในเวลานี้เรายังคงทำงานกับตารางต่อไป เราทำบางอย่างในนั้น อัปเดต เปลี่ยนแปลงข้อมูล ฐานข้อมูลควรทำอย่างไรในเวลานี้? เธอไม่มีทางเลือกนอกจากเพิ่มบรรทัดใหม่ต่อท้ายตารางที่มีอยู่ ดังนั้นขนาดโต๊ะของเราจึงเริ่มบวม

ในความเป็นจริง เราจำเป็นต้องมีเส้นสีเขียวในการทำงาน แต่ในระหว่างที่เกิดปัญหาดังกล่าว ปรากฏว่าเปอร์เซ็นต์ของเส้นสีเขียวต่ำมากทั่วทั้งตาราง

และเมื่อเราดำเนินการค้นหา ฐานข้อมูลจะต้องผ่านทุกบรรทัด: ทั้งสีแดงและสีเขียว เพื่อค้นหาบรรทัดที่ต้องการ และผลกระทบของการทำให้ตารางมีข้อมูลที่ไร้ประโยชน์นั้นเรียกว่า "การบวม" ซึ่งจะกินพื้นที่ดิสก์ของเราด้วย จำไว้ว่ามันคือ 2 MB กลายเป็น 300 MB เหรอ? ตอนนี้เปลี่ยนเมกะไบต์เป็นกิกะไบต์และคุณจะสูญเสียทรัพยากรดิสก์ทั้งหมดอย่างรวดเร็ว

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

อาจเกิดผลเสียอะไรกับเราบ้าง?

  • ในตัวอย่างของฉัน ตารางและดัชนีเพิ่มขึ้น 150 เท่า ลูกค้าบางรายของเรามีกรณีที่ร้ายแรงมากขึ้นเมื่อพวกเขาเริ่มใช้พื้นที่ดิสก์จนหมด
  • ขนาดของตารางจะไม่ลดลงเลย เครื่องดูดฝุ่นอัตโนมัติในบางกรณีสามารถตัดส่วนท้ายของโต๊ะได้หากมีเส้นตายเท่านั้น แต่เนื่องจากมีการหมุนอย่างต่อเนื่อง เส้นสีเขียวเส้นหนึ่งอาจค้างที่ส่วนท้ายและไม่ได้รับการอัปเดต ในขณะที่เส้นอื่นๆ ทั้งหมดจะถูกเขียนลงที่ไหนสักแห่งที่จุดเริ่มต้นของจาน แต่นี่เป็นเหตุการณ์ที่ไม่น่าเป็นไปได้ที่โต๊ะของคุณจะลดขนาดลง ดังนั้นคุณไม่ควรคาดหวัง
  • ฐานข้อมูลจำเป็นต้องเรียงลำดับบรรทัดที่ไม่มีประโยชน์ทั้งหมด และเราเปลืองทรัพยากรของดิสก์ เราเปลืองทรัพยากรตัวประมวลผลและไฟฟ้า
  • และสิ่งนี้ส่งผลโดยตรงต่อแอปพลิเคชันของเรา เพราะหากในตอนแรกเราใช้เวลา 10 มิลลิวินาทีกับคำขอ, 10 มิลลิวินาทีกับโค้ดของเรา จากนั้นในระหว่างที่ขัดข้องเราเริ่มใช้เวลาหนึ่งวินาทีกับคำขอและ 10 มิลลิวินาทีกับโค้ด นั่นคือ ลำดับของ ขนาดประสิทธิภาพของแอปพลิเคชันลดลง และเมื่ออุบัติเหตุได้รับการแก้ไขแล้ว เราเริ่มใช้เวลา 20 มิลลิวินาทีกับคำขอ และ 10 มิลลิวินาทีกับโค้ด ซึ่งหมายความว่าเรายังคงผลิตภาพลดลงหนึ่งเท่าครึ่ง และทั้งหมดนี้เป็นเพราะการทำธุรกรรมครั้งหนึ่งที่หยุดชะงัก บางทีอาจเป็นเพราะความผิดของเรา
  • และคำถามว่า “เราจะได้ทุกอย่างกลับคืนมาได้อย่างไร” เพื่อให้ทุกอย่างเรียบร้อยดีและคำขอเข้ามาอย่างรวดเร็วเหมือนก่อนเกิดอุบัติเหตุ

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

เพื่อจุดประสงค์นี้มีรอบการทำงานที่แน่นอน

ก่อนอื่นเราต้องค้นหาตารางที่มีปัญหาซึ่งป่อง เราเข้าใจดีว่าในบางตาราง การบันทึกจะมีการใช้งานมากกว่า ในขณะที่ตารางอื่นๆ มีการใช้งานน้อยกว่า และสำหรับสิ่งนี้เราใช้ส่วนขยาย pgstattuple. เมื่อติดตั้งส่วนขยายนี้ คุณสามารถเขียนคำสั่งที่จะช่วยคุณค้นหาตารางที่ค่อนข้างป่องได้

เมื่อคุณพบตารางเหล่านี้แล้ว คุณจะต้องบีบอัดข้อมูลเหล่านั้น มีเครื่องมือสำหรับสิ่งนี้อยู่แล้ว ในบริษัทของเรา เราใช้เครื่องมือสามอย่าง อย่างแรกคือ VACUUM FULL ในตัว เขาเป็นคนโหดร้าย รุนแรง และไร้ความปราณี แต่บางครั้งเขาก็มีประโยชน์มาก Pg_repack и pgกะทัดรัด - เหล่านี้เป็นยูทิลิตี้ของบุคคลที่สามสำหรับการบีบอัดตาราง และพวกเขาจะปฏิบัติต่อฐานข้อมูลอย่างระมัดระวังมากขึ้น

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

หลังจากที่เราแก้ไขทุกอย่างและแน่ใจว่าทุกอย่างเรียบร้อยดีแล้ว เราต้องรู้วิธีป้องกันสถานการณ์นี้ในอนาคต:

  • ก็สามารถป้องกันได้ค่อนข้างง่าย คุณต้องตรวจสอบระยะเวลาของเซสชันบนเซิร์ฟเวอร์หลัก เซสชันที่เป็นอันตรายโดยเฉพาะอย่างยิ่งไม่ได้ใช้งานในสถานะธุรกรรม. คนเหล่านี้คือคนที่เพิ่งเปิดธุรกรรม ทำอะไรบางอย่าง และจากไป หรือเพียงแค่หยุดทำงาน หลงอยู่ในโค้ด
  • และสำหรับคุณในฐานะนักพัฒนา สิ่งสำคัญคือต้องทดสอบโค้ดของคุณเมื่อเกิดสถานการณ์เหล่านี้ มันไม่ยากที่จะทำ นี่จะเป็นการตรวจสอบที่มีประโยชน์ คุณจะหลีกเลี่ยงปัญหา "เด็ก" จำนวนมากที่เกี่ยวข้องกับการทำธุรกรรมระยะยาว

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ในกราฟเหล่านี้ ฉันต้องการแสดงให้คุณเห็นว่าสัญลักษณ์และพฤติกรรมของฐานข้อมูลเปลี่ยนแปลงไปอย่างไรหลังจากที่ฉันตรวจสอบสัญลักษณ์ที่มี VACUUM FULL ในกรณีนี้ นี่ไม่ใช่การผลิตสำหรับฉัน

ขนาดตารางกลับสู่สถานะการทำงานปกติทันทีที่สองสามเมกะไบต์ สิ่งนี้ไม่ได้ส่งผลกระทบอย่างมากต่อเวลาตอบสนองโดยเฉลี่ยของเซิร์ฟเวอร์

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

เรื่องที่สอง ซึ่งเรากระจายโหลดและเพิ่มประสิทธิภาพทรัพยากรเซิร์ฟเวอร์

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และสถานการณ์นี้มีลักษณะอย่างไร? โดยเฉพาะในกราฟเหล่านี้ ฉันยังเพิ่มระยะเวลาของธุรกรรมจากแบบจำลองสำหรับระยะเวลาของธุรกรรมด้วย กราฟอื่นๆ ทั้งหมดอ้างอิงถึงเซิร์ฟเวอร์หลักเท่านั้น

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

โดยเฉพาะอย่างยิ่งตามแผ่นทดสอบ เรายังคงอัปเดตยอดคงเหลือในบัญชีที่นั่นต่อไป และเรายังมีเวลาตอบสนองที่เสถียรสำหรับคำขอ การใช้ทรัพยากรที่เสถียร ทุกอย่างดีกับเรา

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ทุกอย่างเรียบร้อยดีจนกว่าจะถึงเวลาที่รายงานเหล่านี้เริ่มส่งกลับเนื่องจากข้อขัดแย้งกับการจำลอง และพวกมันจะยิงกลับเป็นระยะ

เราออนไลน์และเริ่มอ่านว่าทำไมสิ่งนี้จึงเกิดขึ้น และเราพบวิธีแก้ปัญหา

วิธีแก้ปัญหาแรกคือเพิ่มเวลาแฝงในการจำลอง เรารู้ว่ารายงานของเราใช้เวลา 3 ชั่วโมง เราตั้งค่าความล่าช้าในการจำลองเป็น 3 ชั่วโมง เรากำลังเปิดตัวทุกอย่าง แต่เรายังคงประสบปัญหากับรายงานที่บางครั้งถูกยกเลิก

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และเกิดอะไรขึ้นกับเซิร์ฟเวอร์ Master ในเวลานี้? และเราประสบปัญหาอย่างมากกับเซิร์ฟเวอร์หลัก ตอนนี้เราเห็นกราฟเมื่อฉันเปิดใช้งานการตั้งค่าทั้งสองนี้ และเราเห็นว่าเซสชันในแบบจำลองของเราเริ่มมีอิทธิพลต่อสถานการณ์บนเซิร์ฟเวอร์หลัก เธอมีผลกระทบเพราะเธอหยุดระบบสุญญากาศอัตโนมัติชั่วคราว ซึ่งจะทำให้เส้นตายหายไป ขนาดโต๊ะของเราพุ่งสูงขึ้นอีกครั้ง เวลาดำเนินการสืบค้นโดยเฉลี่ยทั่วทั้งฐานข้อมูลก็เพิ่มขึ้นอย่างรวดเร็วเช่นกัน autovacuums กระชับขึ้นเล็กน้อย

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

จะเป็นอย่างไรถ้าเราไม่รู้ว่าเมื่อก่อนฉันพูดถึงอะไร?

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

จากแท็บเล็ตที่ได้รับการทดสอบ ซึ่งเราอัปเดตยอดคงเหลือในบัญชี เราจะเห็นภาพเดียวกันทุกประการ เวลาในการอัปเดตบัญชีโดยเฉลี่ยจะค่อยๆ เป็นปกติเช่นกัน ทรัพยากรที่โปรเซสเซอร์ใช้ก็ลดลงเช่นกัน และจำนวนธุรกรรมต่อวินาทีกลับมาเป็นปกติ แต่เราก็กลับมาเป็นปกติอีกครั้งไม่เหมือนเดิมก่อนเกิดอุบัติเหตุ

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ไม่ว่าในกรณีใด เราได้รับการลดประสิทธิภาพลง เช่นในกรณีแรก XNUMX ถึง XNUMX เท่า และบางครั้งก็อาจมากกว่านั้น

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

  • อย่าเปิดใช้งาน hot_standby_feedback ใช่ไหม ใช่ ไม่แนะนำให้เปิดใช้งานโดยไม่มีเหตุผลที่ชัดเจนเป็นพิเศษ เนื่องจากการบิดนี้ส่งผลโดยตรงต่อเซิร์ฟเวอร์หลักและระงับการทำงานของระบบดูดอัตโนมัติที่นั่น ด้วยการเปิดใช้งานบนแบบจำลองบางส่วนและลืมมันไป คุณสามารถฆ่า Master และรับปัญหาใหญ่กับแอปพลิเคชันได้
  • เพิ่ม max_standby_streaming_delay หรือไม่ ใช่ สำหรับรายงาน นี่เป็นเรื่องจริง หากคุณมีรายงานที่ใช้เวลาสามชั่วโมงและคุณไม่ต้องการให้เกิดปัญหาเนื่องจากข้อขัดแย้งในการจำลอง ให้เพิ่มความล่าช้า รายงานระยะยาวไม่ต้องการข้อมูลที่มาถึงฐานข้อมูลในขณะนี้ หากคุณมีเวลาสามชั่วโมง แสดงว่าคุณใช้งานในช่วงข้อมูลเก่า และสำหรับคุณไม่ว่าจะล่าช้าสามชั่วโมงหรือหกชั่วโมงจะไม่สร้างความแตกต่างใด ๆ แต่คุณจะได้รับรายงานอย่างสม่ำเสมอและจะไม่มีปัญหาใด ๆ กับการตก
  • โดยปกติแล้ว คุณจะต้องควบคุมเซสชันที่ยาวนานบนเรพลิกา โดยเฉพาะอย่างยิ่งหากคุณตัดสินใจเปิดใช้งาน hot_standby_feedback บนเรพลิกา เพราะอะไรก็เกิดขึ้นได้ เรามอบแบบจำลองนี้ให้กับนักพัฒนาเพื่อให้เขาสามารถทดสอบคำขอได้ เขาเขียนคำขอบ้าๆ เขาเปิดมันและออกไปดื่มชา และเราก็ได้อาจารย์ที่จัดตั้งขึ้นแล้ว หรือบางทีเราใส่ใบสมัครผิดลงไป สถานการณ์มีความหลากหลาย เซสชันในเรพลิกาจะต้องได้รับการตรวจสอบอย่างรอบคอบเช่นเดียวกับบนมาสเตอร์
  • และหากคุณมีการสืบค้นที่รวดเร็วและยาวเกี่ยวกับเรพลิกา ในกรณีนี้ จะเป็นการดีกว่าถ้าแยกการสืบค้นเพื่อกระจายโหลด นี่คือลิงค์ไปยังสตรีมมิ่ง_ดีเลย์ สำหรับผู้ที่รวดเร็ว มีแบบจำลองหนึ่งรายการที่มีความล่าช้าในการจำลองเล็กน้อย สำหรับคำขอการรายงานที่ใช้เวลานาน ควรมีการจำลองที่สามารถล่าช้าได้ 6 ชั่วโมงหรือหนึ่งวัน นี่เป็นสถานการณ์ปกติโดยสมบูรณ์

เรากำจัดผลที่ตามมาในลักษณะเดียวกัน:

  • เราพบโต๊ะป่อง
  • และเราบีบอัดด้วยเครื่องมือที่สะดวกที่สุดที่เหมาะกับเรา

เรื่องที่สองจบลงที่นี่ มาต่อกันที่เรื่องที่สามกันเลย

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

ค่อนข้างเป็นเรื่องปกติสำหรับเราในการโยกย้าย

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

  • ผลิตภัณฑ์ซอฟต์แวร์ใด ๆ ที่กำลังเติบโต ข้อกำหนดสำหรับมันกำลังเปลี่ยนแปลง ยังไงก็ต้องการพัฒนา และมันเกิดขึ้นที่เราจำเป็นต้องอัปเดตข้อมูลในตาราง กล่าวคือ เพื่อเรียกใช้การอัปเดตในแง่ของการโยกย้ายสำหรับฟังก์ชันใหม่ที่เรานำเสนอซึ่งเป็นส่วนหนึ่งของการพัฒนาของเรา
  • รูปแบบข้อมูลเก่าไม่เป็นที่พอใจ สมมติว่าตอนนี้เราหันไปที่ตารางที่สอง ซึ่งฉันมีธุรกรรมในบัญชีเหล่านี้ และสมมติว่าพวกมันอยู่ในรูเบิล และเราตัดสินใจที่จะเพิ่มความแม่นยำและทำในโกเปค และสำหรับสิ่งนี้ เราจำเป็นต้องทำการอัปเดต: คูณฟิลด์ด้วยจำนวนธุรกรรมด้วยหนึ่งร้อย
  • ในโลกปัจจุบัน เราใช้เครื่องมือควบคุมเวอร์ชันฐานข้อมูลอัตโนมัติ เอาเป็นว่า ลิควิเบส. เราลงทะเบียนการโยกย้ายของเราที่นั่น เราทดสอบบนฐานทดสอบของเรา ทุกอย่างปกติดี. การอัปเดตกำลังดำเนินไป มันบล็อกการทำงานอยู่ระยะหนึ่ง แต่เราได้รับข้อมูลที่อัปเดต และเราสามารถเปิดตัวฟังก์ชันใหม่เกี่ยวกับเรื่องนี้ได้ ทุกอย่างได้รับการทดสอบและตรวจสอบแล้ว ทุกอย่างได้รับการยืนยัน
  • เราดำเนินงานตามแผนและดำเนินการโยกย้าย

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

นี่คือการย้ายข้อมูลพร้อมการอัปเดตที่แสดงตรงหน้าคุณ เนื่องจากสิ่งเหล่านี้เป็นธุรกรรมในบัญชีของฉัน จานจึงมีขนาด 15 GB และเนื่องจากเราอัปเดตทุกบรรทัด เราจึงเพิ่มขนาดของตารางเป็นสองเท่าด้วยการอัปเดต เพราะเราเขียนใหม่ทุกบรรทัด

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

เราดำเนินการย้ายข้อมูลและประสบปัญหาอีกครั้ง

การย้ายข้อมูลสำเร็จ แต่:

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

และนี่ก็บวมอีกครั้ง ซึ่งทำให้ชีวิตเราพังอีกครั้ง

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และถ้าเราหันไปที่ตารางพร้อมบัญชี เราจะเห็นว่าเวลาร้องขอโดยเฉลี่ยเพิ่มขึ้นเป็นสองเท่าสำหรับตารางนี้ โหลดบนโปรเซสเซอร์และจำนวนบรรทัดที่จัดเรียงในหน่วยความจำเพิ่มขึ้นสูงกว่า 7,5 แต่น้อยกว่า และเพิ่มขึ้น 2 เท่าในกรณีของโปรเซสเซอร์ 1,5 เท่าในกรณีของการดำเนินการบล็อกนั่นคือ ประสิทธิภาพของเซิร์ฟเวอร์ลดลง และผลที่ตามมาก็คือ - ประสิทธิภาพการทำงานของแอปพลิเคชันของเราลดลง ในขณะเดียวกันจำนวนการโทรยังคงอยู่ที่ระดับเดิมโดยประมาณ

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

และสิ่งสำคัญที่นี่คือต้องเข้าใจวิธีการย้ายข้อมูลอย่างถูกต้อง และพวกเขาจำเป็นต้องทำ เราทำการย้ายข้อมูลเหล่านี้ค่อนข้างสม่ำเสมอ

  • การโยกย้ายขนาดใหญ่ดังกล่าวไม่ได้เกิดขึ้นโดยอัตโนมัติ พวกเขาจะต้องอยู่ภายใต้การควบคุมเสมอ
  • ต้องมีผู้มีความรู้คอยควบคุมดูแล หากคุณมี DBA ในทีมของคุณ ก็ปล่อยให้ DBA จัดการไป มันเป็นงานของเขา ถ้าไม่เช่นนั้น ก็ปล่อยให้ผู้ที่มีประสบการณ์มากที่สุดเป็นผู้ดำเนินการ ซึ่งรู้วิธีทำงานกับฐานข้อมูล
  • สคีมาฐานข้อมูลใหม่ แม้ว่าเราจะอัปเดตหนึ่งคอลัมน์ เราก็จะเตรียมการเป็นระยะๆ เสมอ กล่าวคือ ล่วงหน้าก่อนที่แอปพลิเคชันเวอร์ชันใหม่จะเปิดตัว:
  • มีการเพิ่มฟิลด์ใหม่ซึ่งเราจะบันทึกข้อมูลที่อัปเดต
  • เราถ่ายโอนข้อมูลจากฟิลด์เก่าไปยังฟิลด์ใหม่โดยเป็นส่วนเล็กๆ เราจะทำเช่นนี้ทำไม? ประการแรก เราควบคุมกระบวนการของกระบวนการนี้อยู่เสมอ เรารู้ว่าเราได้โอนแบทช์ไปจำนวนมากแล้ว และเรายังเหลืออีกจำนวนมาก
  • และผลเชิงบวกประการที่สองคือระหว่างแต่ละชุดดังกล่าวเราปิดธุรกรรม เปิดรายการใหม่ และทำให้ autovacuum ทำงานตามแผ่น ทำเครื่องหมายเส้นตายเพื่อนำกลับมาใช้ใหม่
  • สำหรับบรรทัดที่จะปรากฏขึ้นในขณะที่แอปพลิเคชันกำลังทำงาน (เรายังมีแอปพลิเคชันเก่าทำงานอยู่) เราจะเพิ่มทริกเกอร์ที่เขียนค่าใหม่ลงในฟิลด์ใหม่ ในกรณีของเรา นี่คือการคูณด้วยหนึ่งร้อยของค่าเก่า
  • หากเราดื้อรั้นโดยสิ้นเชิงและต้องการฟิลด์เดียวกัน เมื่อการย้ายทั้งหมดเสร็จสิ้นและก่อนที่จะเปิดตัวแอปพลิเคชันเวอร์ชันใหม่ เราก็เพียงเปลี่ยนชื่อฟิลด์ อันเก่าจะได้รับชื่อที่ประดิษฐ์ขึ้นและฟิลด์ใหม่จะถูกเปลี่ยนชื่อเป็นอันเก่า
  • และหลังจากนั้นเราก็เปิดตัวแอปพลิเคชันเวอร์ชันใหม่เท่านั้น

และในเวลาเดียวกันเราจะไม่บวมและไม่ต้องทนทุกข์ทรมานในแง่ของประสิทธิภาพ

นี่คือจุดสิ้นสุดของเรื่องที่สาม

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

และตอนนี้รายละเอียดเพิ่มเติมเล็กน้อยเกี่ยวกับเครื่องมือที่ฉันพูดถึงในเรื่องแรก

ก่อนที่จะค้นหาส่วนขยาย คุณต้องติดตั้งส่วนขยายก่อน pgstattuple.

เพื่อที่คุณจะได้ไม่ต้องถามคำถาม เราได้เขียนคำถามเหล่านี้ไว้ในงานของเราแล้ว คุณสามารถใช้มันได้ มีสองคำขอที่นี่

  • อันแรกใช้เวลาค่อนข้างนานในการทำงาน แต่จะแสดงค่าการขยายตัวที่แน่นอนจากตาราง
  • อันที่สองทำงานเร็วขึ้นและมีประสิทธิภาพมากเมื่อคุณต้องประเมินอย่างรวดเร็วว่ามีอาการบวมหรือไม่ตามตาราง และคุณควรเข้าใจด้วยว่าการบวมนั้นมีอยู่ในตาราง Postgres เสมอ นี่คือคุณลักษณะของรุ่น MVCC
  • และการขยายตัว 20% ถือเป็นเรื่องปกติสำหรับโต๊ะในกรณีส่วนใหญ่ นั่นคือคุณไม่ควรกังวลและบีบอัดตารางนี้

เราค้นพบวิธีระบุตารางที่เต็มไปด้วยข้อมูลที่ไม่มีประโยชน์

ตอนนี้เกี่ยวกับวิธีการแก้ไขอาการบวม:

  • หากเรามีแท็บเล็ตขนาดเล็กและมีดิสก์ที่ดี นั่นคือบนแท็บเล็ตที่มีขนาดไม่เกินกิกะไบต์ ก็ค่อนข้างเป็นไปได้ที่จะใช้ VACUUM FULL เขาจะรับกุญแจพิเศษจากคุณบนโต๊ะสักสองสามวินาทีและโอเค แต่เขาจะทำทุกอย่างอย่างรวดเร็วและรุนแรง VACUUM FULL ทำหน้าที่อะไร? ใช้การล็อคแบบเอกสิทธิ์เฉพาะบุคคลบนโต๊ะและเขียนแถวสดจากตารางเก่าลงในตารางใหม่ และในที่สุดเขาก็เข้ามาแทนที่พวกเขา มันจะลบไฟล์เก่าและแทนที่ไฟล์เก่าด้วยไฟล์ใหม่ แต่ตลอดระยะเวลาการทำงานนั้นจะต้องมีการล็อคแบบเอกสิทธิ์เฉพาะบุคคลบนโต๊ะ ซึ่งหมายความว่าคุณไม่สามารถทำอะไรกับตารางนี้ได้: ห้ามเขียนหรืออ่านหรือแก้ไขตาราง และ VACUUM FULL ต้องการพื้นที่ดิสก์เพิ่มเติมในการเขียนข้อมูล
  • เครื่องมือถัดไป pg_repack. โดยหลักการแล้วมันคล้ายกับ VACUUM FULL มาก เนื่องจากมันยังเขียนข้อมูลจากไฟล์เก่าไปเป็นไฟล์ใหม่และแทนที่ในตารางด้วย แต่ในขณะเดียวกันก็ไม่ได้ทำการล็อคแบบเอกสิทธิ์เฉพาะบุคคลบนโต๊ะในช่วงเริ่มต้นของการทำงาน แต่จะใช้เฉพาะในช่วงเวลาที่มีข้อมูลพร้อมแล้วเท่านั้นเพื่อที่จะแทนที่ไฟล์ ข้อกำหนดทรัพยากรดิสก์นั้นคล้ายคลึงกับข้อกำหนดของ VACUUM FULL คุณต้องมีเนื้อที่ดิสก์เพิ่มเติม และบางครั้งสิ่งนี้อาจสำคัญหากคุณมีตารางเทราไบต์ และค่อนข้างต้องใช้โปรเซสเซอร์มากเพราะสามารถทำงานร่วมกับ I/O ได้
  • ยูทิลิตี้ที่สามคือ pgกะทัดรัด. ควรใช้ความระมัดระวังกับทรัพยากรมากขึ้นเนื่องจากทำงานตามหลักการที่แตกต่างกันเล็กน้อย แนวคิดหลักของ pgcompacttable คือการย้ายแถวสดทั้งหมดไปที่จุดเริ่มต้นของตารางโดยใช้การอัปเดตในตาราง แล้วมันก็เกิดสุญญากาศบนโต๊ะนี้ เพราะเรารู้ว่าเรามีแถวสดที่จุดเริ่มต้นและแถวตายที่ตอนท้าย และสูญญากาศเองก็ตัดหางนี้ออกนั่นคือ ไม่ต้องการพื้นที่ดิสก์เพิ่มเติมมากนัก และในขณะเดียวกันก็ยังสามารถบีบคั้นในแง่ของทรัพยากรได้

ทุกอย่างมีเครื่องมือ

ข้อผิดพลาดทั่วไปในแอปพลิเคชันที่นำไปสู่การขยายใน postgresql อันเดรย์ ซาลนิคอฟ

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

  • https://www.slideshare.net/alexius2Mb/where-is-the-space-postgres - นี่คือรายงานจากเพื่อนร่วมงานของฉัน โดยทั่วไปแล้วพื้นที่ของ Postgres จะไปที่ไหนระหว่างการทำงานและการใช้ชีวิต และมีชิ้นส่วนทางเทคนิคที่ใหญ่และมีรายละเอียดมากสำหรับผู้ดูแลระบบฐานข้อมูลเกี่ยวกับการบวม
  • https://github.com/dataegret/pg-utils – นี่คือลิงก์ไปยังพื้นที่เก็บข้อมูลของเรา ซึ่งเราจัดเก็บสคริปต์ที่มีประโยชน์มากมายสำหรับการตรวจสอบสถานะของฐานข้อมูล ที่นั่นคุณจะพบสคริปต์เพื่อค้นหาการขยายตัว
  • สาม и ที่สี่ ลิงก์ไปยังเครื่องมือที่จะช่วยให้คุณย่อขนาดป้ายได้
  • http://blog.dataegret.com/2Mb018/03/postgresql-bloatbusters.html – นี่คือโพสต์จากเพื่อนร่วมงานของฉัน ที่นั่นเขาค่อนข้างจริงจังและวิเคราะห์รายละเอียดทางเทคนิคในระดับที่ใกล้เคียงกับผู้ดูแลระบบ

ฉันพยายามแสดงเรื่องราวสยองขวัญให้นักพัฒนามากขึ้น เพราะพวกเขาเป็นลูกค้าฐานข้อมูลโดยตรงของเรา และต้องเข้าใจว่าการกระทำใดนำไปสู่อะไร ฉันหวังว่าฉันจะประสบความสำเร็จ ขอขอบคุณสำหรับความสนใจของคุณ!

คำถาม

ขอบคุณสำหรับรายงาน! คุณได้พูดคุยเกี่ยวกับวิธีที่คุณสามารถระบุปัญหาได้ พวกเขาจะเตือนได้อย่างไร? นั่นคือฉันมีสถานการณ์ที่คำขอหยุดทำงานไม่เพียงเพราะพวกเขาเข้าถึงบริการภายนอกบางอย่างเท่านั้น นี่เป็นเพียงการเข้าร่วมที่ดุเดือด มีคำขอเล็กๆ น้อยๆ ที่ไม่เป็นอันตรายซึ่งวนเวียนอยู่เป็นเวลาหนึ่งวัน จากนั้นก็เริ่มทำเรื่องไร้สาระ นั่นคือคล้ายกับสิ่งที่คุณอธิบายมาก จะติดตามสิ่งนี้ได้อย่างไร? นั่งดูไปเรื่อยๆว่าคำขอไหนติดขัด? สิ่งนี้สามารถป้องกันได้อย่างไร?

ในกรณีนี้ นี่เป็นงานสำหรับผู้ดูแลระบบของบริษัทของคุณ ไม่จำเป็นสำหรับ DBA

ฉันเป็นผู้ดูแลระบบ

PostgreSQL มีมุมมองที่เรียกว่า pg_stat_activity ที่แสดงข้อความค้นหาที่ห้อยต่องแต่ง และคุณจะเห็นว่ามันค้างอยู่ตรงนั้นนานแค่ไหน

ต้องเข้ามาดูทุกๆ 5 นาทีมั้ย?

ตั้งค่า cron และตรวจสอบ หากคุณมีคำขอระยะยาวก็เขียนจดหมายได้เลย นั่นคือคุณไม่จำเป็นต้องมองด้วยตา มันสามารถทำงานอัตโนมัติได้ คุณจะได้รับจดหมายคุณก็ตอบสนองต่อมัน หรือคุณสามารถถ่ายภาพอัตโนมัติ

มีเหตุผลที่ชัดเจนว่าทำไมสิ่งนี้ถึงเกิดขึ้น?

ฉันได้ระบุไว้บางส่วน ตัวอย่างอื่นๆ ที่ซับซ้อนกว่า และสามารถสนทนาได้นาน

ขอบคุณสำหรับรายงาน! ฉันต้องการชี้แจงเกี่ยวกับยูทิลิตี้ pg_repack ถ้าเธอไม่ทำการล็อคพิเศษ งั้น...

เธอทำการล็อคพิเศษ

... ฉันอาจสูญเสียข้อมูลได้ แอปพลิเคชันของฉันไม่ควรบันทึกสิ่งใดในช่วงเวลานี้หรือไม่

ไม่ มันทำงานได้อย่างราบรื่นกับตาราง เช่น pg_repack จะโอนสายสดทั้งหมดที่มีอยู่ก่อน โดยธรรมชาติแล้วการเข้าสู่ตารางบางอย่างเกิดขึ้นที่นั่น เขาเพิ่งจะปล่อยผมหางม้านี้ออก

นั่นคือในที่สุดเขาก็ทำมันจริงเหรอ?

ในท้ายที่สุด เขาใช้การล็อคพิเศษเพื่อสลับไฟล์เหล่านี้

จะเร็วกว่า VACUUM FULL หรือไม่?

VACUUM FULL ทันทีที่เริ่มต้น จะมีการล็อคพิเศษทันที และจนกว่าเขาจะทำทุกอย่างเขาจะไม่ยอมปล่อยเธอไป และ pg_repack จะใช้การล็อคแบบเอกสิทธิ์เฉพาะบุคคลเฉพาะในเวลาที่มีการเปลี่ยนไฟล์เท่านั้น ในขณะนี้คุณจะไม่เขียนที่นั่น แต่ข้อมูลจะไม่สูญหายทุกอย่างจะเรียบร้อยดี

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

ใช่. Postgres จะไม่ลบบรรทัด เขามีความเฉพาะเจาะจงเช่นนี้ หากเราอัปเดตบรรทัด เราจะทำเครื่องหมายบรรทัดเก่าว่าถูกลบแล้ว รหัสของธุรกรรมที่เปลี่ยนบรรทัดนี้ปรากฏขึ้นที่นั่น และเราเขียนบรรทัดใหม่ และเรามีเซสชันที่อาจอ่านได้ เมื่อถึงจุดหนึ่งพวกเขาก็แก่มาก และสาระสำคัญของวิธีการทำงานของ autovacuum ก็คือมันต้องผ่านเส้นเหล่านี้และทำเครื่องหมายว่าไม่จำเป็น และคุณสามารถเขียนทับข้อมูลตรงนั้นได้

ฉันเข้าใจ. แต่นั่นไม่ใช่สิ่งที่เป็นคำถาม ฉันไม่จบ สมมติว่าเรามีโต๊ะ มีเขตข้อมูลที่มีขนาดแปรผัน และหากฉันพยายามแทรกสิ่งใหม่ มันอาจไม่พอดีกับเซลล์เก่า

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

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

เป็นที่ยอมรับมาก เราใช้สิ่งนี้ทุกที่ และเนื่องจากเราไม่มีบริการของตัวเอง เราจึงให้การสนับสนุนระยะไกล เราจึงมีลูกค้าที่หลากหลาย และทุกคนก็พอใจกับสิ่งนี้อย่างเต็มที่ นั่นคือเรามีงาน cron ที่ตรวจสอบ ระยะเวลาของเซสชันเป็นเพียงการตกลงกับลูกค้า ซึ่งก่อนหน้านั้นเราไม่เห็นด้วย อาจเป็นนาที อาจเป็น 10 นาที ขึ้นอยู่กับภาระบนฐานและวัตถุประสงค์ แต่เราทุกคนใช้ pg_stat_activity

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

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

นั่นคือมันปิดทันทีหลังจากการอัพเดต?

ขึ้นอยู่กับการตั้งค่า ฉันตั้งชื่อการตั้งค่าหนึ่งรายการ นี่คือการคอมมิตอัตโนมัติ มันค่อนข้างจะธรรมดา หากเปิดใช้งาน แสดงว่าธุรกรรมได้เปิดและปิดแล้ว เว้นแต่คุณจะระบุไว้อย่างชัดเจนว่า "เริ่มต้นธุรกรรม" และ "สิ้นสุดธุรกรรม" แต่เพียงแค่เปิดตัวคำขอในเซสชัน

สวัสดี! ขอบคุณสำหรับรายงาน! ลองจินตนาการว่าเรามีฐานข้อมูลที่บวมและบวมแล้วพื้นที่บนเซิร์ฟเวอร์จะหมด มีเครื่องมือใดบ้างในการแก้ไขสถานการณ์นี้?

พื้นที่บนเซิร์ฟเวอร์จะต้องมีการตรวจสอบอย่างเหมาะสม

เช่น สธ. ไปดื่มชา อยู่ที่รีสอร์ท เป็นต้น

เมื่อระบบไฟล์ถูกสร้างขึ้น พื้นที่สำรองข้อมูลอย่างน้อยบางประเภทจะถูกสร้างขึ้นโดยไม่ได้เขียนข้อมูล

จะเกิดอะไรขึ้นถ้ามันต่ำกว่าศูนย์โดยสิ้นเชิง?

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

มีเครื่องมืออื่นอีกไหม?

ทำด้วยมือเสมอ และในท้องถิ่นก็ชัดเจนว่าอะไรคือสิ่งที่ดีที่สุดที่ควรทำ เนื่องจากข้อมูลบางอย่างมีความสำคัญและบางส่วนก็ไม่มีความสำคัญ และสำหรับแต่ละฐานข้อมูลและแอปพลิเคชันที่ใช้งานได้ก็ขึ้นอยู่กับธุรกิจ มีการตัดสินใจในท้องถิ่นเสมอ

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

พวกเขาแพ็คมันด้วย

แต่สุญญากาศไม่ส่งผลต่อดัชนี?

บางส่วนทำงานร่วมกับดัชนี ตัวอย่างเช่น pg_rapack, pgcompacttable สุญญากาศจะสร้างดัชนีขึ้นใหม่และส่งผลต่อดัชนีเหล่านั้น ด้วย VACUUM FULL แนวคิดก็คือการเขียนทับทุกสิ่ง กล่าวคือ ใช้ได้กับทุกคน

และคำถามที่สอง ฉันไม่เข้าใจว่าเหตุใดรายงานเกี่ยวกับการจำลองจึงขึ้นอยู่กับการจำลองข้อมูลอย่างมาก สำหรับฉันดูเหมือนว่ามีการอ่านรายงานและการจำลองแบบคือการเขียน

อะไรทำให้เกิดความขัดแย้งในการจำลองแบบ? เรามีผู้เชี่ยวชาญในกระบวนการที่เกิดขึ้น เรามีเรื่องดูดฝุ่นในรถยนต์เกิดขึ้น จริงๆ แล้ว autovacuum ทำหน้าที่อะไร? เขากำลังตัดบรรทัดเก่าออก หากในเวลานี้ เรามีคำขอเกี่ยวกับแบบจำลองที่อ่านบรรทัดเก่าเหล่านี้ และบน Master มีสถานการณ์เกิดขึ้นที่ autovacuum ได้ทำเครื่องหมายบรรทัดเหล่านี้ว่าเป็นไปได้สำหรับการเขียนทับ เราก็ได้เขียนทับแล้ว และเราได้รับแพ็กเก็ตข้อมูล เมื่อเราต้องการเขียนบรรทัดเหล่านั้นที่คำขอต้องการบนเรพลิกา กระบวนการจำลองแบบจะรอจนหมดเวลาที่คุณกำหนดค่าไว้ จากนั้น PostgreSQL จะตัดสินใจว่าอะไรสำคัญกว่ากัน และการจำลองแบบมีความสำคัญต่อเขามากกว่าคำขอ และเขาจะบันทึกคำขอเพื่อทำการเปลี่ยนแปลงเหล่านี้กับแบบจำลอง

อันเดรย์ ฉันมีคำถาม กราฟที่ยอดเยี่ยมเหล่านี้ที่คุณแสดงระหว่างการนำเสนอนี่เป็นผลมาจากการทำงานที่เป็นประโยชน์บางอย่างของคุณหรือเปล่า? กราฟถูกสร้างขึ้นมาอย่างไร?

นี่คือบริการ โอเคมิเตอร์.

นี่เป็นผลิตภัณฑ์เชิงพาณิชย์หรือไม่?

ใช่. นี่เป็นผลิตภัณฑ์เชิงพาณิชย์

ที่มา: will.com

เพิ่มความคิดเห็น