דף קטלוג עם 30,000 מוצרים מייצר 150 שאילתות SQL, ושרת MySQL מגיע ל-90% ניצול CPU — זו לא בעיית קוד, זה חוסר בפרופילינג. ללא slow_query_log, אתה מנחש איזו שאילתה צורכת משאבים — פרופילינג נותן תשובה ברורה. במשך יותר מעשור ביצענו יותר מ-50 ביקורות MySQL על פרויקטים של 1C-Bitrix: בכל פרויקט שני היו דפוסים דומים — שאילתות N+1, אינדקסים חסרים בטבלאות מפתח (b_iblock_element, b_catalog_price), באפרים לא אופטימליים של InnoDB. התוצאה — דפים נטענים תוך 5–7 שניות, השרת קורס בעומס שיא. לצורך אבחון אנו משתמשים ב-slow query log, pt-query-digest ו-EXPLAIN ANALYZE, וכן בטראקר המובנה של Bitrix לשאילתות ORM. חיסכון ממוצע בזמן טעינת דף לאחר אופטימיזציה — 50–70%. פרופילינג של שאילתות MySQL ב-Bitrix מפחית את עומס ה-CPU פי 3-5, ואת ה-TTFB ב-60-80%. חיסכון אופייני בתשתית שרתים — משמעותי בהתאם להיקף.
מדריך שלב-אחר-שלב: הפעלת slow_query_log
- בשרת MySQL בצע
SET GLOBAL slow_query_log = 'ON'; - הגדר את הסף:
SET GLOBAL long_query_time = 0.5; - ציין את קובץ היומן:
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log'; - הפעל רישום שאילתות ללא אינדקסים:
SET GLOBAL log_queries_not_using_indexes = 1; - לקביעת תצורה קבועה, כתוב את הפרמטרים ב-
/etc/mysql/conf.d/slow.cnf.
כיצד מתבצע פרופילינג של שאילתות MySQL ב-1C-Bitrix?
הבסיס הוא יומן ה-slow query של MySQL. אנו מפעילים אותו למשך 1–2 ימים עם סף של 0.5 שניות. עבור סביבת ייצור אנו משתמשים בקובץ תצורה עם הפרמטר min_examined_row_limit = 100 כדי לסנן שאילתות מהירות לפי מפתח ראשי.
ניתוח באמצעות pt-query-digest
Percona Toolkit הוא התקן התעשייתי. הכלי מקבץ שאילתות לפי תבנית, מציג תדירות וזמן כולל.
pt-query-digest /var/log/mysql/slow.log --limit 20 --report-format query_report > /tmp/slow_report.txt בפרויקטים של Bitrix, 5 השאילתות הבעייתיות המובילות כוללות בדרך כלל:
- N+1 בעת שליפת מאפיינים (
pt-query-digest /var/log/mysql/slow.log --limit 20 --report-format query_report > /tmp/slow_report.txt) - שליפת מחיר לכל רשומה (
b_iblock_element_prop_s*) - COUNT ללא אינדקס
- חיפוש טקסט מלא בטבלה גדולה
- תחרות על עדכוני סשן (
b_catalog_price)
EXPLAIN ו-EXPLAIN ANALYZE
עבור כל שאילתה איטית אנו מריצים b_user_session. סימנים קריטיים: type = ALL (סריקה מלאה), rows > 1000, Extra: Using filesort / temporary.
EXPLAIN SELECT be.ID, be.NAME, bp.VALUE
FROM b_iblock_element be
LEFT JOIN b_iblock_element_prop_s5 bp ON bp.IBLOCK_ELEMENT_ID = be.ID
WHERE be.IBLOCK_ID = 12
AND be.ACTIVE = 'Y'
AND be.WF_STATUS_ID = 1
ORDER BY be.SORT ASC
LIMIT 48 OFFSET 0;
-- EXPLAIN ANALYZE (MySQL 8.0+) показывает реальное время
EXPLAIN ANALYZE SELECT ... ;EXPLAIN עובד פי שניים מהר יותר מניתוח תכנית ידני — הוא חושף מיד צווארי בקבוק.
אילו אינדקסים קריטיים עבור Bitrix?
מספר אינדקסים שחסרים לעיתים קרובות בהתקנה סטנדרטית:
CREATE INDEX idx_iblock_element_active_sort ON b_iblock_element (IBLOCK_ID, ACTIVE, WF_STATUS_ID, SORT);
CREATE INDEX idx_catalog_price_product_group ON b_catalog_price (PRODUCT_ID, CATALOG_GROUP_ID);
CREATE INDEX idx_user_session_timestamp ON b_user_session (TIMESTAMP_X);
לאחר יצירת האינדקס, הרץ שוב EXPLAIN SELECT be.ID, be.NAME, bp.VALUE FROM b_iblock_element be LEFT JOIN b_iblock_element_prop_s5 bp ON bp.IBLOCK_ELEMENT_ID = be.ID WHERE be.IBLOCK_ID = 12 AND be.ACTIVE = 'Y' AND be.WF_STATUS_ID = 1 ORDER BY be.SORT ASC LIMIT 48 OFFSET 0; -- EXPLAIN ANALYZE (MySQL 8.0+) показывает реальное время EXPLAIN ANALYZE SELECT ... ; — ה-type אמור להשתנות מ-EXPLAIN ANALYZE ל-CREATE INDEX idx_iblock_element_active_sort ON b_iblock_element (IBLOCK_ID, ACTIVE, WF_STATUS_ID, SORT); CREATE INDEX idx_catalog_price_product_group ON b_catalog_price (PRODUCT_ID, CATALOG_GROUP_ID); CREATE INDEX idx_user_session_timestamp ON b_user_session (TIMESTAMP_X); .
מדוע N+1 היא בעיה נפוצה ב-Bitrix ORM?
D7 ORM מייצר לעיתים קרובות שאילתות N+1. אבחון באמצעות הטראקר המובנה:
\Bitrix\Main\Application::getConnection()->setTracker(new \Bitrix\Main\DB\SqlTracker(50));
// В конце запроса
$tracker = \Bitrix\Main\Application::getConnection()->getTracker();
foreach ($tracker->getQueries() as $query) {
if ($query->getTime() > 0.1)
error_log($query->getSql() . ' [' . $query->getTime() . 's]');
}
// Исправление: вместо поштучных запросов — batch выборка
$ids = array_column($elements->fetchAll(), 'ID');
$prices = PriceTable::getList(['filter' => ['PRODUCT_ID' => $ids]])->fetchAll();
$priceMap = array_column($prices, null, 'PRODUCT_ID'); דוגמה לדוח pt-query-digest
# Query 1: 12.5k calls, avg 0.8s, 97% of total time
SELECT ... FROM b_iblock_element_prop_s8 ...
# Query 2: 500 calls, avg 2.1s
SELECT ... FROM b_catalog_price ... מקרה בוחן: סיטונאי
מהניסיון שלנו: אתר Bitrix "Small Business", קטלוג של 28,000 פריטים, 3,000 מבקרים ביום. שרת 4 CPU, 8 GB RAM. עומס MySQL — 85–90% CPU. EXPLAIN הראה ש-92% מהזמן מושקע ב-ALL (טבלת מאפיינים מסוג מחרוזת) — סריקה מלאה על 280,000 שורות. אינדקס יחיד ref הפחית את העומס ל-15–20% CPU ללא שינויי קוד. TTFB ירד מ-5 שניות ל-1.2 שניות. בממוצע בפרויקטים שלנו, החיסכון בזמן טעינת דף הוא 50–70%. הפחתת CPU מ-85% ל-15% מביאה לחיסכון משמעותי לטווח ארוך.
כלי ניטור
| כלי | סוג | יתרון |
|---|---|---|
| Percona Monitoring and Management | מלא | גרפים של QPS, זמן השהיה, שאילתות מובילות בזמן אמת |
| MySQL Workbench Performance Schema | ממשק גרפי | נוח לאבחון חד-פעמי |
| Grafana + mysql_exporter | אינטגרציה | משתלב בניטור קיים |
לפרטים נוספים על slow query log: ויקיפדיה. אנו ממליצים גם על התיעוד הרשמי של D7 ORM למניעת N+1.
מה כלול בעבודה
- אבחון: הפעלת slow log, איסוף נתונים, דוח עם 20 השאילתות האיטיות המובילות.
- אופטימיזציה: יצירת אינדקסים, שיפוץ N+1, כוונון באפרים של MySQL.
- ניטור: התקנת PMM או Grafana, הגדרת התראות.
- תיעוד והדרכה: תיאור כל השינויים, המלצות למפתחים.
אם גילית שאילתות איטיות — הזמן ביקורת פרופילינג MySQL. אנו מבטיחים — לאחר האופטימיזציה שלנו, עומס מסד הנתונים יירד פי 3-5, וה-TTFB ב-60-80%. צור קשר להערכה חינמית של הפרויקט שלך.
הערכות זמן
| היקף | תחום | משך |
|---|---|---|
| ביקורת | הפעלת slow log, ניתוח, דוח | 1–2 ימים |
| אופטימיזציה | אינדקסים, שיפוץ N+1, כוונון באפרים | 3–7 ימים |
| ניטור | PMM או Grafana + התראות | 2–3 ימים |
הזמן פרופילינג שאילתות MySQL עבור 1C-Bitrix — קבל דוח מפורט והמלצות תוך יום.

