לקחת את הביצועים ברצינות, בעזרת MySQL Performance Schema

פוסטים בסדרה:
“תסביר לי” – גרסת ה SQL
לקחת את הביצועים ברצינות, בעזרת MySQL Performance Schema
נושאים מתקדמים ב MySQL: חלק א’ – עבודה עם JSON
נושאים מתקדמים ב MySQL: חלק ב’ – json_each  ו Generated Columns

—-

טוב. בפוסט הקודם דיברנו על Explain, הפקודה שמציגה לנו את תוכנית ההרצה של שאילתה נבחרת.

איך יודעים אלו שאילתות רצות לאט?

הדרך הנפוצה ביותר היא בעזרת ה slow-log.

MySQL slow Query Log

  • בכדי להפעיל אותו יש לקבוע 1 בפרמטר slow_query_log. הוא ירשום כל שאילתה שארכה יותר מ 10 שניות.
  • ניתן לקבוע סף אחר בעזרת הפרמטר long_query_time, בו ניתן לקבוע גם ערכים שאינם מספרים שלמים, למשל “0.6”.
    • אם תקבעו בפרמטר ערך של 0 – סביר להניח שכתיבה ללוג תהיה החלק האטי במערכת שלכם 😀.
    • שינוי הפרמטר ישפיע רק על connections חדשים ל DB, כך שאם שיניתם את הערך ושרת סורר מחזיק חיבורים פתוחים בעזרת ה connection pool שלו, שאילתות שעמודות בסף החדש שנקבע – לא ירשמו ללוג.
  • בכדי לשלוף ערכים מה slow log פשוט בצעו שאילתה על הטבלה mysql.slow_log.
    • למשל: SELECT * FROM mysql.slow_log ORDER BY start_time DESC limit 50;
    • אני לא זוכר מה הם ערכי ברירת המחדל, ייתכן וצריך לשנות עוד קונפיגורציה בכדי שהלוג ייכתב לטבלה.

התוצאה נראית כך:

  • user_host (הסתרתי את כתובות ה IP) – עוזר להבין איזה צרכן יצר את השאילתה. זה חשוב ב DB מונולטיים – המשרתים צרכנים רבים ומגוונים.
  • lock_time (בשניות) – הזמן בו ממתינים ל”תפיסת מנעול” על מנת להתחיל ולהריץ את השאילתה. למשל: הטבלה נעולה ע”י פעולה אחרת.
  • query_time (בשניות) – זמן הריצה הכולל.
    • הרצה עוקבת של אותה שאילתה אמורה להיות מהירה יותר – כאשר הנתונים טעונים כבר ל buffer pool.
    • כמובן שגם השפעות חיצוניות שונות (שאילתות אחרות שרצות ברקע ומתחרות על משאבי ה DB Server) – ישפיעו על זמני הריצה. אני לא מכיר דרך פשוטה להוסיף מדדים כמו cpu ו/או disk_queue_depth לשאילתה, בכדי לקשר את זמני הביצוע שלה למה שהתרחש ב DB Server באותו הרגע.
  • מספר שורות שנשלח / נסרק – בשונה מפקודת ה Explain, זהו המספר בפועל ולא הערכה.
    • ייתכנו מקרים בהם Explain יעריך תוכנית אחת – אך ה slow log יראה שבוצע משהו אחר (יקר יותר) בפועל. זה עלול לקרות.
  • db – שם בסיס הנתונים (= סכמה) שבה בוצעה השאילתה.
    • למשל, אני יכול לבחור לפלטר את הסכמה של redash – כי רצות שהם הרבה שאילתות אטיות, וזה בסדר מבחינתי.

שווה עוד לציין ששאילתות שלא משתמשות באינדקסים – אינן נרשמות ל slow_log כהתנהגות ברירת-מחדל. אפשר לשנות זאת בעזרת הפרמטר log_queries_not_using_indexes. ייתכן ויהיה שווה גם לקבוע ערך בפרמטר log_throttle_queries_not_using_indexes – כדי שלא תוצפו.

ה Performance Schema

אפשרות מודרנית יותר לאתר שאילתות אטיות היא על בסיס ה Performance Schema (בקיצור: ps)

מאז MySQL 5.6 ה ps מופעלת כברירת מחדל, אבל אם אתם רצים על AWS RDS – יהיה עליכם להדליק אותה ולבצע restart לשרת, לפני שתוכלו להשתמש בה.

השאילתה ה”קצרה” לשלוף נתונים של שאילתות אטיות מה ps היא:

SELECT * FROM sys.statements_with_runtimes_in_95th_percentile;

שאילתה זו מציגה את 5% השאילתות האטיות ביותר, ללא קשר כמה זמן אבסולוטית הן ארכו (סף שניות כזה או אחר).

אתם שמים לב, בוודאי, שאנו קוראים ל system_schema  (השימוש ב FROM sys) ולא ל ps (השימוש ב FROM performance_schema).
ה ps חושפת כמות אדירה של נתונים (+ כמות גדולה של קונפיגורציה מה לאסוף ובאיזו רזולוציה) – מה שמקשה על השימוש בה עבור רוב המשתמשים.
ה system schema משמשת כשכבת הפשטה המציגה רשימה של views המרכזים מידע מתוך ה ps (וכמה מקומות אחרים) – ומנגישים את המידע המורכב למשתמש הפשוט.

נ.ב. –  על גרסה שהותקנה כ 5.7 ה system schema מגיעה כברירת-מחדל. על גרסאות ישנות יותר – ייתכן ותצטרכו להתקין אותה.

הנה תוצאה לדוגמה:

ספציפית, עניין שמציק לי הוא שבראש הטבלה נמצאת שאילתה, שאמנם לקחה כ 4 דקות לרוץ – אך התרחשה רק פעם אחת. בכלל: בראש הטבלה מככבות הרבה שאילתות שאינן מעניינות כי הן רצו פעמים בודדות בלבד. שירוצו.

יותר מעניין אותי למצוא שאילתה שגרמה להכנסה של כמיליון רשומות לאחת הטבלאות, לאורך 5 שעות שרת, גם אם כל מופע של השאילתה אורך 15 מילישניות בלבד. שאילתה כזו – לעולם לא תכנס ל slow log.

נ.ב: לא לדאוג לגבי “5 שעות שרת”, גם בהינתן שזה מידע של כשבוע: השרת מריץ הרבה שאילתות במקביל. הנתונים הנ״ל נלקחו משרת שפועל בעצימות נמוכה יחסית.

את המידע הזה אני שולף בעזרת השאילתה הבאה:

SELECT `schema_name` AS db,
digest_text AS query,
IF (( ( `stmts`.`sum_no_good_index_used` > 0 )
OR ( `stmts`.`sum_no_index_used` > 0 ) ), ‘*’, ”) AS `full_scan`,
Format(count_star, 0) AS events_count,
sys.Format_time(sum_timer_wait) AS total_latency,
sys.Format_time(avg_timer_wait) AS avg_latency,
sys.Format_time(max_timer_wait) AS max_latency,
Format(sum_rows_examined, 0) AS rows_scanned_sum,
Format(Round(Ifnull(( `stmts`.`sum_rows_examined` /
Nullif(`stmts`.`count_star`, 0) ), 0), 0), 0) AS `rows_scanned_avg`,
Format(Round(Ifnull(( `stmts`.`sum_rows_sent` /
Nullif(`stmts`.`count_star`, 0) ), 0), 0), 0) AS `rows_sent_avg`,
Format(sum_no_index_used, 0) AS rows_no_index_sum,
last_seen,
digest
FROM   performance_schema.events_statements_summary_by_digest AS `stmts`
WHERE  last_seen > ( Curdate() – INTERVAL 15 day )
ORDER  BY sum_timer_wait DESC;
השאילתה הזו ממיינת את התוצאה ע”פ sum_timer_wait, כלומר – זמן ההמתנה הכולל לכל המופעים של אותה השאילתה.
השאילתה הנ״ל תשמיט מהתוצאה שאילתות שלא נראו מופעים שלהן ב 15 הימים האחרונים. מה שנפתר / השתנה – כבר לא מעניין.

ניתוח ריצה של שאילתות

שלב הבא אחרי איתור שאילתה בעייתית הוא הרצה שלה, עם Explain וכלים נוספים.

כלי שימושי במיוחד של ה ps הוא הפרוצדורה trace_statement_digest. אנו מעתיקים את תוצאת ה digest (זיהוי ייחודי לדפוס של query) מתוך העמודה האחרונה של השאילתה הנ״ל, ומעתיקים אותה לתוך הביטוי הבא:

CALL sys.ps_trace_statement_digest(‘1149…405b’, 60, 0.1, TRUE, TRUE);

הפרוצדורה הזו תדגום עכשיו את בסיס הנתונים לאורך 60 שניות, כל עשירית שניה, ותאסוף נתוני-ריצה על השאילתה המדוברת. זהו כנראה הכלי הפשוט והמקיף ביותר לאסוף מידע על הרצה בפועל של שאילתה.

שימו לב שאתם זקוקים להרשאות אדמין בכדי להריץ את הפונקציה.

אם אין לכם הרשאות אדמין, הנה כלים נוספים שניתן להפעיל:

ראשית יש את ה optimizer trace שניתן להפעיל על שאילתה בודדת.

— Turn tracing on (it’s off by default):
SET optimizer_trace=“enabled=on”;

SELECT ; — your query here
SELECT * FROM information_schema.optimizer_trace;

ה Optimizer trace מספק לנו פירוט דיי מדוקדק של אלו החלטות לקח ה optimizer בעת הרצת השאילתה. התיאור הוא של מה שקרה בפועל (ולא הערכה) אם כי גם ביצוע בפועל מבוסס על הערכות (למשל: סטטיסטיקות של טבלאות) שעשויות להיות מוטעות.

ה trace ממש מציג את שלבי ההחלטה השונים של ה optimizer, איזה מידע ואלו אופציות עמדו לפניו, במה הוא בחר – ולמה. הנה דוגמה לחתיכה מתוך ה trace:

אם זו איננה מכונת פיתוח, חשוב לזכור ולסגור את ה optimizer_trace, שיש לו תקורה לא מבוטלת:

SET optimizer_trace=”enabled=off”;

ה trace מספק את תהליך ההחלטות של הרצת השאילתה, אך לא זמנים בפועל. בכדי לקבל זמנים אנו משתמשים ב profile:

SET profiling = 1;             — turn on profiling
SELECT ;                    — your query here
SHOW profiles;                 — list profiled queries
SHOW profile cpu FOR query 12; — place the proper query_id

בדרך ל profile הנכסף עליכם להפעיל את איסוף ה profiling, לבצע את השאילתה, לראות את השאילתות שנאספו – ולבחור את המספר השאילתה שלכם. התוצאה נראית כך:

“מה?? כמעט 5 שניות על שליחת נתונים בחזרה ללקוח? – אני מתקשר להוט!”

כמובן שכדאי גם לבדוק מה המשמעות של כל שורה. sending data כולל את קריאת הנתונים מהדיסק, העיבוד שלהם, והשליחה חזרה ל client.

בד”כ ה Sending Data יהיה החלק הארי, ודווקא כאשר סעיף אחר תופס נפח משמעותי – יש משהו חדש ללמוד מזה.

נ.ב – ניתן לבצע profiling גם על בסיס ה ps – מה שאמור להיות מדויק יותר, ו customizable – אבל מעולם לא השתמשתי ביכול הזו.

לבסוף, אל תשכחו לסגור את איסוף ה profiling:

SET profiling = 0;

עוד שאילתות שימושיות מתוך ה System Schema

הזכרנו את ה system schema, העוטפת את ה performance schema (ועוד קצת) בצורה נוחה יותר.

שווה להזכיר כמה שאילתות שימושיות:

SELECT * FROM sys.version;

הצגת גרסה מדויקת של שרת בסיס הנתונים, מתוך ממשק ה SQL.

SELECT * FROM sys.schema_tables_with_full_table_scans;

הצגה של רשימת הטבלאות שסרקו בהן את כל הרשמות כחלק משאילתה.

SELECT * FROM sys.schema_table_statistics;
SELECT * FROM sys.schema_index_statistics;

פרטים על הטבלאות והאינדקסים השונים – כמה ״עובד״ כל אחד.

SELECT * FROM sys.schema_unused_indexes;
SELECT * FROM sys.schema_redundant_indexes;

דרך טובה למצוא אינדקסים לא נחוצים.
אינדקסים מיותרים גורמים להכנסות (insert/update) להיות אטיות יותר – כי צריך לעדכן גם את האינדקס.

SELECT * FROM sys.statements_with_errors_or_warnings;
SELECT * FROM sys.statements_with_temp_tables;

עוד נתונים שכדאי להסתכל עליהם מדי-פעם.

SELECT * FROM sys.io_global_by_wait_by_latency;
SELECT * FROM sys.io_global_by_file_by_latency;

אחרון חביב, שאילתות אלו יעזרו לכם לאתר עבודה בלתי צפויה שבסיס הנתונים עושה.
מישהו פתח איזה לוג כבד, ושכח לסגור אותו?

בסיס נתונים מונוליטי

אתם בוודאי מכירים את המצב בו שרת בסיס נתונים אחד מריץ loads שונים ושונים של עבודה.
בד”כ זה יהיה בסיס נתונים של מערכת מונוליטית, שמשמש גם לעבודות BI כאלו או אחרות (הוא מונוליטי, וכל הנתונים נמצאים בו – למה להשתמש במערכת אחרת?)

בבסיס נתונים מונוליטי יש מגוון רחב של workloads שרצים במקביל. כאשר בסיס הנתונים מתחיל להתנהג בצורה לא יפה (הרבה CPU, וגמגומים) – אנחנו מנסים לאתר מי מכל המשתמשים הרבים והמגוונים של בסיס הנתונים יוצר את העומס החריג. הרבה פעמים נוכל להרוג תהליך אחד (בשאיפה: לא mission critical) – ולייצב את שרת בסיס הנתונים.

כלי העבודה הבסיסי הוא השאילתה הזו:

SELECT * FROM sys.session ORDER BY command;

היא דרך טובה לדעת מי עושה מה. זוהי “גרסה משופרת” של השאילתה SHOW PROCESSLIST.
כעת נוכל לראות אלו משתמשים גורמים לעומס הרב ביותר / עומס חריג – ונתחיל לנטר אותם:

SELECT * FROM sys.user_summary;

היא שאילתה שתספק בזריזות כמה ואיזה סוג יוצר כל משתמש לבסיס הנתונים (שורה לכל משתמש). אפשר להשוות את הנתון הזה ל session data בכדי לזהות מי מתנהג בצורה חריגה כרגע.
באופן דומה, השאילתות הבאות יספקו לנו קצת יותר drill down:

SELECT * FROM sys.user_summary_by_statement_type;
SELECT * FROM sys.user_summary_by_statement_latency;
SELECT * FROM performance_schema.user_variables_by_thread;
מכאן ייתכן ונרצה לפרק את העבודה לרמת ה thread של בסיס הנתונים. אולי שרת ספציפי של האפליקציה גורם לעומס החריג?

SELECT * FROM sys.io_by_thread_by_latency;

לא מוצאים את השאילתה?

מדי פעם קורא שאני מוצא שאילתות בעייתיות בעזרת ה ps, אבל מכיוון שיש ? במקום פרמטרים – קשה לי להשלים את הערכים כך שהשאילתה תחזיק תוצאות.
וריאציה אחרת: הקוד שלי שולח שאילתות ואני רוצה לראות מה בדיוק הוא שולח.

יש כנראה דרכים מתוחכמות יותר לעשות זאת, אך אני פשוט משתמש ב  general log:

SET global general_log = ‘ON’;
SELECT ; — your query here

SELECT *
FROM mysql.general_log
WHERE argument LIKE ‘/*%’
AND event_time >= ( Curdate()  INTERVAL 5 minute )
ORDER BY event_time DESC
LIMIT 50;
SET global general_log = ‘OFF’;

ה General Log יותר overhead כבד למדי על בסיס הנתונים, ולכן אם מדובר בסביבת production – אני מפעיל אותו לזמן קצר בלבד (בעזרת ‘general_log = ‘ON/OFF). על סביבת הפיתוח אני יכול לעבוד כשהוא דלוק כל הזמן.

נ.ב. גם כאן, ייתכן ותצטרכו לכוון את כתיבת הנתונים לטבלה בעזרת:

SET global log_output = ‘FILE,TABLE’;

מכיוון שה framework שאני עובד איתו, JDBI, מוסיף לשאילות מזהה המופיע כהערה בתחילת השאילתה “/* comment */” – הוספתי תנאי המסנן את הלוג לשאילות בהם מופיעה הערה (להלן …argument LIKE).

שיהיה בהצלחה!

"תסביר לי" – גרסת ה SQL

פוסטים בסדרה:
“תסביר לי” – גרסת ה SQL
לקחת את הביצועים ברצינות, בעזרת MySQL Performance Schema
נושאים מתקדמים ב MySQL: חלק א’ – עבודה עם JSON
נושאים מתקדמים ב MySQL: חלק ב’ – json_each  ו Generated Columns

יש מי שאמר:

“EXPLAIN היא הפקודה החשובה ביותר ב SQL, אחרי הפקודה SELECT”

בבסיס נתונים אנחנו רוצים:

  • לשמור נתונים (לאורך זמן, בצורה אמינה)
  • לשלוף נתונים / לבצע שאילתות.

ישנה אכזבה קלה כאשר מגלים שה DB הוא לא דבר תאורטי, אלא מערכת מעשית עם tradeoffs מוחשיים – ולפעמים השאילתות שלנו תתבצענה… לאט משציפינו.

זה לא קורה ביום הראשון, וגם לא על “מכנות הפיתוח”. אצלי על המחשב – כל השאילתות רצות מהר.
זה קורה אחרי חודשים שאנחנו עובדים עם בסיס הנתונים, בסביבת בדיקות בה יש הרבה נתונים – או יותר נפוץ: בפרודקשיין. פתאום אנחנו מגלים ששאילתה רצה בזמן לא סביר, מבחינתנו (“אבל על המחשב שלי – השאילתה רצה בפחות משניה!”).

אפשר להחליט שזהו. “בואי שרהל’ה, אנחנו אורזים ועוברים – ל Cassandra”.
מי אמר שעל קסנדרה יהיה טוב יותר?
כבר נתקלתי במקרים בהם גילוי שאילתה אטית לווה מיד בקריאות “ה DB איטי – בואו נעבור ל !” – עוד לפני שבוצע ניתוח בסיסי. זו כנראה הגרסה המודרנית לקריאה “!A Witch! – burn her“.

נתקלתי פעם בפרויקט שביצע מעבר ל Cassandra לאורך לשנה – ורק אז הסיק ש Cassandra היא no-go לצרכים שלו (הם היו זקוקים Graph Database… זו הייתה טעות בסיסית בזיהוי).

בפוסט הזה אנסה לעבור על כמה כלים חשובים לניתוח בעיות הביצועים של בסיס נתונים רלציוני (אתמקד ב MySQL 5.7 המאוד-נפוץ). עם כל הכבוד ל NoSQL – רוב המערכות עדיין עובדות (גם) עם בסיס-נתונים רלציוני.
אני לא טוען ש DB רלציוני הוא (בהכרח, תמיד) הדרך הנכונה לנהל נתונים – כתבתי כמה פוסטים שהראו שלפעמים זה אחרת, אבל לבסיס נתונים רלציוני יש כמה יתרונות חשובים, וחבל לוותר עליהם עם קושי ראשון.

 

דוגמה בסיסית

אני מריץ את השאילתה הבאה, ואינני מרוצה מזמני הביצוע שלה.

SELECT `value` 
FROM `quote_job_execution_internals` 
WHERE `quote_job_id` = ( 
  SELECT `quote_job_id` 
  FROM `quote_job_execution_internals` 
  WHERE `key` = ‘some key’ AND `value` = ‘1290268’ 
) AND `key` = ‘other key’ 
;

מה אני יכול לעשות?

שניה!
בואו נתחיל בבקשה מבסיס הנתונים להסביר כיצד הוא מתכנן להריץ את השאילתה (להלן ה query plan):

EXPLAIN SELECT `value` 
FROM `quote…

התוצאה תראה משהו כזה:

 

אמנם זהו Query אחד – אך תוצאת ה Explain מציגה שורה עבור כל Select (בדוגמה שלנו: שניים).

ה Quickwin בטיפול ב Explain נמצא בשתי עמודות: key ו type

    • key – באיזה אינדקס נעשה שימוש בכדי להגיע לנתונים. לרוב נרצה שיהיה שימוש באינדקס.
      • נשאל: האם האינדקס מתאים?
        • אולי חסר אינדקס?
        • אולי תנאי ה WHERE מכיל 2 עמודות – אך האינדקס מכסה רק אחת מהן? (ואז כדאי לשנות אינדקס / להוסיף עמודות לאינדקס).
      • לפעמים יש מקרים “לא צפויים” בהם האומפטימייזר בוחר לא להשתמש באינדקס. 
          למשל: התנאי WHERE ref_number = 1023 לא גרם לשימוש באינדקס כי העמודה ref_number היא מסוג varchar. השאילתה תחזיר תשובה נכונה – אבל האופטימייזר מבצע השוואת טיפוסים ומפספס שיש אינדקס מתאים. שינוי התנאי ל
        • ‘WHERE ref_number = ‘1023 – יחולל שינוי דרמטי בזמני הריצה.
      • דוגמה נוספת:  WHERE Lower(some_column) = ‘some_value…
        – לא ישתמש באינדקס על some_column (כי המידע לא מאונדקס ב lower_case) בעוד:(WHERE  some_column = Upper(‘some_value
        – דווקא כן. 
  • type – כיצד טענו את הנתונים מהטבלה? גם כשיש שימוש באינדקס, יש הבדל אם סורקים את כל האינדקס, רשומה אחר רשומה, או מצליחים להגיע לערכים הנכונים באינדקס מהר יותר.
    • בקירוב, אלו הערכים העיקריים שנראה, ממוינים מה”טוב” ל”רע”:
      • system / const – יש עמודה אחת רלוונטית לשליפה (למשל: טבלה עם רשומה בודדת).
      • eq_ref – יש תנאי ב WHERE המבטיח לנו איבר יחיד באינדקס שהוא רלוונטי – אנו ניגשים לאיבר יחיד באינדקס. למשל: כאשר העמודה היא UNIQUE + NOT NULL.
      • ref – אנו הולכים לסרוק איברים באינדקס כמספר הרשומות שנשלוף. הגישה לאינדקס היא “מדויקת”.
      • range – עלינו לסרוק טווח (או מספר טווחים) באינדקס – שכנראה יש בהם יותר איברים ממספר הרשומות שאנו עומדים לשלוף.
      • index – יש צורך לסרוק את כל האינדקס
      • ALL – יש צורך לסרוק את כל הרשומות בטבלה. 😱
    • ייתכנו מקרי-קצה בהם index ו ALL – יהיו עדיפים על range או ref. אפרט אותם בהמשך.
רק להזכיר: האינדקס הוא “טבלה” רזה יותר המכילה רק עמודה בודדת (או מספר עמודות בודדות) מהטבלה – וממוינת ע”פ הסדר הזה. לא נדיר שאינדקס הוא רזה פי 10 ויותר מהטבלה (במיוחד אם בטבלה יש שדות מטיפוס varchar) – ואז גם סריקה מלאה של האינדקס הוא טעינה של (נניח) 10MB מדיסק – מול 100MB או יותר של נתונים שיש לטעון עבור סריקה מלאה של הטבלה.

 

מצד שני, גישה לאינדקס היא רק הקדמה לגישה לטבלה.

למשל: 20% מהרשומות עלולה בקלות להתמפות ל 50% מהבלוקים בדיסק בהם מאוחסנת הטבלה (במיוחד כאשר הרשומות קטנות). רזולוציית השליפה היא בלוקים – ולא רשומות בודדות.

יתרה מכך: שליפת כל הבלוקים של הטבלה כקובץ רציף תהיה לרוב מהירה יותר משליפת חצי מהבלוקים – כקבצים “רנדומליים”.

אפשר לומר שהכדאיות של סריקת אינדקס, המניבה 20% או יותר מהרשומות בטבלה – מוטלת בספק.
כאשר אתם משתמשים באינדקס אתם רוצים שהוא יסנן לכם את מספר הרשומות לשליפה של מאית, אלפית, או פחות מסה”כ האיברים – לא עשירית או חמישית.

 

זהו. אם רציתם לקבל 50% מה value בקריאת 20% מהפוסט – אתם יכולים לעצור כאן, ולהמשיך בעיסוקכם.
 

 

תזכורת

מידע נוסף שניתן לשאוב מתוך פקודת ה Explain

בואו נחזור לרגע למבנה ה output של הפקודה, ונעבור עליו סעיף אחר סעיף.

 

קבוצה #1: “איך הנתונים נשלפים”

שלושת העמודות הראשונות עוזרות לנו לקשר בין השורה ב output לחלק המתאים בשאילתה.
בד”כ תוצאה של explain תהיה שורה או שתיים – אך גם נתקלתי גם ב 6 או 7 שורות בשאילתות מורכבות במיוחד.

  • Select Type הוא שדה זה נועד לאתר סוג ה SELECT שאליו מתייחסת השורה: SIMPLE – כאשר אין קינון שאילתות או איחוד שלהן, PRIMARY – השאילתה החיצונית ביותר (כאשר יש קינון) או הראשונה (ב UNION), יש גם SUBQUERY, UNION וכו’.
    • מעבר למה שציינתי / מה שאינטואטיבי – חבל להשקיע בהבנת העמודה הזו. בהמשך אראה לכם מה עושים אם יש שאילתה מורכבת במיוחד.
  • table – הטבלה שעליה המתבצע ה Select. זה יכול להיות שם של טבלה, או ביטוי כגון <Union <table1, table2.
  • partition ה partition של הטבלה, במידה ויש כזה.
    • partition היא היכולת להגדיר שחלקים שונים של הטבלה יאוחסנו על הדיסק בנפרד זה מזה (על מנת לשפר ביצועים בגישה לחלק המסוים). לדוגמה: כל הרשומות של שנת 2018 נשמרות בנפרד מהרשומות של שנת 2017 – למרות שלוגית זו טבלה אחת. התוצאה: שאילתות בתוך שנה בודדת יהיו מהירות יותר – על חשבון שאילתות הדורשות נתונים מכמה שנים.

העמודה החשובה יותר היא type – אותה כבר הזכרנו. הנה כמה פרטים נוספים ששווה להכיר:

סריקת index יכולה להיות יעילה יותר מ range או ref

שימוש באינדקס נעשה לרוב ב-2 שלבים:
  1. האינדקס נסרק / נקרא, וממנו נשלפו מזהיי (primary key) הרשומות שיש לקרוא מהטבלה.
  2. נעשית גישה נוספת לטבלה על מנת לטעון את ערכי הרשומות המתאימות.

אם בעמודה “Extra” (האחרונה) מופיע הערך “Using Index” משמע שלא היה צורך בשלב 2 – כי ערכי העמודות שביקשנו – נמצאו כבר באינדקס.

למשל: ביקשנו SELECT x FROM table1 WHERE y = 4
אם יש לנו אינדקס על עמודות x ו y – הרי שניתן ניתן לספק את התשובה מתוך קריאת האינדקס בלבד – וללא גישה לטבלה. זהו מצב מצוין.

 

סריקת ALL עשויה להיות יעילה יותר מ index, range או ref

בהמשך להסבר של שני שלבי השליפה:

  1. כאשר הטבלאות מאוד קטנות (KBs מעטים) – טעינת האינדקס ואז טעינת הטבלה – דורשת לפחות שתי גישות לדיסק.
  2. במקרים כאלו עדיף כבר לטעון את תוכן הטבלה (“ALL”) ולסרוק אותה. העלות העיקרית היא מספר הגישות לדיסק – ולא הסריקה בזיכרון.
קבוצה #2: “אינדקסים”
 
  • possible_key – היא רשימת האינדקסים שמכילים את העמודות בתנאי ה WHERE / JOIN.
    האופטימייזר בוחר אינדקס ספציפי (זה שהופיע בעמודה key) ע”פ יוריסטיקות מסוימות – והוא עשוי גם לפספס.
    • אם אתם משוכנעים שהוא לא בחר נכון (הוא טועה פחות ממה שנדמה לנו) אתם יכולים להנחות אותו באיזה אינדקס להשתמש בעזרת ההנחיה (USE INDEX (idx המגדילה את ציון האינדקס באלגוריתם הבחירה.
    • אתם יכולים להשתמש גם ב  FORCE INDEX – אבל התוצאה עשויה להכאיב: מגיעה שאילתה (או אלפי מופעמים של שאילתה) שאין טעם באינדקס – אך האופטימייזר ישתמש באינדקס, כי אתם אמרתם.
    • הנחיה אחרונה שימושית היא IGNORE INDEX – אתם יכולים לקרוא עוד בנושא כאן.
  • key_len – נשמעת כמו עמודה משעממת עד חרפה, אך זה בכלל לא המצב!
    • key_len עוזרת לאתר בזריזות אינדקסים “שמנים”. 
      • למשל: בתמונה למעלה יש key באורך 3003 בתים. כיצד זה ייתכן?
      • כל Character ב Unicode היא 3 בתים (בייצוג של MySQL) אז 3*1000 = 3000 בתים. עוד שלושה בתים, כך נראה לי, מגיעים מכך שהשדה הוא Nullable (בד”כ Nullable מוסיף 1+ לגודל).
        כלומר: גודל האינדקס הוא כ 90% מגודל הטבלה.
      • אם יש לי שאילתות הסורקות את כל האינדקס (type=index) – הרי שהאינדקס גורם לפי 2 נתונים להיטען מהדיסק, מאשר לו היינו פשוט סורקים את הטבלה.
      • כשאינדקס הוא כ”כ גדול (3000 בתים!), ובמידה ונעשה שימוש תדיר באינדקס – שווה לחשוב על הוספת עמודה נוספת, רזה יותר, המכילה subset של תוכן העמודה המקורית (למשל: 50 תווים ראשונים) – ואת העמודה הזו נאנדקס. הפתרון הספציפי מאוד תלוי בסוג הנתונים והגישה אליהם.
    • תובנה חשובה נוספת, שעמודת ה key_len יכולה לספק לי היא בכמה עמודות מתוך אינדקס-מרובה-עמודות נעשה שימוש. אם יש לי אינדקס של שני שדות מטיפוס INT (כלומר: ביחד 8 בתים), אך העמודה key_len מחזירה ערך 4 – סימן שנעשה שימוש רק בעמודה הראשונה שבאינדקס (שימוש באינדקס יכול להיעשות רק ע”פ סדר האינדקסים “שמאל לימין”).
      • לא קל להבחין במקרים הללו (צריך לחשב גדלים של שדות) – אך הם יכולים להעיד על בעיה בלתי-צפויה. למשל: תנאי שעוטף את ערך המספר במרכאות – וכך נמנע שימוש בשדה המשני של האינדקס. לכאורה: היה שימוש באינדקס – אבל שיפור שכזה עשוי לשפר את זמן הריצה פי כמה.
      • נ.ב. שדה Int תמיד יהיה 4 בתים. ה “length” משפיע על מספר הספרות בתצוגה.
      • אם אתם לא בטוחים מה מבנה הטבלה, תוכלו להציג אותו בזריזות בעזרת
          SHOW CREATE TABLE tbl_name 
    • ref – איזה סוג של ערכים מושווים כנגד האינדקס. לא כזה חשוב.
      • const הוא הערך הנפוץ, המתאר שההשוואה היא מול ערך “קבוע”, למשל: ערך מתוך טבלה אחרת.
      • NULL הכוונה שלא נעשתה השוואה. זה מתרחש כאשר יש Join ומדובר בטבלה המובילה את ה join.
      • func משמע שנעשה שימוש בפונקציה. 
 
אם קיבלתם ערך ref=func, ואתם רוצים לדעת איזה פונקציה הופעלה, הפעילו את הפקודה SHOW WARNINGS מיד לאחר ה Explain. היא תספק לכם מידע נוסף (מה שנקרא בעבר “extended explain“).
 
זוכרים שאמרתי לכם שאם יש שאילתה מורכבת מאוד, חבל לנסות ולשבור את הראש איזו שורה ב Explain מתאימה לאיזה חלק בשאילה? 
כאשר אתם קוראים ל Show Warnings אתם גם מקבלים את הגרסה ה “expanded” של השאילתה.
 
/* select#1 */ SELECT `quote_job_execution_internals`.`value` AS `value` 
FROM  `quote_job_execution_internals` 
WHERE  ( ( `quote_job_execution_internals`.`quote_job_id` 
           = ( /* select#2 */ SELECT            `quote_job_execution_internals`.`quote_job_id` 
           FROM   `quote_job_execution_internals` 
           WHERE  ( (            `quote_job_execution_internals`.`key` = ‘nimiGlPolicyId’ ) 
           AND (`quote_job_execution_internals`.`value` = ‘1290268’ ) )) ) 
           AND ( `quote_job_execution_internals`.`key` = ‘storageId’ ) ) 
 

הסימון בהערות (“select#1” ו “select#2“) הוא הדרך הקלה לזהות אלו חלקים בשאילתה מתייחסים לאלו שורות ב Explain. 

 
 
 
קבוצה #3: כמות הרשומות

זו עוד קבוצה חשובה של פרמטרים.
כידוע אנו רוצים לטעון מהדיסק – כמה שפחות נתונים.

  • עמודת ה rows מספקת הערכה כמה נתונים ייסרקו. השאילתה עדיין לא רצה – אז לא ניתן לדעת.
    • במקרה שלנו, כאשר ה subquery עתיד לסרוק 2 שורות, וה primary select עתיד לסרוק 6 שורות – אנו מצפים לסריקה של 6 * 2 = 12 שורות, וזה מספר קטן. 
    • אם יש לנו 3 שאילתות מקוננות, וכל אחת סורקת 50 שורות – אזי נגיע סה”כ לסריקה של עד 125,000 שורות – וזה כבר הרבה!
  • עמודת ה filtered מספקת הערכה (לא מדויקת) באיזה אחוז מהרשומות שנסרקו באינדקס – ייעשה שימוש. 
    • בדוגמה למעלה קיבלנו הערכה שנטען 6 או 2 רשומות, וב “10%” מהן יעשה שימוש, כלומר: באחת.
    • כאשר הערך הוא נמוך מאוד (פחות מאחוזים בודדים, או שבריר האחוז), ובמיוחד כאשר מספר הרשומות הנסרק (rows) הוא גבוה – זהו סימן שהשימוש באינדקס הוא לא יעיל. ייתכן ואינדקס אחר יכול להתאים יותר?
    • השאיפה שלנו היא להגיע ל rows = 1 ו filtered = 100% – זהו אינדקס המנוצל בצורה מיטבית!
 
לצורך האופטימייזר, בסיס הנתונים שומר לעצמו נתונים סטטיסטיים על התפלגות הנתונים בטבלאות השונות. ב MySQL המידע נשמר ב Information_schema.statistics – והוא מתעדכן עם הזמן. 
 
אם הרגע הוספתם אינדקס, ו/או הכנסתם / עדכנתם חלקים גדולים מהטבלה – יש סיכוי טוב שייקח זמן עד שיאספו נתונים חדשים, שיעזרו לאופטימייזר להשתמש נכון באינדקסים במצב החדש.
 
הפקודה ANALYZE TABLE your_table גורמת ל MySQL לאוסף את הנתונים מחדש. זה עשוי לארוך קצת זמן.

 

הפקודה OPTIMIZE TABLE your_table גורמת ל MySQL לבצע דחיסה (סוג של defrag) על הקבצים של הטבלה – ואז להריץ Analyze Table. זה ייקח יותר זמן – ולכן מומלץ להריץ את הפקודה הזו רק “בשעות המתות”.

 

 

 

קבוצה #4: Extra


עמודת האקסטרא בד”כ תאמר לכם “Using Where” (דאא?), אבל יש כמה ערכים שהיא יכולה לקבל שדווקא מעניינים:

  • Using Index – ציינו את המקרה הזה למעלה. נעשה שימוש באינדקס בלבד על מנת לספק את הערכים (מצוין!)
  • Using filesort – מכיוון שלא הצליח לבצע מיון (ORDER BY) על בסיס אינדקס ו/או כמות הנתונים גדולה מדי – בסיס הנתונים משתמש באלגוריתם בשם filesort בכדי לבצע את המיון. בסיס הנתונים ינסה להקצות שטח גדול בזיכרון ולבצע אותו שם – לפני שהוא באמת משתמש בקבצים. 
    • פעמים רבות אפשר להימנע מ filesort ע״י אינדקס על העמודה לפיה עושים מיון. 
    • [עודכן] כמובן שבאינדקס הכולל כמה שדות – רק השדה הראשון (ה״שמאלי״) שימושי למיון, או לחלופין אם סיננו ע”פ האינדקס (WHERE) – השדה השמאלי לשדה/שדות על פיהם נעשה הסינון (תודה לאנונימי על ההערה).
    • אם יש ORDER BY על יותר מעמודה אחת – שימוש באינדקס יהיה יעיל רק אם האינדקס מכיל את השדות לפיהם ממיינים בסדר הנכון.
  • Using temporary – מקרה דומה: יש פעולה גדולה (בד״כ GROUP BY) שלא ניתן לבצע בזיכרון – ולכן משתמשים בטבלה זמנית (בזיכרון). גם זה מצב שכדאי לנסות להימנע ממנו.
    • בד”כ אין פתרונות קלים למצב הזה: לנסות ולהסתדר ללא GROUP BY? לבצע את פעולת הקיבוץ אפליקטיבית בקוד? (כמעט תמיד custom code שנכתב בחכמה ינצח את בסיס הנתונים בביצועים – אבל יהיה יקר יותר לפיתוח ותחזוקה)

 

 

אחרית דבר

לאחר הפוסט הזה אתם אמורים להבין את תוצאות הפקודה EXPLAIN בצורה דיי טובה!

חשוב להזכיר, ש Explain מבצע הערכה לגבי אופן ביצוע עתידי – ולא מידע על הביצוע בפועל.
ניתוח ההערכה הוא הכלי השימושי ביותר לשיפור ביצועי שאילתות – אך לא היחיד.

שווה להזכיר את ה Slow Log וה Performance Schema שעוזרים לאתר בכלל את השאילתות הבעייתיות.
את Profile ו Trace (+ מידע שניתן לשלוף מה Performance Schema) – שבעזרתם ניתן לבחון כיצד השאילתה רצה בפועל.

הפרמטר  format=json (כלומר EXPLAIN format=json SELECT something) גורם ל Explain לספק פירוט עשיר יותר (ובפורמט json).

יש גם עוד הנחיות שונות שניתן להוסיף בשאילתה על מנת להנחות את האופטימייזר לפעול באופן מסוים (למשל SQL_BIG_RESULT או STRAIGHT_JOIN), או אפילו הנחיות הנובעות מהחומרה שבשימוש (להלן טבלת ה mysql.server_cost , וה optimizer_switch)
אבל אני חושב שאת תחום זה מוטב להשאיר ל DBAs…

שיהיה בהצלחה!

 

להפיק קצת יותר מ ELK ו Kibana

ELK Stack (ראשי תיבות של Elastic Search, Logstach, and Kibana) הוא אוסף של שלושה מוצרי קוד-פתוח:

  • ElasticSearch – בסיס נתונים NoSQL המבוסס על מנוע החיפוש Lucene (נקרא: “לוסין”)
  • LogStash – כלי לאיסוף לוגים (“log pipeline”),פענוח (parsing), סינון, טיוב, ושליחה שלהם הלאה (ל ElasticSearch, למשל).
  • Kibana – שכבת UI/Visualization ל ElasticSearch.
השלישייה הפכה לסט מאוד מקובל לניהול (ריכוז ופענוח) לוגים, בעיקר בחברות קטנות – תוך כדי שהיא דוחקת הצידה כלים שהיו מקובלים מאוד עד כה, כמו Splunk, או לפניו: SumoLogic. נתקלתי בנתון שטוען שכיום יש יותר הורדות כל חודש של ELK – מאשר התקנות חיות של Splunk.
כמו שייתכן ושמתם לב (או אולי קראתם את המאמר המפורסם של מרטינז) פרויקטי קוד פתוח נוטים “להפסיד” למוצרי SaaS. הערך של מוצר מנוהל הוא ברור, וגם סטאראט-אפים קצרי תקציב – מוכנים לשלם כסף עבור מוצר מנוהל.
בתחום ה “ELK המנוהל” יש מספר אלטרנטיבות כמו Logit, LogSense, או Scalyr – אך בישראל לפחות, ללא ספק המוצר המקובל ביותר הוא Logz.io – מוצר ישראלי, שגם סיים סבב גיוס לאחרונה.
שווה לציין של-ElasticSearch ישנם גם שימושים מעבר לאיסוף לוגים – אך זה איננו נושא הפוסט.

יצא לי להיתקל באנשים רבים שמשתמשים ב ELK – בצורה מאוד בסיסית ונאיבית, ומטרת הפוסט הזה היא לעזור להשתמש ב Kibana (הכלי איתו תהיה לנו הכי הרבה אינטראקציה מהשלושה) – בצורה מעט יותר טובה.

להעלות מ “רמה 3” – ל “רמה 6”: משימוש מוגבל בכלי – ליכולת להפיק קצת תובנות משמעותיות.

“זה לא קשה בכלל, זה דווקא קל!”

בסיס

אני מניח שאתם מכירים את הבסיס.
אני מניח שאתם יודעים מה הכפתורים הבסיסיים על כל פרמטר עושים:

(1 – הוספת פילטר, פוזיטיבי או נגטיבי, הכולל את השדה, 2 – הוספת השדה לתצורה, 3 – הוספת פילטר המחייב את קיום השדה)

אני מניח שאתם מכירים את התחביר הסטנדרטי של לוסין לכתיבת שאילתות:

  1. אני מניח שאתם יודעים לחפש ע”פ ערך של שדה מסוים
  2. אני מניח שאתם יודעים להוסיף תנאים לוגים, וגם זוכרים שאין case sensitivity בערכים.
  3. אני מניח שאתם זוכרים ששמות השדות הם דווקא case sensitive (אחרת לא היה ניתן לזהות אותם חד-חד ערכית).
  4. … שאתם זוכרים שאפשר להשתמש ב wildcard (למשל הביטוי הנ”ל יתאים ל Khrom וגם ל Chromium)
  5. … אולי אתם יודעים שאפשר להשתמש ב wildcards גם בשמות של שדות (אם כי צריך escaping)
  6. .. ואני מניח שאתם זוכרים שכל הסימנים + – = && || ! ( ) { } [ ] ^ ” ~ * ? : \\ / – דורשים escaping. ובעצם הביטוי שאני מחפש הוא: 2=(1+1)
    1. שווה גם לציין שאין דרך לבצע escaping ל < ו >. באסה.
  7. אולי אתם מכירים גם Proximity Search, ששימושי כאשר יש חשש לשגיאת כתיב או הבדלים בין אנגלית אמריקאית לבריטית/אוסטרלית. המספר 5 מציין את “המרחק”, וכמה שיהיה גבוה יותר – יותר ערכים דומים יכללו בתוצאת השאילתא.
  8. ובטח אני מניח שאתם זוכרים שאפשר לסנן ע”פ טווחים, כי בערכים מספריים (או תאריכים) – זה מאוד שימושי!

אני מניח שאתם יודעים להשתמש ב “surrounding documents” על מנת לפלטר את ההודעות שקרו לפני/אחרי ההודעה הנבחרת:

או יותר טוב – שהוספתם trackingId אפליקטיבי – שפשוט יהיה יותר מדויק.

אני מניח שאתם יודעים שאפשר לשמור שאילתות, ולהשתמש בהן מאוחר יותר – וגם עושים זאת, לא מעט.

אני גם מתאר לעצמי שהלכתם ל Management / Index Patterns, מצאתם את שדה הזמן (timestamp?) ושיניתם את הפורמט למבנה קצר יותר.

אין סיבה שיופיע לכם התאריך כ “December 2nd 2017, 21:41:51.155” בכל שורה – מבנה התופס שטח מסך יקר, בזמן שאתם יכולים לעבוד עם מבנה כמו “21:41:51.155 ,12/02/17” – שגם קל יותר לקריאה.

בעצם, אני רואה שיש לכם קצת בסיס!

מה שנותר לי הוא לדבר קצת על aggregation ו visualizations – שזו תחום שאפשר להפיק בו הרבה ערך, במעט מאמץ.

Visualizations & Aggregations

בדרך ליצירת ויזואליזציה יש שלושה שלבים עיקריים:

  • Sampling – בחירת הערכים עליהם נרצה לבצע את הוויזואליזציה. יש לנו הרבה רשומות מאוחסנות ב ElasticSearch – וחשוב שנסנן את אלו הרלוונטיות לוויזואליזציה.
  • Clustering – אני רוצה לקבץ את הרשומות הרלוונטיות ל”אשכולים” החשובים לתובנה העסקית. למשל: ע”פ microservice, סוג משתמש, דפדפן שבשימוש, וכו’.
  • Reduction – גם לאחר כל הסינונים הללו, ייתכנו בכל אשכול אלפים (או מאות אלפים?) של איברים – שזה יותר מדי לצרוך. נרצה להשתמש בפונקציות כגון count או average – על מנת לתמצת את המידע.
ב Kibana:
  • Sampling נקרא Query
  • Clustering נקרא Buckets 
  • ו Reduction נקרא Metrics.

נו, בסדר.

הנה דוגמה להגדרה של ויזואליזציה פשוטה, המראה את ההתרעות (warnings) הנפוצות ביותר בפרודקשיין:

  1. אני רוצה לדגום רק הודעות מסוג Level=Warning וב Production (שהן פחות מ 0.1% מכלל ההודעות במערכת).
    1. אני משתמש בשאילתא שמורה – שזה נוח וחוסך טעויות / maintenance.
    2. אני יכול להוסיף, לצורך הוויזואליזציה, פילטורים נוספים על שאילתת הבסיס. שימושי.
  2. אני מאגד (clustering) את הנתונים ע”פ מחלקה (class בקוד ממנו יצאה ההתרעה) וע”פ שם השירות ממנו יצאו ההודעות.
    1. לסדר בו אני מגדיר את האשכולים – יש משמעות, אותה אסביר בהמשך.
  3. בכל דלי (“Bucket”) יש לי עכשיו עשרות או אפילו מאות הודעות, וייתכן שמסוגים שונים. על מנת להתמודד עם כמות המידע – אני סוכם את ההודעות, להלן count” aggregation” (פעולת reduction).
הערה: מבחינת UI, קיבנה שמה את ה Metrics (כלומר: ה reduction) לפני ה buckets (כלומר: clustering).
יש בזה משהו הגיוני, לכאורה – אך אני מוצא שזה הרבה יותר נוח להתחיל מ count (כלומר: reduction ברירת המחדל), לראות שיש בכלל תוצאות ושהגדרתי את ה buckets נכון – ורק בסוף לגשת להגדיר את ה reduction אם הוא מורכב יותר (למשל: ממוצע הזמנים ע”פ שדה אחר xyz). לכן סימנתי את המספרים 1,2,3 בסדר שאינו תואם לסדר ב UI.

והנה התוצאה:

  1. הנה האשכול הראשי: שם המחלקה שממנה נזרקה ההתרעה.
    1. כאן אני רואה את מספר האיברים בכל אשכול ראשי.
      בקיבנה הגדרות שונות של סכימה – מתארות גם את ה UI שיווצר. מצד אחד – זה מקצר תהליכים, מצד שני – מקשה עלי להשיג בדיוק את ה UI שאני רוצה.
  2. האשכול השני שהגדרתי הוא ע”פ ה micro-service ממנו נזרקה ההתרעה.
    1. כאן אני רואה את מספר האיברים בכל אשכול שני.
      האשכול השני מתאר חיתוך שני. כלומר: ה count שאני רואה הוא מספר האיברים שגם שייכים למחלקה שהופיעה בעמודה #1, וגם לשירות בעמודה #2 – ולכן, זהו המספר שמעניין אותי.
  3. בחוכמה, הזמן הוא ציר שאיננו מקובע בשאילתא (או sampling), ואני יכול לשחק עם הערכים מבלי לבצע שינויים בשאילתא. כלומר: אני יכול בקליק לראות את הערכים עבור שעה, יום, שבוע, וכו’. מאוד שימושי.

הנה תרשים, שמנסה לעזור ולהמחיש את מה שקורה (זה חשוב):

  • לקחנו המון נתונים – ובחרנו רק חלק מהם (מתוך כל המרחב, או חלקו). זה שלב ה Sampling.
  • ה cluster הראשון כולל חיתוך ראשוני.
    • ויזואלית – לא נראה שה clusters לא מכסים את כל מרחב ה sampling. אפשר שכן ואפשר שלא – לצורך פשטות התרשים – ציירתי אותו כך.
      בדוגמה למעלה – ה cluster הראשון מכסה את כל המרחב, כי לכל הודעה יש ערך לשדה ה class.
  • ה cluster השני מבצע חיתוך נוסף על החיתוך הראשון.
    • ה aggregation שלו (במקרה שלנו: count), מייצג את האיברים באזור החיתוך (הסגול).
  • אפשר להוסיף גם cluster שלישי ורביעי. לוגית – זה אמור להיות מובן, אבל פשוט קשה לצייר את זה 😏.
סוגי הויזואליזציות העיקריים בקיבנה 5.5. אלו המסומנים בורוד הם אלו שאני מוצא כשימושיים ביותר.
Timelion ו Visual Builder הם יותר מורכבים – כמעט נושא בפני עצמו.

עוד כמה פרטים חשובים על Aggregations

המידע בפוסט עד כה אמור לסייע להבין כיצד לבנות ויזואליזציות בקיבנה, אבל כמה פרטים עדיין חסרים על מנת שתוכלו להסתדר בקלות.

בואו נחזור מעט לפרטים של הויזואליזציה (הפשוטה) מהדוגמה למעלה: רשימת ההתרעות הנפוצות. ואיך הגדרנו את האשכולות (או “דליים”).

  1. ישנן כמה שיטות לקבץ נתונים לאשכולות, חלקן שימושיות יותר – חלקן פחות.
  2. השיטה השימושית ביותר, לדעתי, היא ע”פ Term.

קצת היסטוריה:
עד גרסה 5 של Elastic Stack, הטיפוס הנפוץ לשדות היה String – מחרוזת.
על שדה מסוג מחרוזת היה ניתן להגדיר תכונה בשם analyzed.

מחרוזת שהיא analyzed הייתה מפורקת למונחים (terms) בודדים, למשל “elastic stack” היה מפורק ל “elastic” ול “stack” ולכל term היה נרשמת רשומה באינדקס הפוך האומר לכל מילה – באילו מחרוזות היא נמצאה. זה חלק ממנגנון שנקרא “Full Text Search”.

מחרוזת שהייתה not_analyzed – היו מתייחסים אליה כ term בודד, והיה נבנה אינדקס הפוך שמצביע באלו רשומות מופיע הביטוי השלם “elastic stack”.

התכונות analyzed / not analyzed לא חלחלו יפה לכלים השונים במערכת, וגרמו לבלבול אצל המשתמשים ולכן החליטו להפסיק לעבוד ומחרוזת, ולעבוד עם 2 טיפוסי-בסיס חדשים: term ו text.

term הוא בעצם non_analyzed string ו text הוא בעצם analyzed string. לפעמים אתם תמצאו שדה המופיע בשתי הצורות. למשל: message מסוג text, ו message.term או message.raw מסוג term.

clustering ע”פ term אומר שאנו יוצרים אשכול / דלי לכל ערך אפשרי של השדה.

למשל עבור השדה class ייווצרו כ 220 אשכולות, כי יש לי במערכת לוגים מ 221 מחלקות שונות של המערכת.
220 הוא מספר גדול מידי להציג בוויזואליזציה ולכן עלי לציין כמה אשכולות אני רוצה לאסוף (3). בחרתי להתייחס בוויזואליזציה ל 25 האשכולות שמכילים הכי הרבה פריטים (descending) – כלומר רק 25 המחלקות ששלחו הכי הרבה התרעות – יכללו בוויזואליזציה.
שימו לב שב Tab ה Options יש הגדרות שעשויות להגביל את מספר הערכים המוצגים בוויזואליזציה.

עוד מושג שרק אזכיר הוא Significant Term המתייחס ל term שהנוכחות שלו בתוך ה sampling היא בולטת. למשל: ה sampling שלי מצמצם את הנתונים רק להודעות מסוג התרעה (Warning). המחלקה com.seriouscompany.Server לא שלחה הרבה הודעות לוג (רק 0.009% מכלל ההודעות), אבל בהודעות מסוג התרעה – חלקה הוא 0.4%. יש מחלקות שאחראיות גם ל1% ו 2% מכלל ההתרעות – אבל הן גם אחראיות 0.5% ו 0.7% מכלל הודעות הלוג.

יחסית לכמות ההודעות שהיא שולחת באופן כללי, המחלקה com.seriouscompany.Server שלחה אחוז גבוה ביותר של הודעות מסוג התרעה – ולכן כ significant term היא תחשב כבעלת הערך הגבוה ביותר.

significant term הוא כלי שימושי להבלטת אנומליות בהתנהגות המערכת.

אני מקווה שזה ברור.

בקצרה, ולקראת סיום, כמה מילים על סוגי ה clustering השונים בקיבנה:

  • Histogram פועלת על שדה מסוג Number ותקבץ לדליים intervals קבועים שהוגדרו. אם שדה “זמן הריצה” מכיל ערכים בין 0 ל 6553 מילי-שניות, ונקבע histogram עם internal של 100 – ייווצרו 66 clusters שיכילו טווחים כמו 0-100, 101-200, וכו’.
  • Date Histogram – היא היסטוגרמה על זמנים. היא יכולה להיות ע”פ דקות / שעות / ימים או מצב auto – בו קיבנה בוחרת interval שייצור כמה עשרות בודדות של clusters.
  • Range – טווחים קבועים שמוגדרים על ידכם. למשל 0-100, 500-101, ו 1000-10000 (תוך התעלמות מכוונות מכל הערכים בין 501 ל 999).
  • Date Range – רשימה קבועה של טווחי זמנים שהוגדרה על ידכם. לדוגמה: 9 עד 11 בבוקר, ו 16 עד 18 אחה”צ.
  • Filter – רשימה קבועה של תנאים לוגים על שדה, שכל תנאי מייצג cluster. למשל: *error, warning*, או nullPointerException.
  • IPv4 ו GeoHash הם clustering מאוד ספציפיים על השדות שבהם הם עובדים. GeoHash למשל יכול לקבץ ע”פ מיקומים גיאורגפיים (למשל: מהן מגיעות הבקשות). אפשר לייצר איתו ויזואליזציות מאוד מגניבות – ולא תמיד כ”כ שימושיות.

הדרך הכי טובה להבין את הויזואליזציות השונות, וסוגי ה clustering – היא לשחק ולהתנסות.
אני ממליץ לכם להחזיק חלון פתוח נוסף בו תוכלו לחפש את הנתונים לפיהם אתם עושים aggregations ולראות אילו סוגי ערכים הם מקבלים. לא פעם – אנו מעריכים בצורה לא נכונה את מרחב / התפלגות הערכים, ומסיקים מחוסר הבנה זו – שבוויזואליזציה שיצרנו יש בעיה.

אזהרה חשובה: ב metrics עומדים לרשותכם aggregations ע”פ אחוזונים: האחוזון ה 95 של ערך מסוים, ה 96, ואפילו ה 96.5.
זה נשמע מגניב, אפילו New Relic לא מאפשר לחתוך נתונים (למשל: זמני ריצה של פונקציה) ע”פ כל אחוזון שרירותי.

אליה וקוץ בה: LogStash לא באמת אוסף את האחוזונים השונים. הוא אוסף כמה נתונים (min, max, median, ועוד מספר סופי של “ריכוזי נקודות” על הטווח) – ויוצר קירוב של התפלגות. יהיה האלגוריתם שבשימוש TDigest או HDR Histogram – ההתפלגות היא רק קירוב להתנהגות הנתונים האמיתית, ולא משהו שבאמת אפשר להסתמך עליו לניתוח ביצועים אמיתי.

ריבוי אוכלוסיות (למשל: המשתמשים שמשתמשים בתכונה x מתפלגים נורמאלית A, ואלו שלא משתמשים בתכונה x – המתפלגים נורמאלית A’) – עשוי בקלות להתפוגג תחת הקירובים, ו”להעלים מהראדר” התנהגויות חשובות.

אם אתם רוצים למדוד ביצועים – השתמשו ב New Relic או ב TimeSeries DB (גם ELK+Kibana מנסים להיכנס לתחום, להלן Timelion visualizations), לא ב percentiles של ELK.

מקור נוסף: Calculating Percentiles.

סיכום

אני יודע שיש משתמשים רבים ל ELK בישראל, וחלק גדול מהם – בעלי הכרות בסיסית בלבד עם Kibana והמנוע מאחוריה: Elastic Search. אני מקווה שהפוסט הזה יהיה שימושי ואכן יצליח לעזור לאנשים להפיק קצת יותר מהנתונים שלהם, הנמצאים ממש מרחק כמה קליקים מניתוח סביר.

כמובן שרב עדיין הנסתר על הגלוי.

בתחתית הפוסט הוספתי כמה קישורים שאני חושב שהם רלוונטיים.

שיהיה בהצלחה!

—-

קישורים רלוונטיים:

על Heatmaps & Point Series – ויזואליזציות נחמדות!

Filebeat vs. logstash – אם אתם תוהים מהו “Filebeat”, ומה תפקידו בכוח.

grok (כלי לפירסור לוגים) שייתכן שתתקלו בו.

Kibana official Reference – שהוא לא-רע

ג’אווה, גרסה 3 – להתעורר מהתרדמה הדוגמטית

שפת ג’אווה. קהילה אדירה, אימוץ משמעותי וארוך שנים…

– אבל גם שפה שהגיל ניכר בה:

  • בעיקר, אבל לא רק: תחביר verbose – מרבה-במילים, שכבר לא מקובל בשפות חדשות (למשל: swift, go, וכו’) – ומסיבה טובה.
  • Generics מבוסס “מחיקת טיפוסים” – שהופך את הדברים לקשים יותר, עניין שצץ מחדש בשימוש ב Steams. מחיקת הטיפוסים נעשתה במקור על מנת לשמר תאימות לג’אווה 1.4. למי אכפת היום מקוד שכתוב בג’אווה 1.4?!
  • רעיונות טובים שהתבררו כשגויים לחלוטין לאורך השנים – כמו checked exceptions.
  • פרימיטיביים מול אובייקטים ו auto-boxing.
  • חתימת זיכרון גדולה למדי של ה JVM.
  • ועוד…
הוותיקים שביננו בוודאי זוכרים, ששפת ג’אווה התחילה בתור ״Project Oak״ – כשפה ל”טלוויזיות חכמות” איי שם בשנות ה-90.

בהמשך, הסבו אותה ל”ג’אווה” כשפה ל Applets – המספקת את היכולת לכתוב קוד שירד ברשת – וירוץ בדפדפן. JavaScript נקראה על שמה של ג’אווה – ובאה להציג גרסה “קלילה” יותר לג’אווה, בתחביר ובצריכת המשאבים.

ואז הבינו שהיכולת של “Write Once, Run Anywhere” (או בקיצור: WORA) היא יכולת שמצוי הפוטנציאל שלה הוא לא בטלוויזיות חכמות (יחסית לשנות ה-90), ולא בדפדפנים – אלא דווקא בצד השרת.

כתוב את קוד השרת על מחשב ווינווס (היכן שקל לפתח) – אבל הרץ אותו על Unix (“שרתים אמיתיים”) או Linux (“שרתים זולים – למי שמתעקש”).

היום, “שרתים רציניים” מריצים Linux, ופיתוח הכי נוח לעשות על Mac (ה Linux Subsystem על “חלונות 10” מנסה קצת לאזן את התמונה), אבל היתרון שב WORA – חשוב כבעבר.

את התובנה הגדולה זו – הציגו בכך שהכריזו (ובצדק) שג’אווה עברה לשלב הבא: Java 2!

נוספו מאז ה J2EE, ה JVM נפתח כקוד פתוח – והשאר היסטוריה.

מקור: https://plumbr.io/blog/java/java-version-and-vendor-data-analyzed-2017-edition

לעשות שינוי

חוק תיכנותיקה ידוע, אומר שלא ניתן לבצע שינויים גדולים – ללא disruption.

קהילת ג’אווה, שהייתה קנאית ל Backward Compatibility מאז ומעולם – יודעת זאת.
יכולות כמו Generics או Streams הגיעו לשפה באיטיות – ונעשו בהן פשרות מוחשיות.

פרויקט ה Jigsaw (“פאזל”, ה Java Module System) החל בשנת 2005. הוא תוכנן להשתחרר כחלק מג’אווה 7 (שהתעכבה ל 2011) – אבל שוחרר עכשיו, רק בג’אווה 9 – עשור ושלוש שנים מאז שהחלה העבודה עליו.

האימוץ של גרסה 9 של ג’אווה עתיד להיות אטי – וכואב. מכיוון שג’אווה 9 איננה backward compatible, אנשי התוכנה יהססו לאמץ אותה גם מסיבות פסיכולוגיות, ולא רק סיבות טכניות גרידא.

אני לא טוען שהתאימות לאחור של ג’אווה היא טעות. אותה תאימות לאחור, מתישה ומכאיבה – היא אחת החוזקות של שפת/קהילת ג’אווה.

בשפת ג’אווה עצמה – היכולת לבצע התקדמויות משמעותיות הולכת וממצה את עצמה משנה לשנה. לפחות: התקדמויות ללא disruption. ההתאוששות מחוסר התאימות-לאחור של ג’אווה 9 צפויה לארוך כמה שנים – מה שמפחית מאוד את הסיכוי לשינויים דרמטיים בשפה בשנים הקרובות.

הפתרון הברור הוא להציג את שפת Java גרסה 3: גרסה מחודשת מהשורש, ולא תואמת-לאחור – של השפה.

החבר’ה ברדווד סיטי יכולים לקרוא לה “ג’אווה 300”, מבחינתי. שיזרמו עם תחושת ההתקדמות.

הרבה מימים אפשר לייצר עם המספר 300

חבלי מעבר

המעבר מ”גרסה 2″ ל”גרסה 3″ – אמור לצלצל לכם מוכר. מזהים משהו?

לא? … נסו שוב.

אי אפשר (כך נדמה לי) שלא לחשוב על המעבר ההיסטורי של שפת פייטון מגרסה 2 לגרסה 3 – וההשלכות של המעבר הזה.

המעבר היה מתבקש, מכיוון שהיה כמו תכונות “ישנות” של פייטון שכבר היו “בלתי-נסבלות”:

  • מחרוזות לא היו Unicode (אלא ANSI?)
  • התנהגויות בעייתיות של הטיפוס הכ”כ בסיסי int:
    • אופרטור החלוקה / היה מעגל את התוצאה למספר שלם – מקום נפוץ לשגיאות.
    • int היה כמו BigInteger של ג’אווה – שזה נהדר, חוץ מהעובדה שקוד פייטון מרבה להשתמש בספריות ++C/C (עבור יעילות – זה חלק מהמודל ההנדסי של השפה) – אבל אז ההמרה יצרה בעיות.
  • אופרטור השווה (=, , וכו’) שהיה מופעל על טיפוסים שונים – היה משווה את ה id של האובייקטים, אם לא הייתה מוגדרת צורת ההשוואה – מקור לצרות אמיתיות.
בדרך הכניסו בגרסה 3 גם שיפורים שימושיים (super ללא פרמטרים, set literal, וכו’). המודעות לחשיבות התאימות-לאחור הייתה קיימת , ובהחלט נמנעו משינויים חשובים, אך לא הכרחיים. למשל: בנאי (constructor) בפייטון עדיין נקרא __init__, עוד מהתקופה שמפתחי השפה הניחו שרק הם זקוקים לבנאים – אבל משתמשי השפה לא יזדקקו להם. זהו שינוי מתבקש, אך לא “הכרחי” – ולכן לא נכלל בפייטון 3.

ראשי הקהילה תיארו זאת כך:

“באותה תקופה שעבדנו על פייטון 3 – השימוש בפייטון בעולם צמח בקצב מהיר מאוד. הנחנו שבשל המגמה, יהיה יותר קוד בפייטון 3 מאשר בפייטון 2 תוך מספר שנים – ולכן החלטנו “לספוג את הכאב” שבמעבר לא-תואם לאחור בכוונה לייצר עתיד טוב יותר לקהילת הפייטון. היום ברור שיעבור עוד עשור או יותר – עד שיהיה יותר קוד פייטון 3 מאשר קוד פייטון 2″.

התוכנית לא כ”כ הצליחה: בסיסי קוד שברמת תחזוקה / שליטה בקוד גבוהה – היה ניתן להמיר במאמץ לא גדול – אבל המפתחים לא המירו את הקוד – כי ספריות רבות שהשתמשו בהן עדיין לא עברו לפייטון 3.

מתחזקי ספריות ותיקות כבר הפחיתו את ההשקעה בספריה – ולא היו מוכנים לעשות את המעבר. גם להם היו לפעמים ספריות אחרות שהם תלויים בהן – שלא ביצעו מעבר לפייטון 3. ה Distros העיקריים של לינוקס המשיכו להיות מסופקים עם פייטון 2 – וחוזר חלילה.

וכך – כל העגלה נתקעה. קריאה לקהילה לבצע את המעבר לא נענתה בחיוב, או לפחות לא בקצב מהיר. הוקמו אתרים כמו http://py3readiness.org ו http://python3wos.appspot.com שמדדו ועודדו – את האימוץ של פייטון 3.

פייטון 3 שוחררה ב 2008.

ב 2010 יצא עדכון 2.7 שהיה אמור להיות העדכון האחרון של פייטון 2 – אך הפך במהרה לגרסה הנפוצה ביותר של השפה.
בלית ברירה מפתחי השפה עשו downport לפיצ’רים רבים מפייטון 3 לגרסה 2.7 – על מנת להקל, בהמשך, על המעבר לפייטון 3.

מקוור: http://blog.thezerobit.com/2014/05/25/python-3-is-killing-python.html

ביקורת על המהלך – לא הייתה חסרה. אינספור דיונים התקיימו בעד ונגד פייטון 2/3. השאלה הנפוצה ביותר בפייטון באותה תקופה הייתה ככל הנראה: “אני מתחיל פרויקט חדש. האם להתחיל בפייטון 2 או פייטון 3?”.

היום, כבר עשור מאז שוחררה הגרסה הראשונה של פייטון 3.

הגרסה הנוכחית של פייטון 3 היא גרסה 3.6 – וקראתי כתבה שטענה ש 80% מהקוד החדש נכתב כיום בפייטון 3.
95% מהספריות הנפוצות כבר תומך בפייטון 3, וחלקים מהן מפסיק לתמוך בפייטון 2 (דוגמה: Django). במצב הזה, כנראה – קיוו יוצרי פייטון להיות בשנת 2010. זה קרה “רק” 8 שנים מאוחר יותר.

“הגרסה הבאה של פייטון תהיה פייטון 8, והיא לא תתאם לאחור בכלום” – מתיחת 1 באפריל

נראה שהזעקות נשמעו היטב. לא רק בקהילת הפייטון – אלא בכל עולם התוכנה. “מה קרה שם לחבר’ה בפייטון? – הסתבך להם משהו עם הגרסאות…” היה הדיבור המקובל.

מקור: https://snarky.ca/why-python-3-exists

אם הייתי האחראי העולמי לשפת ג’אווה, ובמיוחד אם אני עובד בתאגיד גדול (שמרן מטבעו) כמו אורקל – בוודאי (!!) שלא הייתי נוגע בתפוח האדמה הלוהט הזה שנקרא Disruption עמוק בשפה עצמה. הייתי משאיר את זה לבא אחרי – שיבוא עוד 10-12 שנים. הוא בטח יעשה את זה, לא?!

נכון, ג’אווה 9 שברה תאימות לאחור לג’אווה 8 – וזה לא עניין של מה בכך. בכל זאת, השינוי נוגע לניהול packages ותלויות – ולא לשפה עצמה. מכיוון שג’אווה היא strongly typed ו static – לא קשה ליצור כלים שיגלו וינחו בשינויים הנדרשים, להלן jdeps. כן צפוי קושי עם ספריות שעושות שימוש כבד ב reflection, ספריות כמו Hibernate או AspectJ.
שינוי בתחביר של השפה – הוא סיפור אחר לגמרי.

מה עושים? האם אין דרך בה ג’אווה תוכל להתחדש – וליישר קו עם שפות מודרניות?

יש פתרון!

לבעיה של מיגרציה בין ישן וחדש, מיגרציה שלא יכולה להתרחש בזמן קצר – יש פתרון מקובל ומוכח בעולם התוכנה. הפתרון הזה נקרא: Interoperability.

אנו נותנים למנגנון החדש והמנגנון הישן לחיות זה-לצד-זה, תוך כדי אפשרות גם לעבוד זה עם זה.
בהדרגה, וללא לחץ – נעביר עוד ועוד קוד למודול החדש, תהליך שגם יכול לארוך שנים.

במקרה שלנו: נספק שני סוגי סיומות קבצים:

  • java. – עבור ג’אווה 2.
  • java3. – עבור ג’אווה 3.
כל עוד התחבירים השונים יוכלו לחיות זה לצד זה – גם באותו הפרויקט, ולתקשר זה עם זה (להפעיל פונקציות / לקבל ערכי חזרה) ללא קושי מיוחד – הפתרון יעבוד. תמיכה ל Java.V2 יש לספק לעוד עשור, לפחות.
באופן זה אוכל לכתוב בג’אווה 3 – תוך כדי שאני ממשיך להשתמש בספריות שעדיין עובדות בג’אווה 2.
אם בפרויקט, אני צריך לכתוב קוד חדש באזורים הכתובים בג’אווה 2 – אני ממשיך לכתוב אותו בג’אווה 2.
באופן זה אני יכול לכתוב מודול חדש בתוך הפרויקט בג’אווה 3, ולהתחיל לחדש שאת הפרויקט שלי בהדרגה – מבלי שאני נדרש להמיר את כולו, ובמכה אחת – לג’אווה 3.

לו פייטון 3 הייתה נוקטת בגישה דומה .- אני משוכנע שהסיפור שלה היה אחר.

האם נרצה לבצע את השיפורים רק בתחביר השפה, או גם ב JVM?
יש כמה דברים טובים שאפשר לשפר ב JVM:

  • היכולת לצרוך פחות זיכרון (תהליך של ג’אווה מגיע לצריכת זיכרון של חצי GB – דיי בקלות) – הוא שיפור חשוב למיקרו-שירותים, ול FaaS (למשל: AWS lambda).
  • שמירת טיפוסי Generics בזמן ריצה (להלן “reified generics”) – הוא שיפור מבורך שיתיר קוד מסובך במקרים רבים. הוא גם יאפשר לכתוב קוד שלא ניתן היום לכתוב בג’אווה.
מצד שני, שינוי ב JVM עלול להיות קשה וארוך לביצוע. לא נרצה לסיים במצב בו עלי להריץ שני תהליכים עבור Interoperatiblity: תהליך של JVM2 ותהליך של JVM3. אני אסיים עם יותר צריכת זיכרון ו overhead משמעותי (IPC) בתקשורת שבין התהליכים.
לכן, לצורך העניין – נסתפק בשיפורים שהם רק ברמת השפה עצמה. נעדיף את האפשרי – על “המושלם”.

תוכנית עבודה

בואו נחשוב לרגע: כמה זמן לוקח לתכנן מחדש ולרענן את התחביר של ג’אווה?
בהתבוננות על שפות אחרות הייתי משערך שמאמץ שכזה (עד GA) יארוך כחמש שנים לגוף זריז ודינאמי, או 10 שנים לגוף מסורבל כמו גוף התקינה של ג’אווה.
אם נתחיל עכשיו, ונוציא את המטלה מידי ה Java Committee – אנחנו יכולים להגיע לשם כבר בסוף שנת 2023!

הבשורות המשמחות הן שתהליך כזה כבר החל בשנת 2011!

ג’אווה 3 כבר מוכנה ועובדת. יש עשרות אלפי מתכנתים שעובדים בה במשרה מלאה. היא production-ready ועם interoperability מוצלח למדי לג’אווה 2!
אני יכול להעיד זאת לאחר עבודה לאורך החודשיים האחרונים עם JavaV3 בסביבת פרודקשיין.

לא פחות משמח: חברת אורקל תמשיך לתמוך ולפתח את Java.V2 באהבה ובמסירות עוד שנים רבות. זה לא פחות חשוב – כי סביר שקוד Java.V2 עוד יחיה שנים רבות.

הקושי העיקרי של Java.V3 היא שהיא זוכה להתעלמות רועמת מצד קהילת הג’אווה הרשמית.
אצילות היא לא דרישה בעולם העסקים, ולהיפך: על אצילות (שפוגעת בשורה התחתונה / נכסים “אסטרטגיים”) – הולכים הביתה.

אף אחד מקהילת הג’אווה הרשמית לא הולך לומר לכם ששפת קוטלין היא בעצם Java.V3.

ולכן, זה הזמן לקהילה החופשית לעשות את השינוי.

יצא לי לשחק בזמנו בסקאלה – שפה אפית, רחבת-יריעה – בצורה מזיקה, לטעמי. יש מי שאמר ש “Scala היא לא שפת תכנות – היא פלטפורה לשפת תכנות. אתה בוחר את subset היכולות שאתה רוצה לעבוד איתן – ויוצר בכך בפועל מופע של שפת-תכנות”. כמו כן, ה Interoperability בין ג’אווה 2 לסקאלה – הוא לא חלק, בלשון המעטה.

יצא לי לכתוב גם קצת בגרובי. שפה פשוטה וכיפית – אך הבחירה באופי דינאמי, והתאימות המלאה (אך הלא-מחייבת) לתחביר של ג’אווה 2 – יצר שפה ללא פילוסופיה ועקרונות ברורים. יוצר השפה המקורי נטש אותה (ותרם קצת קוד לקוטלין), וגם Pivotal הפסיקה לספק תמיכה – לפני כשנתיים.

בכל מקרה, ל2 השפות הנ”ל הייתה ההזדמנויות להגיע ל adoption משמעותי – אך הן לא צלחו. מכיוון שהן תוכננו לפני עשור ויותר – הן כבר לא יהיו Java V3. לכל היותר – Java V2.5 שנראה עם פוטנציאל – אבל לא הצליח.

מקור: http://pypl.github.io/PYPL.html – בעת כתיבת הפוסט

קוטלין כן הצליחה ליצור spike של adoption – בעיקר על בסיס מתכנתי אנדרואיד.

בעקבות האימוץ הטבעי של קוטלין בקהילת האנדוראיד, וגם בשל הלחץ שהופעל עליה מצד קהילת ה iOS (מול Objective-C Java.V2 נראתה טוב, אבל בהשוואה ל Swift – היא כבר נראית עייפה ומסורבלת) – גוגל הפכה את קוטלין לשפה רשמית של אנדוראיד.

קוטלין היא שפה מודרנית בהחלט: תחביר פשוט, פונקציות הן 1st Class citizens בשפה עצמה, יש לה הגנה בפני nulls, שיפורים לספריות, extension functions, ועוד). קוטלין גם מאפשרת Interoperability מוצלח מאוד מול Java.V2, ויש לה IDE חינמי, הרץ על שלושת מערכות ההפעלה – עם תמיכה מעולה בשפה.

בקיצור:

קוטלין היא המועמדת הטובה ביותר לתפקיד Java.V3!

אני מוכן להעיד על כך מניסיון אישי. זו פשוט שפה טובה.

מה נותר? נותר לנו – הקהילה, לאמץ אותה!

אני באמת לא רואה סיבה מדוע לא לאמץ אותה בחום בצד-השרת. עשינו זאת בחברת Next Insurance – בהצלחה רבה!

השיפור בפריון וקריאות הקוד – הוא לא שיפור ש Java.V2 תוכל איי פעם להשיג – במגבלות שחלות עליה. מודל ה Interoperability למעבר בין גרסאות שפה – הוא מצוין. לא יהיה כאן שוד ושבר.

האם באמת מפתחי הג’אווה שבעו מחידושים, והם מסתפקים בזחילה לכיוון ג’אווה 9, או ב Spring Boot – כהתקדמות בסביבת העבודה שלהם?!

אני חושב שבאמת יש כאן בשורה, עם פספוס גדול של ה Mainstream של קהילת צד-השרת הג’אווה.

אם אתם עוסקים בג׳אווה בצד השרת – הייתי ממליץ ללמוד ולבחון קצת את השפה. להבין מה היא מחדשת מול ג’אווה.
אם אתם מתחברים לפילוסופיה – אפשר להתחיל מיקרו-שירות או פרויקט צדדי ולהתנסות בשפה. אפשר, באותה המידה, לכתוב מודול בתוך פרויקט קיים. השפות חיות בשלום זו לצד זו.

מי יותר מקהילת הסטאראט-אפים בישראל מתאים להחיל כזה שינוי?

לסיכום (עודכן)

משפת ג’אווה כבר קשה לצפות לשיפורים משמעותיים, ובטח לא בעתיד הקרוב.
גרסה 8 של ג’אווה “סחטה” דיי הרבה שיפורים – מהמצב הנתון, וזו הייתה התבלטות לטובה. בשנים הקרובות – נראה שקהילת הג’אווה תהיה עסוקה בעיקר במעבר ל Java Module System. שיפור חיובי – אך הדרגתי. שיפור שאגב יחול – על כל שפות ה JVM.

בכלל, כמה וכמה חידושים ברמת השפה בג’אווה 9 נראים דומים בצורה מחשידה ליכולות שיש כבר בקוטלין: Stream API improvements? – כבר יש. private interface methods – כבר יש. REPL? – ברור שיש. גם default interface methods הגיעו לקוטלין לפני שהגיעו לג’אווה.
אני חושד שהיוצרים של קוטלין מסתכלים על ה JSRs המתגלגלים של ג’אווה, ומביאים אותם לקוטלין – פשוט יותר מהר.

אם אתם מפתחים ג’אווה רוצים לשפר את כלי העבודה שלכם, שיפור משמעותי – קוטלין היא אופציה ממשית וחשובה.

אני מניח שאם אורקל הייתה מכריזה שקוטלין היא המשך טבעי לג’אווה – הייתה על קוטלין הסתערות רבתי.
אורקל לא תכריז זאת, אבל האם רק הכרזה של חברת ענק, בעלת אינטרסים וצרכים משלה – הוא מה שיזיז את הקהילה?

קהילת האנדרואיד אימצה את קוטלין בצורה “טבעית”. למען ההגינות: כנראה שהיה שם לחץ גדול יותר מזה המונח על קהילת צד-השרת: קהילת האנדרואיד הייתה “תקועה” עם ג’אווה 6.

כרגע, האימוץ של קוטלין בצד-השרת נראה ממש זניח. עבורי – זהו פער בלתי-מובן מול הפוטנציאל.
הייתי מצפה שחברות חדשניות יסתערו על קוטלין – פשוט בגלל שהיא מוצר מוצלח.

האם אני מפספס משהו? האם אתם יודעים להסביר מדוע מעט כ”כ אנשי ג’אווה (צד-שרת) מתעניינים באימוץ של קוטלין?

אשמח לתגובות 🙂