يتوافق Android تلقائيًا مع SQLite، وهي قاعدة بيانات SQL فعّالة. اتّبِع أفضل الممارسات التالية لتحسين أداء تطبيقك، ما يضمن بقاءه سريعًا وبسرعة متوقّعة مع زيادة بياناتك. من خلال استخدام أفضل الممارسات هذه، يمكنك أيضًا تقليل احتمالية مواجهة مشاكل في الأداء يصعب إعادة إنتاجها وتحديد المشاكل وحلّها.
لتحقيق أداء أسرع، اتّبِع مبادئ الأداء التالية:
قراءة عدد أقل من الصفوف والأعمدة: يمكنك تحسين طلبات البحث لاسترداد البيانات الضرورية فقط. قلِّل حجم البيانات التي تتم قراءتها من قاعدة البيانات، لأنّ استرداد البيانات الزائدة يمكن أن يؤثر في الأداء.
نقل العمل إلى محرّك SQLite: يمكنك إجراء العمليات الحسابية وعمليات الفلترة والفرز ضمن طلبات بحث SQL. يمكن أن يؤدي استخدام محرّك طلبات البحث في SQLite إلى تحسين الأداء بشكلٍ كبير.
تعديل مخطط قاعدة البيانات: يمكنك تصميم مخطط قاعدة البيانات لمساعدة SQLite في إنشاء خطط طلبات بحث وعروض بيانات فعّالة. يمكنك فهرسة الجداول بشكلٍ صحيح وتحسين هياكل الجداول لتحسين الأداء.
بالإضافة إلى ذلك، يمكنك استخدام أدوات تحديد المشاكل وحلّها المتاحة لقياس أداء قاعدة بيانات SQLite للمساعدة في تحديد المجالات التي تتطلّب التحسين.
ننصحك باستخدام مكتبة Jetpack Room.
إعداد قاعدة البيانات لتحسين الأداء
اتّبِع الخطوات الواردة في هذا القسم لإعداد قاعدة البيانات لتحقيق أفضل أداء في SQLite.
تفعيل ميزة Write-Ahead Logging
تنفّذ SQLite عمليات التعديل من خلال إلحاقها بسجلّ، ثم تضغطها بشكلٍ دوري في قاعدة البيانات. يُعرف ذلك باسم Write-Ahead Logging (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");
في هذا الإعداد، يمكن أن يعرض إجراء `commit` نتيجة قبل تخزين البيانات على قرص. في حال إيقاف تشغيل الجهاز، مثلاً عند انقطاع التيار الكهربائي أو حدوث ذعر النواة، قد يتم فقدان البيانات التي تم إجراء `commit` لها. ومع ذلك، لا تتلف قاعدة البيانات بسبب التسجيل.
إذا تعرّض تطبيقك فقط للتعطّل، ستظل بياناتك تصل إلى القرص. بالنسبة إلى معظم التطبيقات، يؤدي هذا الإعداد إلى تحسينات في الأداء بدون تكلفة مادية.
تحديد مخططات جداول فعّالة
لتحسين الأداء وتقليل استهلاك البيانات، حدِّد مخطط جدول فعّالاً. ينشئ 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 | John Lennon | Liverpool, England |
| 2 | 123 | مايكل جاكسون | غاري، إنديانا |
| 3 | 789 | Dolly Parton | Sevier County, TN |
العمود rowid هو
فهرس يحافظ على ترتيب الإدراج. يتم تنفيذ طلبات البحث التي
يتم فلترتها حسب rowid كبحث سريع في شجرة B، ولكن طلبات البحث التي
يتم فلترتها حسب id هي عملية فحص بطيئة للجدول.
إذا كنت تخطط لإجراء عمليات بحث حسب id، يمكنك تجنُّب تخزين الـ
rowid عمود للحصول على بيانات أقل في مساحة التخزين وقاعدة بيانات أسرع بشكلٍ عام:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
يبدو جدولك الآن على النحو التالي:
| id | الاسم | مدينة |
|---|---|---|
| 123 | مايكل جاكسون | غاري، إنديانا |
| 456 | John Lennon | Liverpool, England |
| 789 | Dolly Parton | Sevier County, TN |
بما أنّك لست بحاجة إلى تخزين العمود 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 |
| Liverpool, England | 1 |
| Sevier County, TN | 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 أو في ملف، ثم تخزين المسار في العمود.
يتم تقريب حجم الملفات بشكلٍ عام إلى مضاعفات 4 كيلوبايت. بالنسبة إلى الملفات الصغيرة جدًا، حيث يكون خطأ التقريب كبيرًا، من الأفضل تخزينها في قاعدة البيانات كـ 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 لعرض النتائج الفردية. تكون الحلقة البرمجية أبطأ بنحو 1000 مرة من طلب بحث 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: تربط السلاسل باستخدام فاصل اختياري.
استخدِم COUNT() بدلاً من Cursor.getCount()
في المثال التالي، تقرأ الدالة
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 واجهة سطر الأوامر sqlite3_analyzer لعرض
معلومات إضافية يمكن استخدامها لتحديد المشاكل وحلّها في الأداء. لتثبيت هذه الواجهة،
انتقِل إلى صفحة تنزيل SQLite.
يمكنك استخدام adb pull لتنزيل ملف قاعدة بيانات من جهاز الاختبار إلى محطة العمل لتحليله:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
متصفّح SQLite
يمكنك أيضًا تثبيت أداة واجهة المستخدم الرسومية SQLite Browser من صفحة تنزيلات SQLite .
تسجيل 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
عند إعداد 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: حجم صفحة واحدة من قاعدة البيانات بالكيلوبايت.dbsz: حجم قاعدة البيانات بالكامل بالصفحات. للحصول على الحجم بالكيلوبايت، اضربpgszفيdbsz.Lookaside(b): الذاكرة المخصّصة لمخزن SQLite المؤقت لكل اتصال بالبايت. عادةً ما تكون هذه الذاكرة صغيرة جدًا.cache hits: يحتفظ SQLite بذاكرة تخزين مؤقت لصفحات قاعدة البيانات. هذا هو عدد عمليات الوصول إلى ذاكرة التخزين المؤقت للصفحات (العدد).cache misses: عدد عمليات عدم الوصول إلى ذاكرة التخزين المؤقت للصفحات (العدد).cache size: عدد الصفحات في ذاكرة التخزين المؤقت (العدد). للحصول على الحجم بالكيلوبايت، اضرب هذا الرقم فيpgsz.Dbname: مسار ملف قاعدة البيانات. في مثالنا، تم إلحاق(1)أو رقم آخر بأسماء بعض قواعد البيانات للإشارة إلى وجود أكثر من اتصال بقاعدة البيانات الأساسية نفسها. يتم تتبُّع الإحصاءات لكل اتصال.
ضمن POOL STATS، ستجد ما يلي:
cache hits: يخزّن SQLite العبارات المُعدّة مؤقتًا ويحاول إعادة استخدامها عند تشغيل طلبات البحث، لتوفير بعض الجهد والذاكرة في تجميع عبارات SQL. هذا هو عدد عمليات الوصول إلى ذاكرة التخزين المؤقت للعبارات (العدد).cache misses: عدد عمليات عدم الوصول إلى ذاكرة التخزين المؤقت للعبارات (العدد).cache size: بدءًا من Android 17، يعرض هذا الخيار العدد الإجمالي للعبارات المُعدّة في ذاكرة التخزين المؤقت. في الإصدارات السابقة، تكون هذه القيمة مساوية لمجموع عمليات الوصول وعدم الوصول المدرَجة في العمودَين الآخرَين، ولا تمثّل حجم ذاكرة التخزين المؤقت.
مراجع إضافية
عرض المحتوى
مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعّل
- تشغيل الاختبارات المعيارية في عملية التكامل المستمر
- الإطارات المجمّدة
- إنشاء ملفات شخصية أساسية وقياسها بدون Macrobenchmark