Android מציעה תמיכה מובנית ב-SQLite, מסד נתונים יעיל של SQL. כדי לשפר את הביצועים של האפליקציה ולוודא שהיא תישאר מהירה גם כשהנתונים יגדלו, כדאי לפעול לפי השיטות המומלצות הבאות. בנוסף, השימוש בשיטות המומלצות האלה מפחית את הסיכוי להיתקל בבעיות בביצועים שקשה לשחזר ולפתור.
כדי לשפר את הביצועים, כדאי לפעול לפי העקרונות הבאים:
קריאה של פחות שורות ועמודות: כדאי לבצע אופטימיזציה של השאילתות כדי לאחזר רק את הנתונים הדרושים. כדאי לצמצם את כמות הנתונים שנקראים ממסד הנתונים, כי אחזור של עודף נתונים עלול להשפיע על הביצועים.
העברת עבודה למנוע SQLite: ביצוע חישובים, סינון ומיון של פעולות בשאילתות SQL. שימוש במנוע השאילתות של SQLite יכול לשפר באופן משמעותי את הביצועים.
שינוי סכימת מסד הנתונים: עיצוב סכימת מסד הנתונים כדי לעזור ל-SQLite ליצור תוכניות יעילות לשאילתות ולייצוגי נתונים. כדאי ליצור אינדקסים לטבלאות בצורה נכונה ולבצע אופטימיזציה של מבני הטבלאות כדי לשפר את הביצועים.
בנוסף, אתם יכולים להשתמש בכלים לפתרון בעיות שזמינים כדי למדוד את הביצועים של מסד הנתונים של SQLite ולזהות תחומים שצריך לבצע בהם אופטימיזציה.
מומלץ להשתמש בספריית Jetpack Room.
הגדרת מסד הנתונים לביצועים
כדי להגדיר את מסד הנתונים לביצועים אופטימליים ב-SQLite, פועלים לפי השלבים שמפורטים בקטע הזה.
הפעלת רישום מראש ביומן
ב-SQLite, המערכת מטמיעה שינויים על ידי הוספה שלהם ליומן, שמתבצעת בו דחיסה מדי פעם במסד הנתונים. הפעולה הזו נקראת כתיבה מראש ביומן (WAL).
מפעילים את WAL
אלא אם משתמשים ב-ATTACH DATABASE.
הגדרת שיטת סנכרון פחות מחמירה
כשמשתמשים ב-WAL, כברירת מחדל כל פעולת commit מנפיקה fsync כדי לוודא שהנתונים מגיעים לדיסק. כך משפרים את עמידות הנתונים, אבל מאטים את פעולות ה-commit.
ב-SQLite יש אפשרות לשלוט במצב הסינכרוני. אם מפעילים את WAL, צריך להגדיר את המצב הסינכרוני ל-NORMAL:
// When opening the database
val paramsBuilder: SQLiteDatabase.OpenParams.Builder = SQLiteDatabase.OpenParams.Builder()
paramsBuilder.journalMode = SQLiteDatabase.SYNC_MODE_NORMAL
// Or: after having opened the database
db.execSQL("PRAGMA synchronous = NORMAL");
בהגדרה הזו, אפשר להחזיר אישור לפני שהנתונים מאוחסנים בדיסק. אם מתרחש אירוע השבתה של המכשיר, למשל בגלל הפסקת חשמל או תגובה לשגיאת ליבה קריטית, יכול להיות שהנתונים שנשמרו יאבדו. עם זאת, בגלל הרישום ביומן, מסד הנתונים לא נפגם.
אם רק האפליקציה קורסת, הנתונים עדיין מגיעים לדיסק. ברוב האפליקציות, ההגדרה הזו משפרת את הביצועים ללא עלות משמעותית.
הגדרת סכימות יעילות של טבלאות
כדי לשפר את הביצועים ולצמצם את צריכת הנתונים, צריך להגדיר סכימת טבלה יעילה. SQLite בונה תוכניות שאילתה ונתונים יעילים, וכך מאפשר שליפה מהירה יותר של נתונים. בקטע הזה מפורטות שיטות מומלצות ליצירת סכימות של טבלאות.
כדאי לשקול INTEGER PRIMARY KEY
בדוגמה הזו, מגדירים ומאכלסים טבלה באופן הבא:
CREATE TABLE Customers(
id INTEGER,
name TEXT,
city TEXT
);
INSERT INTO Customers Values(456, 'John Lennon', 'Liverpool, England');
INSERT INTO Customers Values(123, 'Michael Jackson', 'Gary, IN');
INSERT INTO Customers Values(789, 'Dolly Parton', 'Sevier County, TN');
הפלט של הטבלה אמור להיראות כך:
| rowid | id | שם | עיר |
|---|---|---|---|
| 1 | 456 | ג'ון לנון | ליברפול, אנגליה |
| 2 | 123 | מייקל ג'קסון | גארי, אינדיאנה |
| 3 | 789 | דולי פרטון | מחוז סוויר, טנסי |
העמודה rowid היא אינדקס ששומר על סדר ההוספה. שאילתות שמסננות לפי rowid מיושמות כחיפוש מהיר בעץ B, אבל שאילתות שמסננות לפי id הן סריקה איטית של הטבלה.
אם אתם מתכננים לבצע חיפושים לפי id, תוכלו להימנע מאחסון העמודה rowid כדי לאחסן פחות נתונים ולקבל מסד נתונים מהיר יותר באופן כללי:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
הטבלה שלכם תיראה עכשיו כך:
| id | שם | עיר |
|---|---|---|
| 123 | מייקל ג'קסון | גארי, אינדיאנה |
| 456 | ג'ון לנון | ליברפול, אנגליה |
| 789 | דולי פרטון | מחוז סוויר, טנסי |
מכיוון שלא צריך לאחסן את העמודה rowid, השאילתות של id מהירות. הערה:
הטבלה ממוינת עכשיו לפי id במקום לפי סדר ההכנסה.
שיפור מהירות השאילתות באמצעות אינדקסים
SQLite משתמש באינדקסים כדי להאיץ את השאילתות. כשמסננים (WHERE), ממיינים (ORDER BY) או מצברים (GROUP BY) עמודה, אם בטבלה יש אינדקס לעמודה, השאילתה מואצת.
בדוגמה הקודמת, סינון לפי city מחייב סריקה של כל הטבלה:
SELECT id, name
WHERE city = 'London, England';
אם יש אפליקציה עם הרבה שאילתות לגבי ערים, אפשר להשתמש באינדקס כדי להריץ את השאילתות האלה מהר יותר:
CREATE INDEX city_index ON Customers(city);
אינדקס מיושם כטבלה נוספת, שממוינת לפי עמודת האינדקס וממופה ל-rowid:
| עיר | rowid |
|---|---|
| גארי, אינדיאנה | 2 |
| ליברפול, אנגליה | 1 |
| מחוז סוויר, טנסי | 3 |
שימו לב שעלות האחסון של העמודה city מוכפלת, כי היא מופיעה עכשיו גם בטבלה המקורית וגם באינדקס. מכיוון שאתם משתמשים באינדקס, העלות של אחסון נוסף שווה את היתרון של שאילתות מהירות יותר.
עם זאת, כדי להימנע מתשלום על אחסון של אינדקס שלא משמש לשיפור הביצועים של השאילתות, לא כדאי לשמור אינדקס שלא משתמשים בו.
יצירת אינדקסים עם כמה עמודות
אם השאילתות שלכם משלבות כמה עמודות, אתם יכולים ליצור אינדקסים של כמה עמודות כדי להאיץ את השאילתה באופן מלא. אפשר גם להשתמש באינדקס בעמודה חיצונית ולאפשר לחיפוש הפנימי להתבצע כסריקה לינארית.
לדוגמה, אם נתונה השאילתה הבאה:
SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name
כדי להאיץ את השאילתה, אפשר להשתמש באינדקס רב-עמודתי באותו סדר שצוין בשאילתה:
CREATE INDEX city_name_index ON Customers(city, name);
עם זאת, אם יש לכם אינדקס רק על city, המיון החיצוני עדיין מואץ, בעוד שהמיון הפנימי דורש סריקה לינארית.
האפשרות הזו פועלת גם עם שאילתות של קידומות. לדוגמה, אינדקס ON Customers (city, name) גם מאיץ את הסינון, המיון והקיבוץ לפי city, כי טבלת האינדקס של אינדקס רב-עמודתי מסודרת לפי האינדקסים שצוינו בסדר שצוין.
כדאי לשקול WITHOUT ROWID
כברירת מחדל, SQLite יוצר עמודה בשם rowid לטבלה, כאשר rowid הוא INTEGER PRIMARY KEY AUTOINCREMENT מרומז. אם כבר יש לכם עמודה שהיא INTEGER PRIMARY KEY, העמודה הזו הופכת לכינוי של rowid.
בטבלאות שיש להן מפתח ראשי שאינו INTEGER או שילוב של עמודות, כדאי להשתמש ב-WITHOUT ROWID.
אחסון נתונים קטנים כ-BLOB ונתונים גדולים כקובץ
אם רוצים לשייך נתונים גדולים לשורה, כמו תמונה ממוזערת או תמונה של איש קשר, אפשר לאחסן את הנתונים בעמודה BLOB או בקובץ, ואז לאחסן את הנתיב בעמודה.
בדרך כלל, הגודל של הקבצים מעוגל כלפי מעלה במרווחים של 4KB. בקובצי נתונים קטנים מאוד, שבהם שגיאת העיגול משמעותית, עדיף לאחסן אותם במסד הנתונים כ-BLOB. SQLite מצמצם את הקריאות למערכת הקבצים, ובחלק מהמקרים הוא מהיר יותר ממערכת הקבצים הבסיסית.
שיפור הביצועים של שאילתות
כדי לשפר את ביצועי השאילתות ב-SQLite, כדאי לפעול לפי השיטות המומלצות הבאות כדי לצמצם את זמני התגובה ולמקסם את יעילות העיבוד.
יכול להחזיר רק שורה אחת או אפס שורות.קריאה רק של השורות שצריך
המסננים מאפשרים לצמצם את התוצאות על ידי ציון קריטריונים מסוימים, כמו טווח תאריכים, מיקום או שם. ההגבלות מאפשרות לכם לקבוע את מספר התוצאות שיוצגו:
db.rawQuery("""
SELECT name
FROM Customers
LIMIT 10;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
// Process cursor data
}
}
קריאה רק של העמודות שצריך
מומלץ להימנע מבחירה של עמודות לא נחוצות, כי זה עלול להאט את השאילתות ולבזבז משאבים. במקום זאת, בוחרים רק את העמודות שבהן משתמשים.
בדוגמה הבאה, בוחרים באפשרויות id, name ו-phone:
// This is not the most efficient way of doing this.
// See the following example for a better approach.
db.rawQuery(
"""
SELECT id, name, phone
FROM customers;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
val name = cursor.getString(1)
// Further processing
}
}
עם זאת, צריך רק את העמודה name:
db.rawQuery("""
SELECT name
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
val name = cursor.getString(0)
// Further processing
}
}
הוספת פרמטרים לשאילתות
מחרוזת השאילתה יכולה לכלול פרמטר שמוכר רק בזמן הריצה, כמו:
fun getNameById(id: Long): String?
db.rawQuery(
"SELECT name FROM customers WHERE id=$id", null
).use { cursor ->
return if (cursor.moveToFirst()) {
cursor.getString(0)
} else {
null
}
}
}
בקוד שלמעלה, כל שאילתה יוצרת מחרוזת שונה, ולכן היא לא נהנית ממטמון ההצהרות. כל קריאה מחייבת קומפילציה של SQLite לפני ההרצה. במקום זאת, אפשר להחליף את הארגומנט id בפרמטר ולקשר את הערך באמצעות selectionArgs:
fun getNameById(id: Long): String? {
db.rawQuery(
"""
SELECT name
FROM customers
WHERE id=?
""".trimIndent(), arrayOf(id.toString())
).use { cursor ->
return if (cursor.moveToFirst()) {
cursor.getString(0)
} else {
null
}
}
}
עכשיו אפשר לקמפל את השאילתה פעם אחת ולשמור אותה במטמון. השאילתה המהודרת מנוצלת מחדש בין הפעלות שונות של getNameById(long).
ביצוע איטרציות ב-SQL ולא בקוד
משתמשים בשאילתה אחת שמחזירה את כל התוצאות הרלוונטיות, במקום בלולאה תוכניתית שחוזרת על שאילתות SQL כדי להחזיר תוצאות נפרדות. הלולאה התוכניתית איטית פי 1,000 משאילתת SQL אחת.
שימוש במשתנה DISTINCT לערכים ייחודיים
השימוש במילת המפתח DISTINCT יכול לשפר את הביצועים של השאילתות על ידי צמצום כמות הנתונים שצריך לעבד. לדוגמה, אם רוצים להחזיר רק את הערכים הייחודיים מעמודה, משתמשים בפונקציה DISTINCT:
db.rawQuery("""
SELECT DISTINCT name
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
// Only iterate over distinct names in Kotlin
// Process distinct name
}
}
השתמשו בפונקציות צבירה כשהדבר אפשרי
להשתמש בפונקציות צבירה כדי לצבור תוצאות בלי נתוני שורות. לדוגמה, הקוד הבא בודק אם יש לפחות שורה אחת שתואמת:
// This is not the most efficient way of doing this.
// See the following example for a better approach.
db.rawQuery("""
SELECT id, name
FROM Customers
WHERE city = 'Paris';
""".trimIndent(),
null
).use { cursor ->
if (cursor.moveToFirst()) {
// At least one customer from Paris
// Handle found
} else {
// No customers from Paris
// Handle not found
}
כדי לאחזר רק את השורה הראשונה, אפשר להשתמש ב-EXISTS() כדי להחזיר 0 אם לא קיימת שורה תואמת, ו-1 אם קיימת שורה אחת או יותר שתואמת:
db.rawQuery("""
SELECT EXISTS (
SELECT null
FROM Customers
WHERE city = 'Paris';
);
""".trimIndent(),
null
).use { cursor ->
if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
// At least one customer from Paris
// Handle found
} else {
// No customers from Paris
// Handle not found
}
}
שימוש בפונקציות מצטברות של SQLite בקוד האפליקציה:
-
COUNT: סופרת כמה שורות יש בעמודה. -
SUM: מוסיפה את כל הערכים המספריים בעמודה. -
MINאוMAX: קובעים את הערך הנמוך או הגבוה ביותר. הפונקציה פועלת עבור עמודות מספריות, סוגיDATEוסוגי טקסט. -
AVG: מחזירה את הערך המספרי הממוצע. GROUP_CONCAT: שרשור מחרוזות עם מפריד אופציונלי.
במקום Cursor.getCount(), צריך להשתמש ב-COUNT()
בדוגמה הבאה, הפונקציה Cursor.getCount() קוראת את כל השורות ממסד הנתונים ומחזירה את כל ערכי השורות:
// This is not the most efficient way of doing this.
// See the following example for a better approach.
db.rawQuery("""
SELECT id
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
val count = cursor.getCount()
// Use count
}
אבל אם משתמשים ב-COUNT(), מסד הנתונים מחזיר רק את המספר:
db.rawQuery("""
SELECT COUNT(*)
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
cursor.moveToFirst()
val count = cursor.getInt(0)
// Use count
}
הוספת שאילתות במקום קוד
אפשר להרכיב שאילתות SQL, והיא תומכת בשאילתות משנה, באיחודים ובאילוצים של מפתחות זרים. אפשר להשתמש בתוצאה של שאילתה אחת בשאילתה אחרת בלי לעבור דרך קוד האפליקציה. כך לא צריך להעתיק נתונים מ-SQLite ומנוע מסד הנתונים יכול לבצע אופטימיזציה של השאילתה.
בדוגמה הבאה, אפשר להריץ שאילתה כדי לגלות באיזו עיר יש הכי הרבה לקוחות, ואז להשתמש בתוצאה בשאילתה אחרת כדי למצוא את כל הלקוחות מהעיר הזו:
// This is not the most efficient way of doing this.
// See the following example for a better approach.
db.rawQuery("""
SELECT city
FROM Customers
GROUP BY city
ORDER BY COUNT(*) DESC
LIMIT 1;
""".trimIndent(),
null
).use { cursor ->
if (cursor.moveToFirst()) {
val topCity = cursor.getString(0)
db.rawQuery("""
SELECT name, city
FROM Customers
WHERE city = ?;
""".trimIndent(),
arrayOf(topCity)).use { innerCursor ->
while (innerCursor.moveToNext()) {
// Process inner cursor data
}
}
}
}
כדי לקבל את התוצאה בחצי מהזמן של הדוגמה הקודמת, אפשר להשתמש בשאילתת SQL אחת עם הצהרות מקוננות:
db.rawQuery("""
SELECT name, city
FROM Customers
WHERE city IN (
SELECT city
FROM Customers
GROUP BY city
ORDER BY COUNT (*) DESC
LIMIT 1;
);
""".trimIndent(),
null
).use { cursor ->
if (cursor.moveToNext()) {
// Process cursor data
}
}
בדיקת ייחודיות ב-SQL
אם יש שורה שאסור להוסיף אלא אם ערך מסוים בעמודה הוא ייחודי בטבלה, יכול להיות שיותר יעיל להגדיר את הייחודיות הזו כמגבלה של העמודה.
בדוגמה הבאה, שאילתה אחת מורצת כדי לאמת את השורה שרוצים להוסיף, ושאילתה אחרת מורצת כדי להוסיף אותה בפועל:
// This is not the most efficient way of doing this.
// See the following example for a better approach.
db.rawQuery(
"""
SELECT EXISTS (
SELECT null
FROM customers
WHERE username = ?
);
""".trimIndent(),
arrayOf(customer.username)
).use { cursor ->
if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
throw AddCustomerException(customer)
}
}
db.execSQL(
"INSERT INTO customers VALUES (?, ?, ?)",
arrayOf(
customer.id.toString(),
customer.name,
customer.username
)
)
במקום לבדוק את האילוץ הייחודי ב-Kotlin, אפשר לבדוק אותו ב-SQL כשמגדירים את הטבלה:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
username TEXT UNIQUE
);
SQLite עושה את אותו הדבר כמו הפעולות הבאות:
CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);
עכשיו אפשר להוסיף שורה ולתת ל-SQLite לבדוק את האילוץ:
try {
db.execSql(
"INSERT INTO Customers VALUES (?, ?, ?)",
arrayOf(customer.id.toString(), customer.name, customer.username)
)
} catch(e: SQLiteConstraintException) {
throw AddCustomerException(customer, e)
}
SQLite תומך באינדקסים ייחודיים עם כמה עמודות:
CREATE TABLE table(...);
CREATE UNIQUE INDEX unique_table ON table(column1, column2, ...);
SQLite מאמת אילוצים מהר יותר ועם תקורה נמוכה יותר מאשר קוד Kotlin. מומלץ להשתמש ב-SQLite במקום בקוד האפליקציה.
הוספה של כמה פריטים בבת אחת בעסקה אחת
עסקה מבצעת כמה פעולות, מה שמשפר לא רק את היעילות אלא גם את הדיוק. כדי לשפר את עקביות הנתונים ולזרז את הביצועים, אפשר להוסיף את הנתונים בקבוצות:
db.beginTransaction()
try {
customers.forEach { customer ->
db.execSql(
"INSERT INTO Customers VALUES (?, ?, ?)",
arrayOf(customer.id.toString(), customer.name, "customerValue")
)
}
} finally {
db.endTransaction()
}
שימוש בכלים לפתרון בעיות
SQLite מספקת את הכלים הבאים לפתרון בעיות כדי לעזור למדוד את הביצועים.
שימוש בפרומפט האינטראקטיבי של SQLite
מריצים SQLite במחשב כדי להריץ שאילתות וללמוד.
בגרסאות שונות של פלטפורמת Android נעשה שימוש בגרסאות שונות של SQLite. כדי להשתמש באותו מנוע שמופעל במכשיר Android, משתמשים ב-adb shell ומריצים את sqlite3 במכשיר היעד.
אפשר לבקש מ-SQLite לתזמן שאילתות:
sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...
EXPLAIN QUERY PLAN
אפשר לבקש מ-SQLite להסביר איך הוא מתכוון להשיב לשאילתה באמצעות EXPLAIN QUERY PLAN:
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SCAN Customers
בדוגמה הקודמת נדרש סריקה מלאה של הטבלה ללא אינדקס כדי למצוא את כל הלקוחות מפריז. זה נקרא מורכבות לינארית. SQLite צריך לקרוא את כל השורות ולשמור רק את השורות שתואמות ללקוחות מפריז. כדי לפתור את הבעיה, אפשר להוסיף אינדקס:
sqlite> CREATE INDEX Idx1 ON Customers(city);
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SEARCH test USING INDEX Idx1 (city=?
אם אתם משתמשים במעטפת האינטראקטיבית, אתם יכולים לבקש מ-SQLite להסביר תמיד את תוכניות השאילתות:
sqlite> .eqp on
מידע נוסף זמין במאמר בנושא תכנון שאילתות.
כלי לניתוח SQLite
SQLite מציע את ממשק שורת הפקודה (CLI) sqlite3_analyzer כדי להציג מידע נוסף שאפשר להשתמש בו לפתרון בעיות בביצועים. כדי להתקין, צריך להיכנס אל דף ההורדה של SQLite.
אתם יכולים להשתמש ב-adb pull כדי להוריד קובץ מסד נתונים ממכשיר היעד לתחנת העבודה שלכם לצורך ניתוח:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
דפדפן SQLite
אפשר גם להתקין את כלי ה-GUI SQLite Browser בדף ההורדות של SQLite Downloads page.
רישום ביומן ב-Android
מערכת Android מודדת את הזמן של שאילתות SQLite ומתעדת אותן בשבילכם:
# Enable query time logging
$ adb shell setprop log.tag.SQLiteTime VERBOSE
# Disable query time logging
$ adb shell setprop log.tag.SQLiteTime ERROR
Perfetto tracing
כשמגדירים את Perfetto, אפשר להוסיף את הקוד הבא כדי לכלול מסלולים לשאילתות ספציפיות:
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "database"
}
}
}
dumpsys meminfo
adb shell dumpsys meminfo <package-name> יציג נתונים סטטיסטיים שקשורים לשימוש בזיכרון של האפליקציה, כולל פרטים מסוימים על זיכרון SQLite. לדוגמה, הפלט הבא נלקח מ-adb shell dumpsys meminfo com.google.android.gms.persistent במכשיר של מפתח:
DATABASES
pgsz dbsz Lookaside(b) cache hits cache misses cache size Dbname
PER CONNECTION STATS
4 52 45 8 41 6 /data/user/10/com.google.android.gms/databases/gaia-discovery
4 8 0 0 0 (attached) temp
4 52 56 5 23 6 /data/user/10/com.google.android.gms/databases/gaia-discovery (1)
4 252 95 233 124 12 /data/user_de/10/com.google.android.gms/databases/phenotype.db
4 8 0 0 0 (attached) temp
4 252 17 0 17 1 /data/user_de/10/com.google.android.gms/databases/phenotype.db (1)
4 9280 105 103169 69805 25 /data/user/10/com.google.android.gms/databases/phenotype.db
4 20 0 0 0 (attached) temp
4 9280 108 13877 6394 25 /data/user/10/com.google.android.gms/databases/phenotype.db (2)
4 8 0 0 0 (attached) temp
4 9280 105 12548 5519 25 /data/user/10/com.google.android.gms/databases/phenotype.db (3)
4 8 0 0 0 (attached) temp
4 9280 107 18328 7886 25 /data/user/10/com.google.android.gms/databases/phenotype.db (1)
4 8 0 0 0 (attached) temp
4 36 51 156 29 5 /data/user/10/com.google.android.gms/databases/mobstore_gc_db_v0
4 36 97 47 27 10 /data/user/10/com.google.android.gms/databases/context_feature_default.db
4 36 56 3 16 4 /data/user/10/com.google.android.gms/databases/context_feature_default.db (2)
4 300 40 2111 24 5 /data/user/10/com.google.android.gms/databases/gservices.db
4 300 39 3 17 4 /data/user/10/com.google.android.gms/databases/gservices.db (1)
4 20 17 0 14 1 /data/user/10/com.google.android.gms/databases/gms.notifications.db
4 20 33 1 15 2 /data/user/10/com.google.android.gms/databases/gms.notifications.db (1)
4 120 40 143 163 4 /data/user/10/com.google.android.gms/databases/android_pay
4 120 123 86 32 19 /data/user/10/com.google.android.gms/databases/android_pay (1)
4 28 33 4 17 3 /data/user/10/com.google.android.gms/databases/googlesettings.db
POOL STATS
cache hits cache misses cache size Dbname
13 68 81 /data/user/10/com.google.android.gms/databases/gaia-discovery
233 145 378 /data/user_de/10/com.google.android.gms/databases/phenotype.db
147921 89616 237537 /data/user/10/com.google.android.gms/databases/phenotype.db
156 30 186 /data/user/10/com.google.android.gms/databases/mobstore_gc_db_v0
50 57 107 /data/user/10/com.google.android.gms/databases/context_feature_default.db
2114 43 2157 /data/user/10/com.google.android.gms/databases/gservices.db
1 31 32 /data/user/10/com.google.android.gms/databases/gms.notifications.db
229 197 426 /data/user/10/com.google.android.gms/databases/android_pay
4 18 22 /data/user/10/com.google.android.gms/databases/googlesettings.db
בקטע DATABASES יופיעו:
-
pgsz: הגודל של דף אחד במסד הנתונים, ב-KB. -
dbsz: הגודל של כל מסד הנתונים, בדפים. כדי לקבל את הגודל ב-KB, מכפילים אתpgszב-dbsz. -
Lookaside(b): הזיכרון שהוקצה למאגר lookaside של SQLite לכל חיבור, בבייטים. הן בדרך כלל קטנות מאוד. -
cache hits: SQLite שומר מטמון של דפי מסד נתונים. זהו מספר ההתאמות במטמון הדפים (ספירה). -
cache misses: מספר הפעמים שבהן לא נמצאו נתונים במטמון הדפים (ספירה). -
cache size: מספר הדפים במטמון (ספירה). כדי לקבל את הגודל בקילו-בייט, מכפילים את המספר הזה ב-pgsz. -
Dbname: הנתיב לקובץ ה-DB. בדוגמה שלנו, לחלק ממסדי הנתונים מצורף(1)או מספר אחר לשם, כדי לציין שיש יותר מחיבור אחד לאותו מסד נתונים בסיסי. הנתונים הסטטיסטיים נמדדים לפי חיבור.
בקטע POOL STATS יופיעו:
-
cache hits: SQLite שומר במטמון הצהרות מוכנות ומנסה לעשות בהן שימוש חוזר כשמריצים שאילתות, כדי לחסוך מאמץ וזיכרון בהידור של הצהרות SQL. זה מספר ההיטים במטמון של ההצהרות (ספירה). -
cache misses: מספר הפעמים שבהן לא נמצאה התאמה במטמון של ההצהרות (ספירה). -
cache size: החל מ-Android 17, הערך הזה מציין את המספר הכולל של ההצהרות המוכנות במטמון. בגרסאות קודמות, הערך הזה שווה לסכום של ההתאמות והפספוסים שמופיעים בשתי העמודות האחרות, והוא לא מייצג את גודל המטמון.
מקורות מידע נוספים
צפיות בתוכן
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- הרצת בדיקות השוואה באינטגרציה רציפה
- פריימים קפואים
- יצירה ומדידה של פרופילי Baseline בלי Macrobenchmark