اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'استعلامات'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 6 نتائج

  1. قاعدة البيانات هي بُنيةٌ هيكليةٌ تحتوي على مجموعة من البيانات المُخزَّنة إلكترونيًا؛ وكان مفهوم قواعد البيانات معروفًا لدى أسلافنا قبل وجود الحواسيب، لكن إنشاء وصيانة تلك القواعد كان أمرًا شاقًا ومملًا. فلنقل أنَّ لدينا قاعدة بيانات فيها 100 صفحة، وأردتَ أن تبحث عن جميع الموظفين الذين يتقاضون أقل من 50 ألف دولار سنويًا، فتخيّل مدى صعوبة الأمر ومقدار الوقت الذي سيستغرقه في ذاك الحين. أما حاليًا، فأنت تصادف قواعد البيانات في كل مكان، إذ تعمل ملايين قواعد البيانات في أنحاء العالم لتخزين والحصول على أي نوع من أنواع البيانات كالبيانات العسكرية وسجلات الموظفين أو حتى مواقع الويب وتقنياتها. تُصنّف قواعد البيانات عمومًا على أنها جزءٌ من «السند الخلفي» (back-end)، وذلك لعدم ظهورها للمستخدم النهائي ولعدم تعامل المستخدم معها مباشرةً. إذ تعمل قواعد البيانات مع لغةٍ برمجيةٍ مثل PHP أو VB أو ASP.NET وتطلب تلك اللغات من قواعد البيانات إجراء عملية ما. تتوافر عدِّة خواديم قواعد بيانات وعملاءٍ لها مثل Oracle و MySQL و MySQLi و MariaDB و MongoDB …إلخ. تتشابه طريقة التعامل مع هذه البيانات، والبنية اللغوية لأوامرها شبه متماثلة، واحتراف التعامل مع إحداها يعني أنَّك ستستطيع التحكم بأريحية مع البقية، وعملية تعلم كتابة الاستعلامات (queries) هي عملية سهلة وممتعة. لنبدأ ببعض الاستعلامات البسيطة على قواعد البيانات؛ وسنستخدم قواعد MySQL التي تأتي مع أغلبية توزيعات لينُكس افتراضيًا، وتستطيع بسهولة تثبيتها من مستودعات توزيعتك إن لم تكن مثبتةً لديك. لكن ربما ينتابك فضولٌ حول معنى «استعلام» (Query)، فبأبسط الكلمات: هي شيفرةٌ بسيطةٌ (أو أمر) تُرسَل إلى قواعد البيانات للحصول على النتيجة المطلوبة. تثبيت قواعد بيانات MySQL يمكنك استخدام مديرَي الحزم yum أو apt لتثبيت قواعد بيانات MySQL: # yum install mysql mysql-client mysql-server (on Yum based Systems) # apt-get install mysql mysql-client mysql-server (on Apt based Systems) تشغيل خادوم MySQL تستطيع تشغيل خدمة MySQL كالآتي: # service mysqld start أو: # service mysql start أذكِّرك أنَّ رمز # في بداية السطر يعني أنَّ عليك تنفيذ الأمر بامتيازات الجذر. سيُطلَب منك أثناء تثبيت قاعدة بيانات MySQL إعداد كلمة مرور لحساب المدير فيها… بعدئذٍ ستتمكن من الوصول إلى مِحَث (prompt، أي المكان الذي تكتب فيه الأوامر) MySQL بتنفيذك للأمر الآتي: # mysql -u root -p ضع اسم المستخدم الذي ضبطته بدلًا من root، وأدخِل كلمة مرورك عند طلبها، فإذا كانت معلومات الدخول صحيحةً، فيمكنك أن تشاهد مِحَث MySQL مباشرةً: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 195 Server version: 5.5.31-0+wheezy1 (Debian) Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. Mysql> تستطيع الآن البدء بتنفيذ الاستعلامات وتعلم إنشاء الاستعلامات. إنشاء قاعدة بيانات سنُنشِئ الآن قاعدة بيانات باسم tecmint كالتّالي: mysql> create database tecmint ; Query OK, 1 row affected (0.02 sec) mysql> ظهر عندنا أنَّ الاستعلام السابق قد نُفِّذَ تنفيذًا سليمًا، وهذا يعني أنَّ قاعدة البيانات قد أُنشِئَت. يمكنك التحقق من وجود قاعدة البيانات الجديدة بتنفيذ الاستعلام الآتي: mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | tecmint | | test | +--------------------+ 9 rows in set (0.00 sec) mysql> لاحظ وجود قاعدة البيانات في مخرجات الاستعلام أعلاه. اختيار قاعدة البيانات علينا قبل إجراء الاستعلامات على إحدى قواعد البيانات أن نختارها، وذلك باستخدام التعليمة use كما يلي: mysql> use tecmint; Database changed mysql> إنشاء الجداول في MySQL لنقل أننا نريد إنشاء جدول باسم minttec في قاعدة البيانات وفيه ثلاثة حقول: mysql> CREATE TABLE minttec ( -> id Int(3), -> first_name Varchar (15), -> email Varchar(20) -> ); Query OK, 0 rows affected (0.08 sec) mysql> لاحظ أنَّ الاستعلام السابق قد أعاد OK مما يعني أنَّ الجدول قد أُنشِئ دون أخطاء. نفِّذ الاستعلام الآتي للتحقق من إنشاء الجدول: mysql> show tables; +-------------------+ | Tables_in_tecmint | +-------------------+ | minttec | +-------------------+ 1 row in set (0.00 sec) mysql> جيد، كل شيء يسير كما ينبغي. يمكننا أن نُطالِع الأعمدة التي أنشأتها في جدول minttec كما يلي: mysql> show columns from minttec; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | id | int(3) | YES | | NULL | | | first_name | varchar(15) | YES | | NULL | | | email | varchar(20) | YES | | NULL | | +------------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> سأخبرك الآن عن أنواع البيانات الموجودة في الجدول السابق ومعانيها: - int: عدد صحيح. - varchar: سلسلة من المحارف التي لا يتجاوز طولها الرقم المُعرَّف. القيمة الرقمية التي تأتي بعد النوع هي «طول» الحقل الذي ستُخزَّن فيه البيانات. لنقل الآن أننا نريد إضافة عمود جديد باسم last_name بعد العمود first_name سنقوم بذلك على النّحو التّالي: mysql> ALTER TABLE minttec ADD last_name varchar (20) AFTER first_name; Query OK, 0 rows affected (0.16 sec) Records: 0 Duplicates: 0 Warnings: 0 ثم سنتحقق من أعمدة الجدول: mysql> show cسolumns from minttec; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | id | int(3) | YES | | NULL | | | first_name | varchar(15) | YES | | NULL | | | last_name | varchar(20) | YES | | NULL | | | email | varchar(20) | YES | | NULL | | +------------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> إضافة أعمدة في MySQL سنضيف الآن عمودًا باسم country بعد العمود email كالتّالي: mysql> ALTER TABLE minttec ADD country varchar (15) AFTER email; Query OK, 0 rows affected (0.16 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> لنتحقق من إضافة العمود: mysql> show columns from minttec; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | id | int(3) | YES | | NULL | | | first_name | varchar(15) | YES | | NULL | | | last_name | varchar(20) | YES | | NULL | | | email | varchar(20) | YES | | NULL | | | country | varchar(15) | YES | | NULL | | +------------+-------------+------+-----+---------+-------+ 5 rows in set (0.00 sec) mysql> إضافة قيمة إلى الحقول ماذا لو أردنا إضافة قيم إلى حقول الجدول؟ سنقوم بذلك كالتّالي: mysql> INSERT INTO minttec VALUES ('1' , 'Ravi' , 'Saive' , 'raivsaive@xyz.com' , 'India' ); Query OK, 1 row affected (0.02 sec) mysql> ماذا عن إضافة أكثر من قيمة واحدة في آنٍ واحد: mysql> INSERT INTO minttec VALUES ('2' , 'Narad' , 'Shrestha' , 'narad@xyz.com' , 'India' ), ('3' , 'user' , 'singh' , 'user@xyz.com' , 'Aus' ), ('4' , 'tecmint' , '[dot]com' , 'tecmint@gmail.com' , 'India' ); Query OK, 3 rows affected (0.05 sec) Records: 3 Duplicates: 0 Warnings: 0 سنتحقق من إدخال المعلومات السابقة في الجدول: mysql> select * from minttec; +------+------------+-----------+-------------------+---------+ | id | first_name | last_name | email | country | +------+------------+-----------+-------------------+---------+ | 1 | Ravi | Saive | raivsaive@xyz.com | India | | 2 | Narad | Shrestha | narad@xyz.com | India | | 3 | user | singh | user@xyz.com | Aus | | 4 | tecmint | [dot]com | tecmint@gmail.com | India | +------+------------+-----------+-------------------+---------+ 4 rows in set (0.00 sec) mysql> حذف قيم في حقول الجدول لنقل أنَّ المُدخَلة الثالثة في الناتج السابق لم تكن صحيحةً ونريد أن نحذفها: mysql> DELETE FROM minttec WHERE id = 3; Query OK, 1 row affected (0.02 sec) التحقق من تنفيذ الاستعلام السابق: mysql> select * from minttec; +------+------------+-----------+-------------------+---------+ | id | first_name | last_name | email | country | +------+------------+-----------+-------------------+---------+ | 1 | Ravi | Saive | raivsaive@xyz.com | India | | 2 | Narad | Shrestha | narad@xyz.com | India | | 4 | tecmint | [dot]com | tecmint@gmail.com | India | +------+------------+-----------+-------------------+---------+ 3 rows in set (0.00 sec) تحديث قيم في حقول الجدول لنفترض أننا نريد تعديل السجل ذي المُعرِّف 4 (أي id=4): mysql> UPDATE minttec SET id = 3 WHERE first_name = 'tecmint'; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> لاحظ أنَّ الاستعلام السابق ليس مثاليًا، فستُغيّر الحقل id إلى القيمة 4 لكل سجل يكون اسمه الأول مساويًا للقيمة tecmint. من الجيد استخدام مُطابقة أكثر من عمود واحد في عبارة WHERE لتقليل نسبة حدوث خطأ، كما في الاستعلام الآتي: mysql> UPDATE minttec SET id = 6 WHERE first_name = 'tecmint'AND last_name = '[dot]com'; Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> حذف أحد أعمدة الجدول لنفترض أنَّك تحتاج إلى حذف أحد أعمدة الجدول غير الضرورية، ولنقل أنَّه العمود country: mysql> ALTER TABLE minttec drop country; Query OK, 3 rows affected (0.15 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> عرض بُنية الجدول: mysql> select * from minttec; +------+------------+-----------+-------------------+ | id | first_name | last_name | email | +------+------------+-----------+-------------------+ | 1 | Ravi | Saive | raivsaive@xyz.com | | 2 | Narad | Shrestha | narad@xyz.com | | 6 | tecmint | [dot]com | tecmint@gmail.com | +------+------------+-----------+-------------------+ 3 rows in set (0.00 sec) mysql> إعادة تسمية الجداول في MySQL ربما لا تجد اسم minttec ذا معنى، وتريد تحويله مثلًا إلى tecmint_table سنقوم بتغييره كالتّالي: mysql> RENAME TABLE minttec TO tecmint_table; Query OK, 0 rows affected (0.03 sec) mysql> عرض جميع الجداول لرؤية جميع الجداول الموجودة في قاعدة البيانات الحالية: mysql> show tables; +-------------------+ | Tables_in_tecmint | +-------------------+ | tecmint_table | +-------------------+ 1 row in set (0.00 sec) mysql> لاحظ إعادة تسمية الجدول. لنأخذ الآن نسخةً احتياطيةً من قاعدة البيانات السابقة، وذلك عبر تنفيذ استعلامٍ من سطرٍ وحيد دون استخدام أيّة أدواتٍ معقدة. نفِّذ الشيفرة أدناه في سطر الأوامر وليس في مِحَث MySQL: # mysqldump -u root -p tecmint > tecmint.sql check the dumped file on your desktop which would have contents something like -- MySQL dump 10.13 Distrib 5.5.31, for debian-linux-gnu (i686) -- -- Server version 5.5.31-0+wheezy1 -- Dump completed on 2013-09-02 12:55:37 من المستحسن الإبقاء على نسخ احتياطية لقواعد بياناتك، فاستعادة قاعدة بيانات MySQL هو أمرٌ بسيطٌ ويجرى بتنفيذ أمرٍ قصيرٍ في سطر الأوامر (أكرِّر، في سطر الأوامر وليس في مِحَث MySQL). لكن ما رأيك أن نحذف قاعدة البيانات أولًا لنرى كيف ستتم عملية الاستعادة؟ حذف قاعدة بيانات mysql> drop database tecmint; Query OK, 1 row affected (0.02 sec) تحقق من وجود قاعدة بيانات باسم tecmint في خادوم قواعد البيانات عندك: mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | my_database | | mysql | | performance_schema | | phpmyadmin | | sisso | | test | +--------------------+ 7 rows in set (0.00 sec) mysql> رائع، لقد فقدنا قاعدة البيانات :-) لكن لا تقلق، فلقد أخذنا نسخةً احتياطيةً منها منذ قليل. استعادة قاعدة بيانات من نسخةٍ احتياطيةٍ منها نفِّذ الأمر الآتي لاستعادة قاعدة البيانات: # mysql -u root -p tecmint < tecmint.sql Enter password: ERROR 1049 (42000): Unknown database 'tecmint' ماذا حدث؟ لقد واجهنا رسالة خطأ، فنحن لم نُنشِئ قاعدة بيانات باسم tecmint بعد. لذا اذهب إلى مِحَث MySQL وأنشِئها: mysql> create database tecmint; Query OK, 1 row affected (0.00 sec) mysql> حان الآن الوقت لتنفيذ أمر الاستعادة في سطر الأوامر: # mysql -u root -p tecmint < tecmint.sql Enter password: لم تظهر بوجهنا أيّة رسائل خطأ مرعبة، لنتحقق الآن من وجود القاعدة في خادومنا: mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | tecmint | | test | +--------------------+ 8 rows in set (0.00 sec) ثم سنتحقق من محتويات قاعدة البيانات (أي جداولها): mysql> show tables from tecmint; +-------------------+ | Tables_in_tecmint | +-------------------+ | tecmint_table | +-------------------+ 1 row in set (0.00 sec) mysql> وليطمئن قلبنا، سنرى محتويات الجدول: mysql> select * from tecmint_table; +------+------------+-----------+-------------------+ | id | first_name | last_name | email | +------+------------+-----------+-------------------+ | 1 | Ravi | Saive | raivsaive@xyz.com | | 2 | Narad | Shrestha | narad@xyz.com | | 6 | tecmint | [dot]com | tecmint@gmail.com | +------+------------+-----------+-------------------+ 3 rows in set (0.00 sec) هذه ليست نهاية المطاف، ما زال أمامنا مناقشة بعض المفاهيم مثل primary key، و foreign key… ترجمة -وبتصرّف- للمقال MySQL Basic Database Administration Commands – Part I.
  2. إن كان قد مضى على استخدامك لووردبريس فترة من الزمن فمن الوارد أن الوقت قد حان لتقوم بعملية تنظيف شاملة لقاعدة بيانات موقعك التي يمكن أن تكون مليئة ببعض الجداول (tables) غير الضرورية والمتسببة في بطئه وتشنجه. يعمل تنظيف قواعد بيانات موقعك بوتيرة منتظمة على خفض حجمها ما يعني صغر حجم ملفات النسخة الاحتياطية وبالتالي سرعة إنشائها واسترجاعها على حد سواء. سنتطرق في موضوع اليوم إلى مجموعة من الطرق التي يمكن اتباعها للحفاظ على قاعدة بيانات نظيفة سواء من خلال استعلامات SQL بسيطة أو من خلال بعض الملحقات، كما سنعمل على الإشارة إلى بعض النصائح سواء بالنسبة لمواقع ووردبريس المنفردة أو شبكات المواقع. أساسيات تنظيف قواعد البيانات قبل أن تنهمك في عملية التنظيف عليك بالتأكد من إنشاء نسخة احتياطية لكل من قاعدة بيانات وملفات موقعك لتكون على أتم استعداد لاسترجاعه بسرعة في حال حدوث مشكل ما غير متوقع. حذف الملحقات غير المستخدمة يعتبر حذف الملحقات غير المستخدمة من بين أبسط الطرق وأكثرها نجاعة في التخفيف من الفوضى التي تعرفها قاعدة بيانات موقعك، خصوصا إن كنت من محبي تجربة الملحقات كما هو الحال بالنسبة لي، فقد يحدث في بعض الأحيان أن تغفل عن حذف الملحقات التي لن تستخدمها أو التي لم تنل إعجابك بعد تجربتها. إن كنت مثلي تملك العديد من الملحقات الغير المنشطة على صفحة الملحقات الخاصة بك عليك أن تأخذ حذفها بعين الاعتبار لا أعتقد أني الوحيد الذي يقوم بهذا الأمر. عدا ذلك، قد يحدث في بعض الأحيان الأخرى أن تقوم بتنصيب ملحق ما ناسيا أنك تمتلك ملحقا مشابها يقوم بنفس الدور. بغض النظر عن أي هذه الوضعيات تواجه، يعتبر فحص قائمة الملحقات المنصبة على موقعك بشكل دوري لتحدد تلك التي تحتاجها من تلك التي يمكن التخلي عنها أمرا مفيدا للغاية. تأخذ الملحقات قسطا مهما من حجم قاعدة بيانات موقعك، لذى فإن حذف تلك غير الضرورية منها يعتبر خطوة أساسية نحو تنظيفها. كيفية تنفيذ تعليمات SQL على قاعدة بيانات موقعك حتى تقوم بالعديد من الخيارات التي سنتطرق لها، سيكون عليك أن تلج إلى قاعدة بيانات موقعك. إن لم تكن تذكر اسم المستخدم وكلمة المرور الخاصتين بك يمكنك أن تجدهما محفوظتين في ملف wp-config.php. يبدو الكود الذي يجب عليك أن تبحث عنه كالتالي: // ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ define('DB_NAME', 'your_db'); /** MySQL database username */ define('DB_USER', 'yourusername'); /** MySQL database password */ define('DB_PASSWORD', 'this-is-your-password'); /** MySQL hostname */ define('DB_HOST', 'localhost'); /** Database Charset to use in creating database tables. */ define('DB_CHARSET', 'utf8'); عوض yourusername الواردة في هذا المثال ستجد كلمة المستخدم الخاصة بقاعدة بيانات موقعك وعوض this-is-your-password ستجد كلمة السر. بمجرد ولوجك إلى لوحة التحكم phpMyAdmin اضغط على قاعدة البيانات التي تود تنظيفها ثم اضغط على تبويب SQL. يمكنك أن تكون مبدعا بعض الشيء وتقوم بإنشاء استعلاماتك الخاصة من خلال الضغط على تبويب Query وملء الخانات المطلوبة يمكنك على هذه الصفحة أن تدخل الاستعلامات التي تريد في الأسفل ثم تقوم بالضغط على زر Go لتشغيلها. إن ظهرت لك رسالة مفادها أنه لم يتم التأثير على أي جدول فهنيئا لك، فجداول قاعدة بيانات موقعك كانت في أحسن حالة من تلك الناحية بالتحديد. بعد تطرقنا لهذه التحضيرات الأولية لنمر الآن إلى تنفيذ بعض الاستعلامات. حذف مخلفات الملحقات ومعطيات المنشورات بعد حذف الملحقات التي لا تستخدمها عليك بالتفكير أيضا في حذف البقايا التي تتركها هذه الملحقات، تتواجد هذه العوالق في نفس مكان تواجد معطيات منشوراتك، أي في جدول wp_postmeta. يسمح لك تنفيذ الاستعلام التالي بالتخلص من بعض الفوضى في قاعدة بياناتك، فقط تذكر وضع القيمة التي تود حذفها مكان your-meta-key. DELETE FROM wp_postmeta WHERE meta_key = 'your-meta-key'; إن كنت تستخدم ووردبريس متعدد المواقع عليك بتنفيذ الاستعلام التالي: DELETE from wp_#_postmeta WHERE meta_key = 'your-meta-key'; عليك أن تغير في هذا الاستعلام # برقم تعريف (ID) موقعك الذي تود تنظيفه، وكما في الاستعلام السابق، يجب أن تعمل على تعويض your-meta-key بالقيمة التي تود حذفها. حذف كل تعليقات السبام المزعجة عرف ووردبريس تواجد تعليقات السبام المزعجة منذ بداياته ولا يبدوا أن لها زوالا في الأمد المنظور، إن كنت تجد كما كبيرا من هذه التعليقات في موقعك، عليك باستخدام هذا الاستعلام الذي سيعمل على حذفها كاملة. DELETE FROM wp_comments WHERE comment_approved = 'spam'; أما بالنسبة لشبكة مواقع ووردبريس، عليك بتنفيذ الاستعلام التالي: DELETE FROM wp_#_comments WHERE comment_approved = 'spam'; قم بتغيير # برقم الموقع الذي تود العمل عليه. تخلص من كل التعليقات التي تنتظر معاينتك لتصنيفها إن كنت تتوفر على الكثير من التعليقات التي لم تسنح لك الفرصة بمعاينتها، يمكنك استخدام الاستعلام في هذه الفقرة للتخلص منها كلها دفعة واحدة. لا تنس تصفح التعليقات والموافقة على نشر تلك الأصلية منها حتى لا يتم حذفها أيضا بعد استخدام هذا الاستعلام. DELETE FROM wp_comments WHERE comment_approved = '0'; بالنسبة لووردبريس متعدد المواقع عليك بتنفيذ الاستعلام التالي: DELETE FROM wp_#_comments WHERE comment_approved = '0'; لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود تنظيفه. الوسوم المنسية وغير المستخدمة إن كنت تمتلك العديد من الوسوم غير المستخدمة لأنك غيرت رأيك حولها أو كنت تقوم بالتعديل على الوسوم بضع مرات كل منشور، عليك بتنفيذ الاستعلام التالي الذي سيعمل على حذف كل الوسوم غير المرتبطة بأي منشور. DELETE FROM wp_terms wt INNER JOIN wp_term_taxonomy wtt ON wt.term_id = wtt.term_id WHERE wtt.taxonomy = 'post_tag' AND wtt.count = 0; أما بالنسبة لشبكة مواقع ووردبريس، عليك بتنفيذ الاستعلام التالي: DELETE FROM wp_#_terms wt INNER JOIN wp_#_term_taxonomy wtt ON wt.term_id = wtt.term_id WHERE wtt.taxonomy = 'post_tag' AND wtt.count = 0; قم بتغيير # برقم الموقع الذي تود العمل عليه. قل وداعا للتنبيهات الداخلة (Pingbacks) إن كنت قد قمت بتعطيل خاصية السماح بالتنبيهات الداخلة (pingbacks) فمن الجيد أن تفكر في حذفها كلها من قاعدة بيانات موقعك، عليك بتنفيذ الاستعلام التالي: DELETE FROM wp_comments WHERE comment_type = 'pingback'; إن كنت تستخدم شبكة مواقع ووردبريس وتريد حذف التنبيهات الداخلة في موقع آخر غير الموقع الرئيسي استخدم الاستعلام التالي: DELETE FROM wp_#_comments WHERE comment_type = 'pingback'; لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود تنظيفه. تخلص من روابط التتبع (Trackbacks) كما قمنا في الفقرة السابقة بحذف التنبيهات الداخلة لنتطرق الآن لكيفية حذف روابط التتبع، عليك بتنفيذ الاستعلام التالي بعد التأكد من تعطيل كل من التنبيهات الداخلة وروابط التتبع. DELETE FROM wp_comments WHERE comment_type = 'trackback'; أما بالنسبة لشبكة مواقع ووردبريس، عليك بتنفيذ الاستعلام التالي، لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود تنظيفه: DELETE FROM wp_#_comments WHERE comment_type = 'trackback'; قل وداعا لمراجعات المنشورات يمكن لمراجعات المنشورات أن تتراكم بسرعة كبيرة خصوصا في المواقع القديمة والتي تتوفر على الكثير من الصفحات والمنشورات، يمكنك استخدام الاستعلام التالي للعمل على حذفها كلها. يرجع الفضل في هذا الكود الذي يسمح بحذف كل المراجعات دون ضياع غير مقصود لأي بيانات للمبرمج Joseph Michael Ambrosio حيث أورده في مقاله: تنظيف مراجعات ووردبريس باستخدام حذف الجداول المتعددة على MySQL DELETE a,b,c FROM wp_posts a LEFT JOIN wp_term_relationships b ON ( a.ID = b.object_id) LEFT JOIN wp_postmeta c ON ( a.ID = c.post_id ) LEFT JOIN wp_term_taxonomy d ON ( b.term_taxonomy_id = d.term_taxonomy_id) WHERE a.post_type = 'revision' AND d.taxonomy != 'link_category'; أما بالنسبة لشبكة مواقع ووردبريس يمكنك استخدام الكود التالي لحذف المراجعات على أحد المواقع غير الموقع الرئيسي: DELETE a,b,c FROM wp_#_posts a LEFT JOIN wp_#_term_relationships b ON ( a.ID = b.object_id) LEFT JOIN wp_#_postmeta c ON ( a.ID = c.post_id ) LEFT JOIN wp_#_term_taxonomy d ON ( b.term_taxonomy_id = d.term_taxonomy_id) WHERE a.post_type = 'revision' AND d.taxonomy != 'link_category'; لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود تنظيفه. التخلص من كل الشيفرات المختصرة (Shortcodes) إن سبق لك تنصيب العديد من الملحقات بغرض تجريبها دون القيام بحذفها فمن الوارد أن يتسبب ذلك في تواجد العديد من الشيفرات المختصرة التي لم يعد لها أي دور يذكر، نفس الأمر يحدث عند التوقف عن استخدام أحد القوالب التي تحتوي على بعض هذه الشيفرات. عليك بتنفيذ الاستعلام التالي بغرض حذفها كلها: UPDATE wp_post SET post_content = replace(post_content, '[your-shortcode]', '' ) ; أما بالنسبة لشبكة مواقع ووردبريس، عليك بتنفيذ الاستعلام التالي، لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود حذف الشيفرات المختصرة منه: UPDATE wp_#_post SET post_content = replace(post_content, '[your-shortcode]', '' ); احذف كل مواضيعك القديمة قد توجد لديك العديد من المواضيع التي تحبذ أن يتم حذفها من موقعك خصوصا إن كنت تملكه منذ عدة سنوات. يمكنك القيام بذلك بكل بساطة باستخدام هذا الاستعلام. عليك بتغيير #-of-days بعدد الأيام التي تريد أن تبدأ عملية الحذف انطلاقا منها، إن أردت على سبيل المثال أن يتم حذف كل المنشورات الأقدم من 5 سنوات عليك بإدخال العدد: 1825. DELETE FROM `wp_posts` WHERE `post_type` = 'post' AND DATEDIFF(NOW(), `post_date`) > #-of-days أما بالنسبة لشبكة مواقع ووردبريس، عليك بتنفيذ الاستعلام التالي: DELETE FROM `wp_#_posts` WHERE `post_type` = 'post' AND DATEDIFF(NOW(), `post_date`) > #-of-days لا تنس تغيير #-of-days بعدد الأيام كما في الكود السابق و# بالرقم التعريفي (ID) للموقع الذي تود تنظيفه. حذف كل بقايا التعليقات قد يحدث أن يمتلئ جدول wp_commentsmeta بالتعليقات غير المخزنة في جدول wp_comments. من المحتمل ألا يناسب هذا الاستعلام كل المستخدمين إلا أنه قد يساعد كثيرا في تنظيف قاعدة البيانات خصوصا إن قمت بحذف أحد الملحقات التي تستخدم جدول wp_commentsmeta. SELECT * FROM wp_commentmeta WHERE comment_id NOT IN ( SELECT comment_id FROM wp_comments ); إن أردت أن تقوم بنفس العملية بالنسبة لأحد مواقع شبكتك عليك بتنفيذ الاستعلام التالي: SELECT * FROM wp_#_commentmeta WHERE comment_id NOT IN ( SELECT comment_id FROM wp_#_comments ); لا تنس تغيير # بالرقم التعريفي (ID) للموقع الذي تود تنظيفه. تحسين جداول قاعدة البيانات يمكنك تحسين جداولك في بضع ضغطات زر دون الحاجة إلى استخدام ملحق ما. بعد الولوج إلى phpMyAdmin، اضغط على قاعدة البيانات الخاصة بك ثم اذهب إلى أسفل الصفحة التي تم تحميلها في اليمين. اضغط على Check All checkbox في الأسفل ثم حدد Optimize table من القائمة المنسدلة. إن كنت تملك عدة صفحات جداول سيكون عليك أن تكرر هذه العملية لها كلها. يكفي أن تحدد الجداول كبيرة الحجم التي تجدها في خانة Size، لا داع لتحديد كل الجداول ستبدأ عملية التحسين بشكل أوتوماتيكي دون الحاجة إلى ضغط زر Go، لا تقلق إن استغرق الأمر بعض الوقت خصوصا إن كان موقعك كبيرا فهذا أمر عادي. عند الانتهاء، ستظهر رسالة تفيد بنجاح العملية. على الصفحة التي يتم تحميلها، من المحتمل أن تظهر رسالة تفيد بنجاح العملية مشيرة إلى أن الجداول لا تقبل عملية التحسين وأنه قد تم إعادة إنشائها، لا عليك فلا ضرر في ذلك. بعد أن تطرقنا لبعض النصائح حول كيفية القيام بتنظيف قاعدة البيانات بشكل يدوي، لنلق الآن نظرة على بعض الملحقات التي من شأنها أن تجعل هذه العملية أكثر بساطة. ملحقات تحسين قاعدة البيانات تتميز هذه الملحقات بتحديثها بشكل مستمر فضلا عن توافقها مع مواقع ووردبريس المنفردة (single installations of WordPress). بعد تجريبها على استضافة ووردبريس متعددة المواقع (Multisite)، لاحظت أن أدائها لم يضعف بل قامت بالعمل على أتم وجه. WP-Optimize يعتبر WP-Optimize من بين أفضل ملحقات تحسين قواعد البيانات المتواجدة على الساحة منذ فترة من الزمن، كما أنه يتوفر على العديد من الإعدادات والخيارات لحذف الجداول غير الضرورية فضلا عن إمكانية برمجة عمليات التحسين بشكل دوري للمساعدة على الحفاظ على موقعك في أحسن حالة. يتميز الملحق أيضا بسهولة التنصيب والإعداد. إن كنت مهتما بهذا الملحق عليك بزيارة الرابط التالي: WP-Optimize Revision Control يمكنك من خلال هذا الملحق أن تحد من عدد المراجعات التي يتم تخزينها في قاعدة البيانات عوض حذفها بشكل كلي كما هو الشأن بالنسبة لاستخدام استعلام SQL الذي أسلفنا الذكر، يتميز Revision Control أيضا بسهولة التنصيب والاستخدام. إن كنت مهتما بهذا الملحق عليك بزيارة الرابط التالي: Revision Control خاتمة يمكنك الآن أن تقوم بتنظيف قاعدة بياناتك سواء بالنسبة لموقع ووردبريس الخاص بك أو شبكة مواقع ووردبريس. ما هي أفضل الاستراتيجيات التي تستخدمها للحفاظ على قاعدة بيانات نظيفة ومنظمة؟ هل أغفلت بعض النقاط؟ شاركنا رأيك في التعليقات أسفله. ترجمة بتصرف للمقال: 10 Tips for Keeping a Squeaky Clean WordPress (and Multisite!) Database لكاتبه: Jenni McKinnon
  3. MemSQL عبارة عن قاعدة بيانات تعمل مُباشرة من الذاكرة in-memory تستطيع أن تُخدِّم القراءة والكتابة بشكل أسرع من قواعد البيانات التقليديّة، وبالرغم من أنّها تقنية حديثة إلّا أنّها تتعامل بميفاق protocol قاعدة بيانات MySQL لذلك يكون التعامل معها مألوفًا. تضم MemSQL أحدث قدرات MySQL مع ميّزات حديثة مثل دعم JSON والقدرة على عمل upsert للبيانات (تعني كلمة upsert إدراج صف جديد إن لم يكن موجودًا أي insert، أو تحديث هذا الصف إن كان موجودًا أي update). إنّ أحد أهم نقاط تفوّق MemSQL على MySQL هي قدرتها على فصل الاستعلام query عبر عدّة عقد nodes، والمعروفة أيضًا باسم المعالجة المتوازية على نطاق واسع massively parallel processing، ممّا يؤدي إلى سرعة أكبر في قراءة الاستعلامات. سنقوم في هذا الدّرس بتثبيت MemSQL على خادوم Ubuntu، تنفيذ تجارب قياس الأداء benchmarks، والتعامل مع إدراج بيانات JSON عبر عميل سطر الأوامر في MySQL. المتطلبات الأساسيةسنحتاج لمتابعة هذا الدّرس إلى: Ubuntu 14.04 x64 Droplet مع ذاكرة RAM 8 غيغابايت على الأقل (أو خادوم محلّي بنفس المواصفات)مستخدم غير جذري non-root مع صلاحيّات sudo، والذي يُمكِن إعداده عن طريق درس الإعداد الأولي للخادوم مع Ubuntu.الخطوة الأولى – تثبيت MemSQLسنقوم في هذا القسم بتحضير بيئة العمل من أجل تثبيت MemSQL. يتم عرض آخر إصدار من MemSQL على صفحة تحميله، سنقوم بتحميل وتثبيت MemSQL Ops، والذي هو برنامج يدير تحميل وتحضير خادومنا لتشغيل MemSQL بشكل صحيح، الإصدار الأحدث من MemSQL Ops لدى كتابة هذا الدّرس هو 4.0.35. نقوم في البداية بتنزيل ملف حزمة تثبيت MemSQL من موقعها على الإنترنت: wget http://download.memsql.com/memsql-ops-4.0.35/memsql-ops-4.0.35.tar.gzنستخرج extract بعدها الحزمة: tar -xzf memsql-ops-4.0.35.tar.gzأنشأ استخراج الحزمة مجلّدًا يُدعى memsql-ops-4.0.35، نلاحظ أنّ اسم المجلّد يحتوي على رقم الإصدار، لذلك إن قمت بتنزيل إصدار أحدث من الإصدار المحدّد في هذا الدّرس فستملك مجلّدًا مع رقم الإصدار الذي نزّلته. نغير الدليل إلى هذا المجلّد: cd memsql-ops-4.0.35ثم نقوم بتنفيذ script التثبيت والذي هو جزء من حزمة التثبيت التي استخرجناها للتو: sudo ./install.shسنرى بعض الخَرْج output من الـ script، وسيسألنا بعد لحظات إذا ما كنّا نرغب بتثبيت MemSQL على هذا المُضيف host فقط، سنتعلّم تثبيت MemSQL على عدّة أجهزة في درس لاحق، لذا من أجل غرض هذا الدّرس فلنقل نعم yes عن طريق إدخال الحرف y: Installation script prompt and output . . . Do you want to install MemSQL on this host only? [y/N] y 2015-09-04 14:30:38: Jd0af3b [INFO] Deploying MemSQL to 45.55.146.81:3306 2015-09-04 14:30:38: J4e047f [INFO] Deploying MemSQL to 45.55.146.81:3307 2015-09-04 14:30:48: J4e047f [INFO] Downloading MemSQL: 100.00% 2015-09-04 14:30:48: J4e047f [INFO] Installing MemSQL 2015-09-04 14:30:49: Jd0af3b [INFO] Downloading MemSQL: 100.00% 2015-09-04 14:30:49: Jd0af3b [INFO] Installing MemSQL 2015-09-04 14:31:01: J4e047f [INFO] Finishing MemSQL Install 2015-09-04 14:31:03: Jd0af3b [INFO] Finishing MemSQL Install Waiting for MemSQL to start...نمتلك الآن مجموعة MemSQL مُثبَّتة على خادوم أوبونتو لدينا، ولكن نلاحظ من السجلّات السّابقة أنّه تم تثبيت MemSQL مرتين. تستطيع MemSQL أن تعمل بدورين مختلفين: عقدة مُجمِّع aggregator node وعقدة ورقة leaf node، إنّ سبب تثبيت MemSQL مرتين هو أنّها تحتاج على الأقل إلى عقدة مُجمِّع واحدة وعقدة ورقة واحدة لكي يعمل العنقود cluster. إنّ عقدة المُجمِّع aggregator node هي واجهتنا إلى MemSQL، وهي تبدو بالنسبة للعالم الخارجي مشابهة كثيرًا لـ MySQL، فهي تستمع إلى نفس المنفذ port، وبإمكاننا أن نربط إليها أدوات تتوقّع أن تتعامل مع MySQL ومكتبات MySQL المعياريّة، وظيفة المُجمِّع هي أن يعرف عن كافّة عُقَد الورقة leaf nodes لـ MemSQL، يتعامل مع عُملاء MySQL، ويترجم استعلاماتهم إلى MemSQL. تُخزِّن عقدة الورقة leaf node البيانات فعليًّا، فعندما تستقبل عقدة الورقة طلبًا من عقدة المُجمِّع لقراءة أو كتابة البيانات تقوم بتنفيذ هذا الاستعلام وتُعيد النتائج إلى عقدة المُجمِّع، تسمح MemSQL لنا بمشاركة بياناتنا عبر عدّة مضيفين، وتمتلك كل عقدة ورقة قسمًا من تلك البيانات (حتى مع وجود عقدة ورقة واحدة تكون البيانات تكون البيانات مُقسَّمة ضمن تلك العقدة). عندما نملك عدة عقد ورقة يكون المُجمِّع مسؤولًا عن ترجمة استعلامات MySQL لجميع عقد الورقة التي ينبغي أن تشارك في هذا الاستعلام، ومن ثمّ يتلقى الردود من جميع عقد الورقة ويُجمِّع النتيجة في استعلام واحد يعود إلى عميل MySQL لدينا، وهكذا تتم إدارة الاستعلامات المتوازية. يمتلك إعداد المضيف الوحيد لدينا عقدة مُجمِّع وورقة تعملان على نفس الجهاز، ولكن نستطيع إضافة المزيد من عقد الورقة عبر العديد من الأجهزة الأخرى. الخطوة الثانية – تنفيذ تجربة قياس أداءفلنرى مدى السّرعة التي تستطيع أن تعمل بها MemSQL باستخدام الأداة MemSQL Ops والتي تم تثبيتها كجزء من script تثبيت MemSQL. نذهب إلى http://you_server_ip:9000 في متصفحنا: تعطينا الأداة MemSQL Ops لمحة عامّة عن العنقود cluster لدينا، نمتلك عقدتي MemSQL: المُجمِّع الرئيسي وعقدة الورقة. فلنقم بإجراء اختبار السّرعة Speed Test على عقدة MemSQL في جهازنا المفرد، نضغط على Speed Test من القائمة الموجودة على اليسار، وبعدها نضغط على START TEST، وهذا مثال عن النتائج التي قد نراها: لن نشرح في هذا الدّرس كيفيّة تثبيت MemSQL على خواديم متعددة، ولكن من أجل المقارنة أدرجنا هنا قياس أداء من عنقود MemSQL على نظام Ubuntu يملك 8 جيجابايت من الذّاكرة Ram وعدّة عقد (عقدة مُجمِّع وعقدتا leaf): نكون قادرين بزيادة عدد عقد leaf أن نُضاعِف مُعدَّل الإدراج insert، وبالنظر إلى أقسام قراءة الصفوف Rows Read نستطيع أن نرى أنّ عنقود العقد الثلاثة لدينا قادر على قراءة الصفوف بشكل متزامن أكثر بمقدار 12 مليون صف من عنقود العقدة الواحدة وفي نفس المدة الزمنية. الخطوة الثالثة – التعامل مع MemSQL من خلال mysql-clientتبدو MemSQL بالنسبة للعملاء مثل MySQL، فكلاهما تتعامل بنفس الميفاق protocol، ولبدء التخاطب مع عنقود MemSQL لدينا نقوم بتثبيت mysql-client. في البداية نقوم بتحديث apt لكي نُثبِّت الإصدار الأخير من العميل client في الخطوة التالية: sudo apt-get updateنُثبِّت الآن عميل MySQL والذي سيُمكّننا من تنفيذ الأمر mysql: sudo apt-get install mysql-client-core-5.6نحن الآن على استعداد للاتصال بـ MemSQL باستخدام عميل MySQL، سنتصل باستخدام المستخدم الجذري root إلى المضيف 127.0.0.1 (والذي هو عنوان IP للمضيف المحلّي localhost لدينا) على المنفذ 3306، سنقوم بتخصيص رسالة المُحث prompt لتكون <memsql: mysql -u root -h 127.0.0.1 -P 3306 --prompt="memsql> "سنشاهد بعض أسطر الخَرْج متبوعة بالمُحث <memsql. فلنقم بسرد list قواعد البيانات: memsql> show databases; سنشاهد هذا الخَرْج: +--------------------+ | Database | +--------------------+ | information_schema | | memsql | | sharding | +--------------------+ 3 rows in set (0.01 sec)نُنشِئ قاعدة بيانات جديدة تُدعى tutorial: memsql> create database tutorial;ثم نتحوّل لاستخدام قاعدة البيانات الجديدة باستخدام الأمر use: memsql> use tutorial;نقوم بعدها بإنشاء جدول users والذي يملك الحقلين id و email، يجب علينا تحديد نوع لكلا هذين الحقلين، فلنجعل id من نوع bigint و email من نوع varchar بطول 255، سنخبر أيضًا قاعدة البيانات أنّ الحقل id هو مفتاح أساسي primary key والحقل email لا يُمكن أن يكون عَدم Null. memsql> create table users (id bigint auto_increment primary key, email varchar(255) not null);ربّما تلاحظ زمن تنفيذ بطيء للأمر الأخير (15-20 ثانية)، هناك سبب رئيسي يجعل من MemSQL بطيئة في إنشاء هذا الجدول الجديد وهو: توليد الشيفرة code generation. تستخدم MemSQL توليد الشيفرة لتنفيذ الاستعلامات، وهذا يعني أنّه في كل مرة تتم فيها مصادفة نوع جديد من الاستعلامات تحتاج MemSQL توليد وتصريف compile الشيفرة التي تُمثِّل الاستعلام، يتم بعدها نقل الشيفرة إلى العنقود لتنفيذها، وهذا يُسرِّع عمليّة معالجة البيانات الفعليّة ولكن هناك تكلفة من أجل التحضير، تبذل MemSQL جهدها لإعادة استخدام الاستعلامات المُولَّدة مُسبقًا pre-generated queries، ولكن تبقى الاستعلامات الجديدة ذات البُنية التي لم تشاهدها MemSQL من قبل بطيئة. وبالعودة إلى جدول users نقوم بإلقاء نظرة على تعريفه: memsql> describe users;+-------+--------------+------+------+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+------+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | email | varchar(255) | NO | | NULL | | +-------+--------------+------+------+---------+----------------+ 2 rows in set (0.00 sec)فلنقم الآن بإدراج بعض أمثلة عناوين البريد الإلكتروني في الجدول users، وهي نفس الصياغة syntax التي نستخدمها لقاعدة بيانات MySQL: memsql> insert into users (email) values ('one@example.com'), ('two@example.com'), ('three@example.com');Inserting emails output Query OK, 3 rows affected (1.57 sec) Records: 3 Duplicates: 0 Warnings: 0نستعلم الآن عن الجدول users: memsql> select * from users;بإمكاننا رؤية البيانات التي أدخلناها للتو: +----+-------------------+ | id | email | +----+-------------------+ | 2 | two@example.com | | 1 | one@example.com | | 3 | three@example.com | +----+-------------------+ 3 rows in set (0.07 sec) الخطوة الرابعة – إدراج واستعلام JSONتوفِّر MemSQL نوع JSON، لذا سنقوم في هذه الخطوة بإنشاء جدول أحداث events لاستخدام الأحداث الواردة، يمتلك هذا الجدول حقل id (كما فعلنا مع جدول users) وحقل event والذي سيكون من النوع JSON: memsql> create table events (id bigint auto_increment primary key, event json not null);فلنقم بإدراج حدثين، سنقوم في JSON بإرجاع حقل email والذي بدوره يقوم بإعادة الإرجاع إلى مُعرِّفات IDs المستخدمين الذين أدخلناهم في الخطوة الثالثة: memsql> insert into events (event) values ('{"name": "sent email", "email": "one@example.com"}'), ('{"name": "received email", "email": "two@example.com"}');نقوم الآن بإلقاء نظرة على الأحداث events التي أدرجناها للتو: memsql> select * from events;+----+-----------------------------------------------------+ | id | event | +----+-----------------------------------------------------+ | 2 | {"email":"two@example.com","name":"received email"} | | 1 | {"email":"one@example.com","name":"sent email"} | +----+-----------------------------------------------------+ 2 rows in set (3.46 sec)نستطيع بعدها الاستعلام عن كل الأحداث events التي تكون خاصّية JSON لها التي تُدعى name هي النّص “received email”: memsql> select * from events where event::$name = 'received email';+----+-----------------------------------------------------+ | id | event | +----+-----------------------------------------------------+ | 2 | {"email":"two@example.com","name":"received email"} | +----+-----------------------------------------------------+ 1 row in set (5.84 sec)نحاول تغيير هذا الاستعلام لإيجاد تلك الأحداث التي خاصيّة name لها هي النّص “sent email”: memsql> select * from events where event::$name = 'sent email';+----+-------------------------------------------------+ | id | event | +----+-------------------------------------------------+ | 1 | {"email":"one@example.com","name":"sent email"} | +----+-------------------------------------------------+ 1 row in set (0.00 sec)تم تنفيذ هذا الاستعلام الأخير بشكل أسرع من الاستعلام الذي سبقه لأنّنا غيّرنا فقط مُعامِل في الاستعلام، لذا كانت MemSQL قادرة على تخطّي توليد الشيفرة. فلنقم بعمل شيء أكثر تقدّمًا بالنسبة لقاعدة بيانات SQL مُوزَّعة، وهو ضم جدولين على أساس مفاتيح غير أساسيّة non-primary حيث يتم تداخل قيمة ما من الانضمام join بداخل قيمة JSON ولكن يكون الترشيح filter على أساس قيمة JSON مختلفة. نسأل في البداية عن كافّة حقول جدول المستخدمين users مع ضم جدول الأحداث events بمطابقة حقل البريد الإلكتروني email حيث يكون اسم الحدث هو “received email”. memsql> select * from users left join events on users.email = events.event::$email where events.event::$name = 'received email';+----+-----------------+------+-----------------------------------------------------+ | id | email | id | event | +----+-----------------+------+-----------------------------------------------------+ | 2 | two@example.com | 2 | {"email":"two@example.com","name":"received email"} | +----+-----------------+------+-----------------------------------------------------+ 1 row in set (14.19 sec)نجرب بعد ذلك نفس الاستعلام ولكن مع ترشيح الأحداث “sent email” فقط: memsql> select * from users left join events on users.email = events.event::$email where events.event::$name = 'sent email'; +----+-----------------+------+-------------------------------------------------+ | id | email | id | event | +----+-----------------+------+-------------------------------------------------+ | 1 | one@example.com | 1 | {"email":"one@example.com","name":"sent email"} | +----+-----------------+------+-------------------------------------------------+ 1 row in set (0.01 sec)وكما وجدنا سابقًا فإنّ الاستعلام الثاني أسرع بكثير من الأول، تظهر فائدة توليد الشيفرة عند تنفيذ أكثر من مليون صف، كما رأينا في قياس الأداء، إنّ مرونة استخدام قواعد بيانات SQL تفهم JSON وكيفيّة الانضمام بين الجداول هي ميزة قوية للمستخدمين. الخاتمةلقد قمنا بتثبيت MemSQL، تنفيذ تجربة قياس أداء العقد لدينا، التعامل مع العقدة من خلال عميل MySQL المعياري، وتعاملنا مع بعض الميّزات المتقدمة غير الموجودة في MySQL، ينبغي أن يعطينا هذا فكرة واضحة عمّا يمكن أن تفعله قاعدة بيانات SQL داخل الذاكرة لنا. لا يزال هناك الكثير لتعلّمه حول كيفيّة توزيع MemSQL لبياناتنا فعليًّا، كيفية بناء الجداول من أجل أفضل أداء، كيفيّة توسيع MemSQL عبر عدّة عقد، كيفيّة تكرار بياناتنا من أجل توافريّة عالية للبيانات، وكيفيّة تأمين MemSQL. ترجمة -وبتصرّف- لـ How to Install MemSQL on Ubuntu 14.04 لصاحبه Ian Hansen.
  4. إنّ Mytop هي أداة سطر أوامر مفتوحة المصدر open source تُستخدَم لمراقبة أداء MySQL، وهي مستوحاة من أداة مراقبة نظام لينِكس التي تُدعى top ومُشابِهة لها في الشّكل والمظهر، تتصل Mytop إلى خادوم MySQL وتقوم بشكل دوري بتشغيل الأمرين show processlist و show global status، وتقوم بعدها بتلخيص المعلومات بشكل مفيد، نستطيع باستخدام Mytop مراقبة (بشكلٍ آني real-time) مناقشات MySQL threads، الاستعلامات queries، وزمن التشغيل uptime، بالإضافة إلى أنّها ترى أي مستخدم يقوم بتنفيذ استعلامات على أي قاعدة بيانات، وأي الاستعلامات تجري ببطء والمزيد من ذلك، نستطيع استخدام كل هذه المعلومات لتحسين أداء خادوم MySQL. سنناقش في هذا الدّرس كيفيّة تثبيت، إعداد، واستخدام mytop. المتطلبات الأساسيةقبل البدء في هذا الدرس ينبغي أن نمتلك ما يلي لدينا: CentOS 7 64-bit Droplet (تعمل أيضًا مع CentOS 6).مستخدم غير جذري non-root مع صلاحيّات sudo سيتم تنفيذ جميع الأوامر عن طريق هذا المستخدم.خادوم MySQL يعمل على الـ Droplet.الخطوة الأولى – تثبيت Mytopفلنقم بتثبيت الحِزَم المطلوبة من أجل mytop. نحتاج في البداية إلى تثبيت مستودع yum الذي يُدعى (EPEL (Extra Packages for Enterprise Linux على الخادوم، إنّ EPEL هي مجموعة ذات اهتمامات مشتركة بتوزيعة Fedora تقوم بإنشاء، إدارة، والحفاظ على مجموعة حِزَم برمجيّة إضافيّة add-on مفتوحة المصدر عالية الجّودة من أجل Enterprise Linux، نقوم بتنفيذ الأمر التالي لتثبيت وتمكين مستودع EPEL على خادومنا: على CentOS 7: sudo rpm -ivh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpmعلى CentOS 6: sudo rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpmوقبل المتابعة نتحقّق أنّه تم تمكين المستودع EPEL باستخدام: sudo yum repolistإن تمّ تمكينه سنشاهد في الخَرْج ما يلي: epel/x86_64 Extra Packages for Enterprise Linux 7 - x86_64فلنقم بعد ذلك بحماية الحِزَم الأساسيّة من EPEL باستخدام إضافة yum plugin التي تُدعى protectbase: sudo yum install yum-plugin-protectbase.noarch -yالغرض من الإضافة protectbase هو حماية بعض مستودعات yum من تحديثات المستودعات الأخرى، فلن يتم تحديث أو تجاوز override الحِزَم الموجودة في المستودعات المحميّة بواسطة الحِزَم في المستودعات غير المحميّة حتى ولو كان المستودع غير المحمي يملك إصدارًا أحدث. نحن الآن مستعدون لتثبيت حِزمة mytop، فلنقم بتنفيذ الأمر التالي لتثبيتها: sudo yum install mytop -yسيقوم هذا الأمر بتثبيت حِزمة mytop بالإضافة إلى جميع اعتمادياتها dependencies والتي هي في معظمها وحدات perl modules. الخطوة الثانية – ضبط إعدادات Mytopنقوم قبل استخدام mytop بإنشاء ملف إعدادات مُخصَّص من أجل mytop يُدعى mytop.، وذلك بكتابة الأمر التالي: sudo nano /root/.mytopنضيف المحتوى التالي إلى الملف ونقوم بحفظه وإغلاقه: root/.mytop/ host=localhost db=mysql delay=5 port=3306 socket= batchmode=0 color=1 idle=1سيتم استخدام ملف الإعدادات عندما نقوم بتشغيل mytop بشكل مباشر كمستخدم جذري root وعندما نقوم بتشغيلها باستخدام الأمر sudo كمستخدم غير جذري يملك صلاحيّات sudo. نستطيع القيام بتغييرات إلى ملف الإعدادات اعتمادًا على احتياجاتنا، على سبيل المثال يُحدِّد الخيار delay الفترة الزمنيّة مقدرةً بالثانية بين تحديثات العرض display، فإن كُنّا نرغب بتحديث عرض mytop كل 3 ثوان بإمكاننا تحرير الملف root/.mytop/ باستخدام: sudo nano /root/.mytopومن ثمّ تغيير ما يلي: root/.mytop/ delay=3يُحدِّد المُعامِل idle إذا ما كان سيسمح للمناقشات threads الخاملة idle (النائمة) بالظهور في قائمة شاشة عرض mytop، الوضع الافتراضي هو إظهار المناقشات الخاملة، إن تمّ حذف المناقشات الخاملة سينعكس ترتيب الفرز sorting الافتراضي بحيث تظهر أطول الاستعلامات قيد التشغيل في أعلى القائمة، إن كُنّا نرغب في فعل هذا نُحرّر الملف root/.mytop/ ونغيّر ما يلي: root/.mytop/ idle=0نستطيع الرجوع إلى صفحات mytop اليدويّة للمزيد من المعلومات عن جميع المُعامِلات في ملف الإعدادات والتي تحتوي على وصف لكل مُعامِل، للوصول إلى الصفحات اليدويّة نستخدم الأمر: man mytopنستطيع كتابة q للخروج من الدّليل. الخطوة الثالثة – الاتصال إلى Mytopسنناقش في هذا القسم كيفيّة الاتصال إلى mytop واستخدامها لعرض استعلامات MySQL. تتطلّب Mytop اعتمادات credentials للنفاذ إلى قاعدة البيانات، والتي يمكن تزويدها عبر مُحِث prompt في سطر الأوامر أو عبر تخزينها في ملف الإعدادات، سنستخدم من أجل أمان أفضل الخيار prompt-- والذي يسأل كل مرّة عن كلمة السّر. فلنتصل إلى mytop باستخدام: sudo mytop --promptونُدخِل كلمة سر المستخدم root في MySQL في المُحِث، بإمكاننا أيضًا استخدام العديد من مُعطيات arguments سطر الأوامر مع الأمر mytop، نرجو الرجوع إلى الصفحات اليدويّة من أجل الحصول على قائمة كاملة بها، على سبيل المثال إن كُنّا نرغب باستخدام مستخدم mysql مُختلف مثل sammy للاتصال إلى mytop نقوم باستخدام الأمر التالي: sudo mytop -u sammy --promptوللاتصال ومراقبة قاعدة بيانات مُحدّدة فقط نستخدم الأمر التالي: sudo mytop -d databasename --promptللخروج من mytop والعودة إلى مُحث الصّدفة shell prompt نكتب q. الخطوة الرابعة – عرض وتفسير شاشة عرض Mytopسنرى في هذا القسم كيفيّة تفسير شاشة عرض mytop والميزات المختلفة التي تُقدّمها هذه الأداة. حالما نتصل إلى mytop باستخدام mytop --prompt سيتم أخذنا إلى طريقة عرض المناقشات thread view، والتي ستظهر خَرْج مشابه لما يلي: Output of mytop MySQL on localhost (5.5.41-MariaDB) up 0+00:05:52 [01:33:15] Queries: 148 qps: 0 Slow: 0.0 Se/In/Up/De(%): 09/00/00/00 qps now: 2 Slow qps: 0.0 Threads: 6 ( 5/ 0) 67/00/00/00 Key Efficiency: 2.0% Bps in/out: 14.7/320.7k Now in/out: 192.5/731.8k Id User Host/IP DB Time Cmd Query or State -- ---- ------- -- ---- --- ---------- 2 root localhost mysql 0 Query show full processlist 16 root localhost 0 Sleep 17 root localhost testdb 0 Query SELECT * FROM dept_emp 18 root localhost testdb 0 Query SELECT * FROM dept_emp 19 root localhost testdb 0 Query SELECT * FROM dept_emp 20 root localhost testdb 0 Query SELECT * FROM dept_empنستطيع العودة إلى طريقة العرض هذه إن كُنّا في طريقة عرض أخرى بكتابة t. تُقسم شاشة العرض السّابقة إلى قسمين، تُؤلّف الأسطر الأربعة الأولى الترويسة header والتي يُمكن تشغيلها وإيقافها بضغط SHIFT-H، تحتوي الترويسة على معلومات موجزة حول خادوم MySQL لدينا. يُحدِّد السطر الأول اسم المضيف hostname للخادوم وإصدار MySQL الموجود، يُظهر القسم الأيمن منه زمن التشغيل uptime لعمليّة خادوم MySQL بصيغة الأيام+السّاعات:الدّقائق:الثواني (days+hours:minutes:seconds) بالإضافة للوقت الحالي.يعرض السطر الثاني العدد الكلّي للاستعلامات التي قام الخادوم بمعالجتها (148 في حالتنا)، متوسط عدد الاستعلامات في الثانية، عدد الاستعلامات البطيئة، والنسبة المئوية للاستعلامات اختيار Select، إدخال Insert، تحديث Update، وحذف Delete.يُظهِر السطر الثالث قيم آنيّة real-time منذ التحديث refresh الأخير لـ mytop، إنّ زمن التّحديث (التأخير) الطبيعي في mytop هو 5 ثوان، لذا إن تمّ إجراء 100 استعلام خلال آخر 5 ثوان منذ التحديث سيكون عدد qps now هو 20، الحقل الأول هو عدد الاستعلامات في الثانية (qps now: 2). القيمة الثانية هي عدد الاستعلامات البطيئة في الثانية، يُشير القسم Threads: 6 ( 5/ 0) إلى وجود 6 مناقشات threads مُتّصلة، 5 منها نشطة (واحدة منها نائمة) وعدم وجود أي مناقشات (الرقم 0) في الذّاكرة المؤقّتة cache للمناقشات، يُظهر الحقل الأخير في السّطر الثالث النّسبة المئويّة للاستعلام، كما هو الحال في السّطر السابق، ولكن منذ التحديث الأخير لـ mytop.يعرض السّطر الرّابع فعاليّة key buffer (وهو عدد المرات التي فيها قراءة المفاتيح keys من الـ buffer بدلًا من القرص) وعدد البايتات التي أرسلتها واستقبلتها MySQL، كلاهما بالمجمل ومنذ آخر تحديث لـ mytop. تُظهِر Key Efficiency: 2.0% أنّه يتم قراءة 2% من المفاتيح من الـ buffer وليس من القرص، تُظهِر Bps in/out: 14.7/320.7k أنّه منذ بدء التشغيل تلقّت MySQL ما يُعادِل 14.7kbps من حركة البيانات traffic الواردة و 320.7kbps من حركة البيانات الصادرة، يُظهِر Now in/out حركة البيانات أيضًا ولكن منذ آخر تحديث لـ mytop.يعرض القسم الثاني من شاشة العرض مناقشات MySQL الحاليّة مع فرزها بحسب زمن خمولها (الأقل خمولًا أولًا)، نستطيع عكس ترتيب الفرز بضغط O عند الحاجة لذلك، يتم هنا أيضًا عرض مُعرِّف المناقشة thread id، اسم المستخدم، المُضيف الذي يتصل منه المستخدم، قاعدة البيانات التي يتصل إليها المستخدم، زمن الخمول مُقدّرًا بالثانية، الأمر الذي تقوم المناقشة بتنفيذه (أو حالة المناقشة)، والقسم الأول من معلومات الاستعلام، إن كانت المناقشة في حالة استعلام Query (أي يعرض العمود Cmd القيمة Query) فسيعرض العمود التالي الذي يُدعى Query or State القسم الأول من الاستعلام الذي يتم تنفيذه، أمّا إن كانت حالة الأمر هي نائم Sleep أو خامل Idle فسيكون العمود Query or State فارغًا عادةً، في مثال الخَرْج السّابق لدينا فإنّ المناقشة ذات المُعرِّف id 2 هي فعليًّا mytop والتي تقوم بتشغيل الاستعلام show processlist لجمع المعلومات، والمناقشة ذات المُعرِّف 16 نائمة (أي لا تقوم بمعالجة استعلام ولكنّها تبقى متصلة)، والمناقشة ذات المُعرِّف 17 تقوم بتشغيل استعلام SELECT على قاعدة البيانات testdb. الآن وقد فهمنا أساسيّات شاشة عرض mytop، سنرى كيفيّة استخدامها لجمع المزيد من المعلومات حول مناقشات واستعلامات MySQL، فلنلقِ نظرة على شاشة عرض mytop التّالية: [secondary_output Output of mytop] MySQL on localhost (5.5.41-MariaDB) up 0+00:13:10 [23:54:45] Queries: 2.8k qps: 4 Slow: 51.0 Se/In/Up/De(%): 45/00/00/00 qps now: 17 Slow qps: 0.0 Threads: 52 ( 51/ 0) 96/00/00/00 Key Efficiency: 100.0% Bps in/out: 215.4/ 7.6M Now in/out: 2.0k/16.2M Id User Host/IP DB Time Cmd Query or State -- ---- ------- -- ---- --- ---------- 34 root localhost testdb 0 Query show full processlist 1241 root localhost 1 Sleep 1242 root localhost testdb 1 Query SELECT * FROM dept_emp 1243 root localhost testdb 1 Query SELECT * FROM dept_emp 1244 root localhost testdb 1 Query SELECT * FROM dept_emp 1245 root localhost testdb 1 Query SELECT * FROM dept_emp 1246 root localhost testdb 1 Query SELECT * FROM dept_emp 1247 root localhost testdb 1 Query SELECT * FROM dept_empتكون الاستعلامات مبتورة في طريقة عرض المناقشات thread view (طريقة العرض الافتراضيّة)، ولمشاهدة كامل الاستعلام بإمكاننا أن نضغط F وسيتم سؤالنا كما يلي: Full query for which thread id:نُدخِل مُعرِّف المناقشة thread id التي نريد عرض استعلامها، على سبيل المثال نكتب 1244 وسنشاهد ما يلي: Thread 1244 was executing following query: SELECT * FROM dept_emp WHERE ... -- paused. press any key to resume or (e) to explain --نستطيع كتابة e لشرح explain الاستعلام، والذي يقوم بشرح الاستعلام الذي يتم تنفيذه حتى نعرف إذا ما كان هذا هو الاستعلام الأمثل، إنّ EXPLAIN هي واحدة من أقوى الأدوات لفهم وتحسين استعلامات MySQL الصعبة، على سبيل المثال: EXPLAIN SELECT * FROM dept_emp: *** row 1 *** table: dept_emp type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 332289 Extra: NULL -- paused. press any key to resume --بإمكاننا أن نضغط على أي مفتاح للخروج من هذا الوضع أو نكتب t للعودة إلى طريقة عرض المناقشات الافتراضيّة. ومن طرق العرض المفيدة الأخرى المتاحة في mytop هي طريقة عرض الأوامر command view، وللوصول إليها نكتب c، ستبدو مشابهة لما يلي: Command Total Pct | Last Pct ------- ----- --- | ---- --- select 1782 55% | 100 8% show status 723 22% | 533 45% show processlist 708 22% | 532 45% change db 2 0% | 0 0% show variables 1 0% | 0 0% Compression 0 0% | 0 0%يُظهِر العمود Command نوع الأمر أو الاستعلام الذي يتم تنفيذه، يرمز العمود Total إلى العدد الإجمالي لهذا النوع من الأوامر التي تم تنفيذها منذ بدء تشغيل الخادوم، ويُظهِر العمود Pct نفس ما سبق ولكن بالنسبة المئوية. وعلى الناحية الأخرى من الخط العمودي نجد العمود Last والذي يُخبرنا بعدد هذا النوع من الأوامر التي تم تشغيلها منذ آخر تحديث لـ mytop، تُعطينا هذه المعلومات فكرة عمّا يقوم به خادوم MySQL على المدى القريب والبعيد. ناقشنا في هذا الدّرس بعضًا من ميّزات mytop الهامّة والمفيدة، هناك العديد من الميّزات الأخرى المتاحة، ولعرض قائمة كاملة من الخيارات نستطيع أن نضغط المفتاح أثناء تشغيل mytop. الخاتمةيجب أن يكون لدينا الآن معرفة جيّدة حول كيفيّة استخدام mytop لمراقبة خادوم MySQL لدينا، وهي أيضًا نقطة انطلاق لإيجاد مشاكل استعلامات SQL وتحسينها، وبالتالي زيادة الأداء الإجمالي للخادوم. ترجمة -وبتصرّف- لـ How To Use Mytop to Monitor MySQL Performance لصاحبته Veena K John.
  5. يكثر الحديث هذه الأيام عن أنماط تصميم البرمجيات، ومن الأسئلة الأكثر شيوعًا "كيف يمكنني استخدام النمط الفلاني مع التقنية (التكنولوجيا) الفلانيّة؟". أما في حالة Laravel ونمط المستودع (Repository pattern)، فكثيرًا أرى أسئلة من قبيل "كيف يمكنني أن أستخدم نمط المستودع في Laravel 4؟" أو في هذه الأيام "مع Laravel 5؟". من المهم أن تتذكر أنّ أنماط التصميم لا تعتمد على تقنيّة محدّدة، أو إطار برمجي محدد، أو لغة برمجة محددة. إذا كنت قد فهمت نمط المستودع بالفعل، فلا يهم الإطار البرمجي أو اللغة البرمجة التي ستستخدمها. المهم أن تفهم المبدأ الذي يقف خلف نمط المستودع، ومن ثم يمكنك استخدامه في أيّ تقنية تريد. آخذين هذا بعين الاعتبار، لنبدأ بتعريف نمط المستودع: يفصل نمط المستودع منطق الوصول إلى البيانات (data access logic) ويربطه مكانيًّا بكيانات الأعمال (business entities) في المنطق الأعمال (business logic). ويتم التواصل بين منطق الوصول إلى البيانات ومنطق الأعمال باستخدام واجهات (interfaces). ولتبسيط ذلك، فإن نمط المستودع نوع من الحاويات يخزّن فيه منطق الوصول إلى البيانات، بحيث يخفي منطق الوصول إلى البيانات عن منطق الأعمال. وبعبارة أخرى، نسمح لمنطق الأعمال أن يصل إلى كائن البيانات دون معرفة هيكلية الوصول إلى البيانات التي تتم خلف الكواليس. ولفصل الوصول إلى البيانات عن منطق الأعمال فوائد عدّة، منها: مركزية منطق الوصول إلى البيانات تجعل النصوص البرمجية أسهل في الصيانة.يمكن فحص منطق الأعمال ومنطق الوصول إلى البيانات كلًّا على حدة.تقليل تكرار الأكواد البرمجية.فرصة أقل للوقوع في أخطاء برمجية.الأمر كلّه يتعلق بالواجهاتكلّ ما في نمط المستودع ذو علاقة بالواجهات. تعمل الواجهة كاتفاقية تحدّد ما على فئة concrete تنفيذه.لنفكّر قليلًا. إذا كان لدينا كائني بيانات، الممثل والفلم، فما هي مجموعة العمليات الشائعة التي يمكن تطبيقها على هذين العنصرين؟ سنرغب في غالب الأحيان بالقيام بالعمليات التالية: الحصول على جميع السجلات.الحصول على مجموعة السجلّات مرقّمة.إنشاء سجلّ جديد.الحصول على سجل باستخدام المفتاح الرئيسيّ.الحصول على سجل باستخدام خواص (attributes) أخرى.تحديث سجلّ.حذف سجلّ.هل يمكنك الآن أن ترى كميّة النصوص البرمجيّة المكررة التي ستكون لدينا إذا قمنا بذلك مع كلّ كائن بيانات؟ حسنًا، هذه ليست مشكلة كبيرة بالنسبة للمشاريع الصغيرة، ولكنها أمر سيّء بالنسبة للمشاريع الكبيرة. الآن، وبعد أن عرّفنا العمليات الشائعة، يمكننا إنشاء واجهة: interface RepositoryInterface { public function all($columns = array('*')); public function paginate($perPage = 15, $columns = array('*')); public function create(array $data); public function update(array $data, $id); public function delete($id); public function find($id, $columns = array('*')); public function findBy($field, $value, $columns = array('*')); }هيكليّة الدليل Directory Structureقبل أن نتابع إنشاء فئة مستودع concrete التي ستنفّذ هذه الواجهة، لنفكّر قليلًا كيف نريد أن ننظّم النصوص البرمجيّة لدينا. عادة، عندما أنشئ شيئًا ما، أفضّل أن أفكر بطريقة تقسيمه إلى مكوّنات، حيث أرغب بأن أكون قادرًا على إعادة استخدام ذاك النصّ البرمجيّ لمشاريع أخرى. إن هيكليّة الدليل البسيطة التي أتبعها لمكونات المستودع تبدو كما يلي: ولكنها يمكن أن تكون مختلفة، كأن يكون للمكونات خيارات ضبط مثلًا. لدي في داخل الدليل src ثلاثة أدلّة أخرى، هي: Contracts وEloquent وexceptions. كما ترى، أسماء المجلدات مناسبة للغاية لما نريد وضعه فيها. ففي Contracts نضع الواجهات، أو الاتفاقيات – كما سميناها أعلاه - . ويحوي مجلد Eloquent مستودعي abstract و concrete تنفّذ الاتفاقات. ونضع في المجيد Exceptions فئات الاستثناءات. وحيث أننا ننشئ حزم، فعلينا أن ننشئ ملف composer.json، حيث نجد فيه ربطًا مكانيًّا لنطاق الأسماء (namespaces) لأدلّة معيّنة، واعتماديات حزم، وبيانات وصفيّة أخرى للحزم. فيما يلي محتوى composer.json لهذه الحزمة: { "name": "bosnadev/repositories", "description": "Laravel Repositories", "keywords": [ "laravel", "repository", "repositories", "eloquent", "database" ], "licence": "MIT", "authors": [ { "name": "Mirza Pasic", "email": "mirza.pasic@edu.fit.ba" } ], "require": { "php": ">=5.4.0", "illuminate/support": "5.*", "illuminate/database": "5.*" }, "autoload": { "psr-4": { "Bosnadev\\Repositories\\": "src/" } }, "autoload-dev": { "psr-4": { "Bosnadev\\Tests\\Repositories\\": "tests/" } }, "extra": { "branch-alias": { "dev-master": "0.x-dev" } }, "minimum-stability": "dev", "prefer-stable": true } كما ترى، فقد ربطنا Bosnadev\Repository إلى الدليل src. وهناك شيء آخر علينا أخذه بعين الاعتبار قبل البدء بتنفيذ RepositoryInterface، فحيث أنها موجودة في المجلد Contracts، فعلينا أن نحدّد نطاق الاسم لها: <?php namespace Bosnadev\Repositories\Contracts; interface RepositoryInterface {...}نحن الآن مستعدون لبدء تنفيذ المحتوى. تنفيذ المستودععلى كل مستودع فرعيّ في concrete أن يزيد من مستودع abstract لدينا، مما يعني تنفيذ اتفاقية RepositoryInterface. أما الآن، فكيف تنفّذ هذه الاتفاقيّة؟ ألقِ نظرة على method الأولى. ما الذي يمكنك قوله عنها بمجرد النظر إليها؟ تُسمى method الأولى في اتفاقيتنا بالاسم ()all للتوضيح. مهمتها هي جلب كلّ السجلات لهيئة concrete. تستقبل معامِلاً واحدًا، وهو columns$ الذي يجب أن يكون مصفوفة (array). يُستخدّم هذا المعامِل – كما هو واضح من اسمه – لتحديد الأعمدة التي ترغب بجلبها من مصدر البيانات، ومبدئيًّا نقوم بجلبها كلّها. لهئيات محدّدة، يمكن أن تبدو كالتالي: <?php namespace Bosnadev\Repositories\Contracts; interface RepositoryInterface {...}لكننا نريدها أن تكون عامّة أكثر لكي نتمكن من استخدامها متى أردنا: public function all($columns = array('*')) { return $this->model->get($columns); } في هذه الحالة this->model$ هي نسخة من Bosnadev\Models\Actor، ولهذا، فعلينا إنشاء نسخة جديدة للنموذج الموجود في مكان ما في المستودع. هنا أحد الحلول التي يمكنك استخدامها لتنفيذ هذا الأمر: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @param App $app * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app) { $this->app = $app; $this->makeModel(); } /** * Specify Model class name * * @return mixed */ abstract function model(); /** * @return Model * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model; } } وحيث أننا عرفنا الفئة كـ abstract، فهذا يعني أن علينا زيادتها باستخدام فئة متفرّعة عن concrete. فعند تعريف method المسمّى ()model كـ abstract، فإننا نجبر المستخدم على تنفيذ هذه الطريقة (method) في فئة متفرعة عن concrete. فمثلًا: <?php namespace App\Repositories; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Eloquent\Repository; class ActorRepository extends Repository { /** * Specify Model class name * * @return mixed */ function model(){ return 'Bosnadev\Models\Actor'; } }يمكننا الآن أن ننفذ بقية طرق (methods) الاتفاقات: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @param App $app * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app) { $this->app = $app; $this->makeModel(); } /** * Specify Model class name * * @return mixed */ abstract function model(); /** * @param array $columns * @return mixed */ public function all($columns = array('*')) { return $this->model->get($columns); } /** * @param int $perPage * @param array $columns * @return mixed */ public function paginate($perPage = 15, $columns = array('*')) { return $this->model->paginate($perPage, $columns); } /** * @param array $data * @return mixed */ public function create(array $data) { return $this->model->create($data); } /** * @param array $data * @param $id * @param string $attribute * @return mixed */ public function update(array $data, $id, $attribute="id") { return $this->model->where($attribute, '=', $id)->update($data); } /** * @param $id * @return mixed */ public function delete($id) { return $this->model->destroy($id); } /** * @param $id * @param array $columns * @return mixed */ public function find($id, $columns = array('*')) { return $this->model->find($id, $columns); } /** * @param $attribute * @param $value * @param array $columns * @return mixed */ public function findBy($attribute, $value, $columns = array('*')) { return $this->model->where($attribute, '=', $value)->first($columns); } /** * @return \Illuminate\Database\Eloquent\Builder * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model->newQuery(); } } الأمر سهل للغاية، صح؟ الشيء الوحيد المتبقي الآن هو حقن ActorRepository في ActorsController، أو الجزء المتعلق بالأعمال (business logic) من تطبيقنا. <?php namespace App\Http\Controllers; use App\Repositories\ActorRepository as Actor; class ActorsController extends Controller { /** * @var Actor */ private $actor; public function __construct(Actor $actor) { $this->actor = $actor; } public function index() { return \Response::json($this->actor->all()); } }استعلامات المعايير Criteria Queriesكما يمكنك أن تتخيل، فهذه الأعمال البسيطة كافية فقط لاستعلامات البسيطة. أما بالنسبة للتطبيقات الكبيرة، فستحتاج بالتأكيد لعمل بعض الاستعلامات الخاصّة لاستدعاء مجموعة بيانات محدّدة أكثر باستخدام معايير محدّدة. للقيام بذلك، نبدأ بتحديد ما على معايير الفروع (child ، أو العميل client) القيام به. وبعبارة أخرى، سننشئ فئة abstract non-instantiable باستخدام method واحدة فقط : <?php namespace Bosnadev\Repositories\Criteria; use Bosnadev\Repositories\Contracts\RepositoryInterface as Repository; use Bosnadev\Repositories\Contracts\RepositoryInterface; abstract class Criteria { /** * @param $model * @param RepositoryInterface $repository * @return mixed */ public abstract function apply($model, Repository $repository); }ستحوي هذه الطريقة (method) استعلام معايير يتم تطبيقه في فئة Repository ضمن هيئة concrete. علينا أيضًا أن نزيد على فئة Repository قليلًا لتغطية استعلامات المعايير. لكن قبل ذلك، هيّا ننشئ اتفاقية أخرى لفئة Repository. <?php namespace Bosnadev\Repositories\Contracts; use Bosnadev\Repositories\Criteria\Criteria; /** * Interface CriteriaInterface * @package Bosnadev\Repositories\Contracts */ interface CriteriaInterface { /*** @param bool $status * @return $this */ public function skipCriteria($status = true); /*** @return mixed*/public function getCriteria(); /*** @param Criteria $criteria* @return $this*/public function getByCriteria(Criteria $criteria); /*** @param Criteria $criteria* @return $this*/public function pushCriteria(Criteria $criteria); /*** @return $this*/public function applyCriteria(); }يمكننا أن نزيد فاعلية فئة Repository بتنفيذ اتفاقية CriteriaInterface: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\CriteriaInterface; use Bosnadev\Repositories\Criteria\Criteria; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface, CriteriaInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @var Collection */ protected $criteria; /** * @var bool */ protected $skipCriteria = false; /** * @param App $app * @param Collection $collection * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app, Collection $collection) { $this->app = $app; $this->criteria = $collection; $this->resetScope(); $this->makeModel(); } /** * Specify Model class name * * @return mixed */ public abstract function model(); /** * @param array $columns * @return mixed */ public function all($columns = array('*')) { $this->applyCriteria(); return $this->model->get($columns); } /** * @param int $perPage * @param array $columns * @return mixed */ public function paginate($perPage = 1, $columns = array('*')) { $this->applyCriteria(); return $this->model->paginate($perPage, $columns); } /** * @param array $data * @return mixed */ public function create(array $data) { return $this->model->create($data); } /** * @param array $data * @param $id * @param string $attribute * @return mixed */ public function update(array $data, $id, $attribute="id") { return $this->model->where($attribute, '=', $id)->update($data); } /** * @param $id * @return mixed */ public function delete($id) { return $this->model->destroy($id); } /** * @param $id * @param array $columns * @return mixed */ public function find($id, $columns = array('*')) { $this->applyCriteria(); return $this->model->find($id, $columns); } /** * @param $attribute * @param $value * @param array $columns * @return mixed */ public function findBy($attribute, $value, $columns = array('*')) { $this->applyCriteria(); return $this->model->where($attribute, '=', $value)->first($columns); } /** * @return \Illuminate\Database\Eloquent\Builder * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model->newQuery(); } /** * @return $this */ public function resetScope() { $this->skipCriteria(false); return $this; } /** * @param bool $status * @return $this */ public function skipCriteria($status = true){ $this->skipCriteria = $status; return $this; } /** * @return mixed */ public function getCriteria() { return $this->criteria; } /** * @param Criteria $criteria * @return $this */ public function getByCriteria(Criteria $criteria) { $this->model = $criteria->apply($this->model, $this); return $this; } /** * @param Criteria $criteria * @return $this */ public function pushCriteria(Criteria $criteria) { $this->criteria->push($criteria); return $this; } /** * @return $this */ public function applyCriteria() { if($this->skipCriteria === true) return $this; foreach($this->getCriteria() as $criteria) { if($criteria instanceof Criteria) $this->model = $criteria->apply($this->model, $this); } return $this; } } إنشاء معايير جديدة، حيث يمكنك الآن أن ترتب مستودعاتك بسهولة أكبر. لست بحاجة لأن تكون مستودعاتك مكونة من آلاف الأسطر. يمكن أن تبدو فئة المعايير لديك كالآتي: <?php namespace App\Repositories\Criteria\Films; use Bosnadev\Repositories\Contracts\CriteriaInterface; use Bosnadev\Repositories\Contracts\RepositoryInterface as Repository; use Bosnadev\Repositories\Contracts\RepositoryInterface; class LengthOverTwoHours implements CriteriaInterface { /** * @param $model * @param RepositoryInterface $repository * @return mixed */ public function apply($model, Repository $repository){ $query = $model->where('length', '>', 120); return $query; } }استخدام للمعايير (Criteria) في المتحكّمات (Controllers)الآن وقد صارت لدينا معايير بسيطة، لنرَ كيف يمكننا استخدامها. هناك طريقتان يمكنك اتباعهما لتطبيق المعايير على مستودع. الأولى باستخدام طريقة ()pushCriteria: <?php namespace App\Http\Controllers; use App\Repositories\Criteria\Films\LengthOverTwoHours; use App\Repositories\FilmRepository as Film; class FilmsController extends Controller { /*** @var Film*/private $film; public function __construct(Film $film) { $this->film = $film; } public function index() { $this->film->pushCriteria(new LengthOverTwoHours()); return \Response::json($this->film->all()); } }هذه الطريقة مفيدة إذا كنت ترغب بتطبيق أكثر من معيار في نفس الوقت، حيث يمكنك رصّها معًا كما تشاء، ولكن إذا رغب بتطبيق معيار واحد فقط، فيمكنك استخدام طريقة ()getByCriteria: <?php namespace App\Http\Controllers; use App\Repositories\Criteria\Films\LengthOverTwoHours; use App\Repositories\FilmRepository as Film; class FilmsController extends Controller { /** * @var Film */ private $film; public function __construct(Film $film) { $this->film = $film; } public function index() { $criteria = new LengthOverTwoHours(); return \Response::json($this->film->getByCriteria($criteria)->all()); } }تثبيت الحزميمكنك تثبيت هذه الحزمة بإضافة الاعتمادية التالية إلى قسم require في ملف composer لديك: "bosnadev/repositories": "0.*"وتنفيذ composer update بعد ذلك، الخلاصةلاستخدام المستودعات في تطبيقك العديد من الفوائد. من أشياء بسيطة كتقليل تكرار الأكواد ومنعك من ارتكاب أخطاء برمجيّة وجعل تطبيقك أسهل في التكبير والتوسعة، والاختبار، والصيانة. من وجهة نظر هندسيّة، فقد تمكنت من عزل الاهتمامات. المتحكّم (controller) لديك ليس بحاجة لأن يعرف كيف وأين تخزَّن البيانات. بسيط وجميل. تجريديّ (Abstract). ترجمة وبتصرف للمقال: Using Repository Pattern in Laravel 5.
  6. بعد أن قمنا مسبقًا بتثبيت وإعداد خادوم قاعدة بيانات MySQL التجريبي الخاصّ بنا، يمكننا الآن البدء باستخدام mysqlslap. يمكن تشغيل mysqlslap عبر طرفية الصدفة العادية، لذا لن يكون هناك حاجة إلى الدخول إلى MySQL لتشغيله من هناك. وعلى الرغم من ذلك، فإننا سنقوم في درسنا هذا بفتح اتصالٍ جديد إلى خادومنا بالإضافة إلى بدء جلسة MySQL جديدة من هناك بواسطة المستخدم sysadmin الذي أنشأناه من قبل، لكي نتمكّن من التحقق من بعض الأمور وتحديث أخرى في MySQL بشكلٍ أسهل. لذا وفي المجمل، فإننا سنمتلك طرفيةً واحدة مفتوحة مع مستخدمٍ بصلاحيات إدارية (sudo)، وطرفية واحدة لـMySQL. قبل أن ندخل في الأوامر الرئيسية المُستخدمة للاختبار، قد تودّ أن تلقي نظرةً على هذه القائمة لأكثر الخيارات المفيدة لـmysqlslap. يمكن أن يساعدك هذا على تصميم أوامر mysqlslap الخاصّ بك لاحقًا. الخياروظيفتهuser--اسم مستخدم MySQL الذي يجب الاتصال به على خادوم قاعدة البياناتpassword--كلمة المرور الخاصّة باسم المستخدم. من الأفضل تركها فارغة في سطر الأوامرhost--اسم خادوم قاعدة بيانات MySQLport--رقم المنفذ الذي يجب من خلاله الاتصال بـMySQL إذا كان الافتراضي غير مستخدمconcurrency--عدد اتصالات العملاء الافتراضية التي سيتم محاكاتها من طرف mysqlslapiterations--عدد المرّات التي سيتم تشغيل الاختبار فيهاcreate-schema--قاعدة البيانات التي سيتم تشغيل الاختبار عليهاquery--عملية الاستعلام التي يجب تنفيذها. يمكن لهذا أن يكون متغيّر استعلام SQL أو مسارًا لملفّ سكربت SQLcreate--الاستعلام الذي يجب إنشاؤه كجدول، مجددًا، يمكن لهذا أن يكون متغيّر استعلام SQL أو مسارًا لملفّ SQLdelimiter--الحائل الذي يجب استخدامه للفصل بين جُمَل SQL متعددةengine--محرّك قاعدة البيانات الذي يجب استخدامه، مثل InnoDBauto-generate-sql-- يسمح هذا الخيار لـMySQL بتنفيذ اختبار الحِمل باستخدام أمر SQL المُنشَئ تلقائيًا من طرف mysqlslapحالة استخدام: اختبار الأداء مع بيانات واستعلامات SQL منشئة تلقائياسنبدأ عبر استخدام ميّزة mysqlslap في الإنشاء التلقائي لجمل SQL. عندما نقوم باستخدامها، فإنّ mysqlslap سيقوم بإنشاء قاعدة بيانات مؤقّتة منفصلة تدعى "mysqlslap". ستحوي قاعدة البيانات هذه جدولًا بسيطًا يحوي هو الآخر عددًا صحيحًا واحدًا وعمودًا واحدًا من نوع varchar بداخله بيانات تجريبية. يمكن أن تكون هذه طريقةً سهلة وسريعة للتحقق من أداء خادوم قاعدة البيانات الكلّي. سنبدأ عبر اختبار اتصال عميلٍ واحدٍ فقط يتم تكراره مرّةً واحدة كذلك باستخدام جملة SQL مُنشئة تلقائيًا: sudo mysqlslap --user=sysadmin --password --host=localhost --auto-generate-sql --verboseيجب أن يبدو الخرج كالتالي: Benchmark Average number of seconds to run all queries: 0.009 seconds Minimum number of seconds to run all queries: 0.009 seconds Maximum number of seconds to run all queries: 0.009 seconds Number of clients running queries: 1 Average number of queries per client: 0يقوم mysqlslap بإعطاء بعض الإحصائيات عن اختبار الأداء كما رأينا من الخرج السابق، حيث أنّه يقوم بإرجاع متوسّط، أقل وأعلى عدد من الثواني التي يحتاجها ليقوم بتنفيذ عملية الاستعلام. يمكننا أيضًا أن نرى أنّ عدد اتصالات العملاء المُستخدمة لاختبار الحِمل هذا كان واحدًا فقط. الآن، جرّب محاكاة 50 اتصال، وقم باختيار تشغيل عملية الاستعلام المُنشَئة تلقائيًا 10 مرّات: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=50 --iterations=10 --auto-generate-sql --verboseيعني هذا الأمر أنّه سيتم محاكاة 50 اتصالًا، يقوم كلّ منها بتنفيذ نفس عملية الاستعلام بنفس الوقت، وسيتم إعادة هذه الاختبار 10 مرّات. يظهر لنا الخرج فرقًا واضحًا في الوقت مع ازدياد الحِمل: Benchmark Average number of seconds to run all queries: 0.197 seconds Minimum number of seconds to run all queries: 0.168 seconds Maximum number of seconds to run all queries: 0.399 seconds Number of clients running queries: 50 Average number of queries per client: 0لاحظ كيف أنّ حقل "Number of clients running queries" أصبح الآن يظهر الرقم 50، وأنّ عدد عمليات الاستعلام بواسطة العميل الواحد هو صفر. تقوم SQL المُشَئة تلقائيًا بإنشاء جدولٍ بسيط يحوي حقلين اثنين، إلّا أنّه وفي معظم البيئات الإنتاجية فستكون بنية الجدول أكبر بكثير من هذا. يمكننا أن نفرض على mysqlslap أن يقوم بمحاكاة هذا عبر إضافة بعض الحقول الإضافية إلى جدول الاختبار. لفعل ذلك، يمكننا استخدام المُعامِلين الجديدين number-char-cols-- وnumber-int-cols--. تقوم هذه المُعامِلات بتحديد عدد الأعمدة من نوع varchar وint لإضافتها إلى جدول الاختبار. سنختبر استعلام SQL مُنشَئ تلقائيًا عن جدولٍ بـ5 أعمدة رقمية و20 عمود نصّي في المثال التالي. سنحاكي أيضًا 50 اتصالًا من أجهزة العملاء في الوقت ذاته وسنقوم بتكرار الاختبار 100 مرّة: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=50 --iterations=100 --number-int-cols=5 --number-char-cols=20 --auto-generate-sql --verbosسيستغرق هذا الأمر وقتًا أطول. يمكننا أن نتحوّل إلى الطرفية الأخرى الني قمنا بتشغيل جلسة MySQL الخاصّة بنا عليها لنرى ما يجري بينما يتم تنفيذ الاختبار. لاحظ أنّه في حال انتظرت وقتًا طويلًا، سيكتمل الاختبار ولن تتمكن من رؤية قاعدة البيانات الاختبارية. من طرفية MySQL، طبّق: show databases;لاحظ وجود قاعدة mysqlslap: +--------------------+ | Database | +--------------------+ | information_schema | | employees | | mysql | | mysqlslap | | performance_schema | +--------------------+ 5 rows in set (0.01 sec)يمكنك أن تتحقق من الجدول في قاعدة البيانات الاختبارية إن أردت; إنّه يدعى t1. تحقق من نافذة الطرفية الأخرى الآن. عندما ينتهي الاختبار، ستلاحظ أنّ الأداء قد انخفض بدرجةٍ أكبر من السابق بسبب الحِمل الزائد: Benchmark Average number of seconds to run all queries: 0.695 seconds Minimum number of seconds to run all queries: 0.627 seconds Maximum number of seconds to run all queries: 1.442 seconds Number of clients running queries: 50 Average number of queries per client: 0ارجع إلى نافذة طرفية MySQL. يمكننا أن نرى أنّ mysqlslap قد حذف قاعدة البيانات المؤقّتة الخاصّة به، طبّق: show databases;الخرج: +--------------------+ | Database | +--------------------+ | information_schema | | employees | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)حالة استخدام: اختبار الأداء مع استعلامات مخصصةاستعلامات SQL المُنشَئة تلقائيًا جيّدة إذا كنت تريد تقييم موارد الخادوم الفيزيائية. هيَ مفيدة عندما تريد معرفة درجة الحِمل التي يمكن للنظام أن يتحمّلها. عندما تريد التحقق من تطبيقٍ يعتمد على قاعدة بيانات، فإنّك ستودّ اختبار استعلاماتٍ حقيقية على بياناتٍ حقيقية. يمكن لهذه الاستعلامات أن تأتي من خادوم الويب الخاصّ بك أو من خادوم التطبيق الذي تشغّله. الآن، سنفترض أنّك تعرف الاستعلامات المحددة التي تريد اختبارها. في القسم التالي، سنريك طريقةً لمعرفة عمليات الاستعلام التي يتم تنفيذها على خادومك. يمكنك جعل mysqlslap ينفّذ عملية استعلام معيّنة عبر الخيار query--. لا يمكن لجُمل الـSQL أن تحتوي على فواصل الأسطر (أكثر من سطر) ضمنها، ويجب فصلها بواسطة فاصلة منقوطة (;). يجب أيضًا إغلاق الاستعلامات بعلامتيّ تنصيص. في الشفرة التالية، سنقوم بتشغيل عملية استعلام بسيطة عن الجدول deptemp. يحوي جدول "deptemp" أكثر من ثلاثمئة ألف سجل بداخله. لاحظ كيف قمنا بتحديد قاعدة البيانات employees مع الخيار create-schema--: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=50 --iterations=10 --create-schema=employees --query="SELECT * FROM dept_emp;" --verboseسيستغرق هذا بعض الوقت ليكتمل. بعدها، يجب أن تتلقّى تقريرًا عن اختبار الأداء كالتالي بعد دقيقة أو اثنتين: Benchmark Average number of seconds to run all queries: 18.486 seconds Minimum number of seconds to run all queries: 15.590 seconds Maximum number of seconds to run all queries: 28.381 seconds Number of clients running queries: 50 Average number of queries per client: 1(ملاحظة: إذا استغرقت العملية حوالي 10 دقائق أو لم ترجع أيّ خرج، فيجب عليك المحاولة مجددًا مع عددٍ أقل للخيار concurrency-- و/أو iterations--, أو محاولة تطبيقها على خادوم أقوى). بعدها، سنستخدم أكثر من جملة SQL مع الخيار query--. في المثال التالي، نقوم بإنهاء كلّ جملة بفاصلة منقوطة. سيعرف mysqlslap أننا نستخدم عددًا من أوامر SQL المنفصلة بسبب استخدامنا للخيارdelimiter--: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=20 --iterations=10 --create-schema=employees --query="SELECT * FROM employees;SELECT * FROM titles;SELECT * FROM dept_emp;SELECT * FROM dept_manager;SELECT * FROM departments;" --delimiter=";" --verboseيستخدم هذا الاختبار نفس عدد الاتصالات ونفس عدد مرّات التكرار، إلّا أنّ الأداء كان أبطئ بشكلٍ ملحوظ بسبب استخدام أكثر من جملة SELECT واحدة (متوسّط 23.8 ثانية مقابل 18.486 ثانية). Benchmark Average number of seconds to run all queries: 23.800 seconds Minimum number of seconds to run all queries: 22.751 seconds Maximum number of seconds to run all queries: 26.788 seconds Number of clients running queries: 20 Average number of queries per client: 5يمكن لجُمل SQL التي يتم استخدامها في بيئة إنتاجية أن تكون معقّدة. إضافة جملة SQL معقّدة إلى سكربت هو أسهل من وضعها للاختبارات، لذا، يمكننا أن نطلب من mysqlslap أن يقوم بقراءة الاستعلامات من ملفّ سكربت. للقيام بهذا، فلنقم بإنشاء ملفّ سكربت من أوامر SQL. يمكننا استخدام الشفرة البرمجية أدناه لإنشاء الملفّ: sudo echo "SELECT * FROM employees;SELECT * FROM titles;SELECT * FROM dept_emp;SELECT * FROM dept_manager;SELECT * FROM departments;" > ~/select_query.sql sudo cp ~/select_query.sql /mysqlslap_tutorial/يحوي ملفّ select_query.sql جميع عبارات SELECT الآن. بما أنّ السكربت يحوي أكثر من عملية استعلام واحدة، فيمكننا استخدام مفهومٍ جديد في الاختبار. يمكن لـmysqlslap أن يقوم بتوزيع عمليات الاستعلام. يمكننا القيام بهذا عبر تحديد عدد الاستعلامات التي يجب على كلّ جهازٍ عميل متّصل أن ينفذّها. يسمح mysqlslap بفعل هذا عبر استخدام الخيار number-of-queries--. لذا، إذا كان لدينا 50 اتصال و1000 عمليّة استعلام يجب تنفيذها، فإنّ كلّ جهاز عميل متّصل سينفّذ حوالي 20 عملية استعلام تقريبًا. أخيرًا، يمكننا أيضًا استخدام الخيار debug-info--، والذي سيظهر لنا حسبة تقريبية للموارد المستعملة. في الشفرة البرمجية التالية، نطلب من mysqlslap أن يستخدم ملفّ السكربت الذي أنشأناه للتوّ. إننا نقوم أيضًا بتحديد المُعامِل number-of-queries. سيتم تكرار العمليّة مرّتين كما أننا نريد معلومات التنقيح (debug) ضمن الخرج: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=20 --number-of-queries=1000 --create-schema=employees --query="/mysqlslap_tutorial/select_query.sql" --delimiter=";" --verbose --iterations=2 --debug-infoبعد أن يكتمل هذا الأمر، يجب أن نرى بعض النتائج المثيرة للاهتمام: Benchmark Average number of seconds to run all queries: 217.151 seconds Minimum number of seconds to run all queries: 213.368 seconds Maximum number of seconds to run all queries: 220.934 seconds Number of clients running queries: 20 Average number of queries per client: 50 User time 58.16, System time 18.31 Maximum resident set size 909008, Integral resident set size 0 Non-physical pagefaults 2353672, Physical pagefaults 0, Swaps 0 Blocks in 0 out 0, Messages in 0 out 0, Signals 0 Voluntary context switches 102785, Involuntary context switches 43هنا يكون متوسّط عدد الثواني اللازم لتنفيذ جميع عمليات الاستعلام في MySQL هو 217 ثانية، حوالي 4 دقائق. صحيحٌ أنّ هذا الرقم له علاقة بحجم الذاكرة العشوائية ونوع المعالج المُستخدم مع آلتنا الوهمية، إلّا أنّه كان كبيرًا أيضًا بسبب عدد الاستعلامات الكبير الذي كان كلّ جهاز عميل متصل يكرره مرّتين. يمكننا أن نرى وجود عددٍ كبير من أخطاء الصفحات غير العتادية (البرمجية). تحصل أخطاء الصفحات (page faults) عندما لا يمكن العثور على البيانات في الذاكرة ويتوجّب على النظام أن يبحث عنها على قرص الـSwap. يظهر الخرج أيضًا بعض المعلومات المتعلّقة بالمعالج المركزي. في حالتنا، فإننا نرى عدد كبيرًا من تبديلات السياق كذلك. حالة استخدام: سيناريو اختبار أداء عملي مع التقاط مباشر للاستعلاماتإلى الآن في أمثلتنا، كنّا نقوم بتطبيق عمليات الاستعلام مع قاعدة بيانات "employees" الخاصّة بنا، وهو ما قد لا يكون شيئًا يريدك مُدراء قواعد البيانات أن تفعله، وهناك سببٌ وجيهٌ لذلك. لا تريد أن تقوم بإضافة حِمل إضافي إلى قاعدة بياناتك الإنتاجية ولا تريد أن تقوم بتفيذ استعلامات اختبارية يمكنها أن تقوم بحذف، تحديث أو إدراج البيانات إلى جداول قاعدة البيانات الإنتاجية الخاصّة بك. سنريك كيفيّة عمل نسخة احتياطية من قاعدة بيانات إنتاجية وكيفيّة نسخها إلى بيئة تجريبية، في هذا المثال وعلى نفس الخادوم، ولكنّك طبعًا قد تودّ نسخها إلى خادومٍ منفصل بنفس قدرات العتاد. والأكثر أهميّة من ذلك، سنريك كيف تقوم بتسجيل الاستعلامات بشكلٍ حيّ أو مباشر (live) من قاعدة بيانات إنتاجية بالإضافة إلى كيفيّة إضافة الاستعلامات إلى سكربتٍ اختباري. في المجمل، ستحصل على الاستعلامات من بيئة عمل إنتاجية، ولكنّك ستقوم بتنفيذ الاختبارات على قاعدة البيانات التجريبية. الخطوات العامّة هي كالتالي، ويمكنك استخدامها لأيّ اختبار mysqlslap: انسخ قاعدة البيانات الإنتاجية إلى بيئة اختبارية.قم بإعداد MySQL لتسجيل والتقاط جميع طلبات الاتصال والاستعلام على قاعدة البيانات الإنتاجية.قم بمحاكاة حالة الاستخدام التي تحاول اختبارها. كمثال، إذا كنتَ تدير موقع تسوّق، يجب أن تشتري شيئًا لتقوم بتفعيل جميع عمليات الاستعلام الأساسية ضمن تطبيقك.قم بتعطيل تسجيل الاستعلامات.انظر إلى سجل الاستعلامات وقم بعمل قائمة بالاستعلامات التي تريد اختبارها.أنشئ ملفًّا اختباريًا لكلّ عملية استعلام تريد اختبارها.نفّذ الاختبارات.استخدم الخرج لتحسين أداء قاعدة البيانات الخاصّة بك.للبدء، قم بإنشاء نسخة احتياطية من قاعدة البيانات "employees". سنقوم بإنشاء مسارٍ منفصل لهذه النسخة الاحتياطية: sudo mkdir /mysqlslap_tutorial/mysqlbackup cd /mysqlslap_tutorial/mysqlbackupأنشئ النسخة الاحتياطية وانقلها إلى المسار الجديد: sudo mysqldump --user sysadmin --password --host localhost employees > ~/employees_backup.sql sudo cp ~/employees_backup.sql /mysqlslap_tutorial/mysqlbackup/اذهب إلى خادوم MySQL التجريبي الخاصّ بك، وأنشئ قاعدة البيانات "employees_backup": CREATE DATABASE employees_backup;في هذه النقطة، إذا كنتَ تستخدم خادومًا منفصلًا للاختبار، فيجب أن تنقل ملفّ employeesbackup.sql إليه، ومن جلسة الطرفيّة الرئيسية، استورد بيانات النسخة الاحتياطية إلى قاعدة البيانات employeesbackup: sudo mysql -u sysadmin -p employees_backup < /mysqlslap_tutorial/mysqlbackup/employees_backup.sqlعلى خادوم قاعدة بيانات MySQL الإنتاجية الخاصّ بك، فعّل سجل استعلامات MySQL العام وقم بتوفير اسم ملفٍّ خاصٍّ به. يقوم سجل الاستعلامات العام بالتقاط الاتصالات، الاتصالات المنقطعة وسجل الاستعلامات لقاعدة بيانات MySQL: SET GLOBAL general_log=1, general_log_file='capture_queries.log';الآن، شغّل الاستعلامات التي تريد اختبارها على خادوم MySQL الإنتاجي. في هذا المثال، سنقوم بتنفيذ عملية استعلام من سطر الأوامر. وعلى كلّ حال، قد تودّ إنشاء الاستعلامات من تطبيقك عوضًا عن تنفيذها بشكلٍ مباشر. إذا كنتَ تمتلك صفحة موقع تريد اختبار أدائها، فيجب عليك تنفيذ تلك العملية أو الوصول إلى تلك الصفحة الآن. كمثال، إذا كنتَ تدير موقع تسوّق إلكتروني، قد تودّ إكمال عملية الدفع الآن، والذي من شأنه أن يقوم بطلب جميع الاستعلامات المطلوبة على خادوم قاعدة البيانات. هذه هي عملية الاستعلام التي سنقوم بطلبها على خادوم MySQL الإنتاجي الخاصّ بنا. أولًا، قم باستخدام قاعدة البيانات الصحيحة: USE employees;والآن عملية الاستعلام: SELECT e.first_name, e.last_name, d.dept_name, t.title, t.from_date, t.to_date FROM employees e INNER JOIN dept_emp de ON e.emp_no=de.emp_no INNER JOIN departments d ON de.dept_no=d.dept_no INNER JOIN titles t ON e.emp_no=t.emp_no ORDER BY e.first_name, e.last_name, d.dept_name, t.from_date;الخرج المتوقّع: 489903 rows in set (4.33 sec)سنقوم بتعطيل التسجيل العام عندما تكتمل عملية الاستعلام: SET GLOBAL general_log=0;لاحظ أنّه في حال تركت التسجيل العام مفعّلًا، سيستمر إضافة الاستعلامات إلى السجل، والذي من شأنه جعل الاختبارات أصعب. لذا تأكّد أنّك قمت بتعطيل التسجيل مباشرةً بعد الإنتهاء من اختبارك. فلنتحقق من أنّ ملفّ السجل تمَّ إنشاؤه ضمن المسار var/lib/mysql/: sudo ls -l /var/lib/mysql/capt* -rw-rw----. 1 mysql mysql 861 Sep 24 15:09 /var/lib/mysql/capture_queries.logفلننسخ هذا الملفّ إلى مسار MySQL الاختباري الخاصّ بنا. إذا كنتَ تستخدم خادومًا منفصلًا للاختبار، فقم بنسخه إلى ذلك الخادوم: sudo cp /var/lib/mysql/capture_queries.log /mysqlslap_tutorial/يجب أن يكون هناك بعض البيانات داخل ملفّ السجل هذا. في مثالنا، يجب أن تكون الاستعلامات التي نريدها بالقرب من نهاية الملفّ. تحقق من آخر جزء من الملفّ عبر الأمر: sudo tail /mysqlslap_tutorial/capture_queries.logالخرج المتوقّع: 6294 Query show databases 6294 Query show tables 6294 Field List departments 6294 Field List dept_emp 6294 Field List dept_manager 6294 Field List employees 6294 Field List salaries 6294 Field List titles 140930 15:34:52 6294 Query SELECT e.first_name, e.last_name, d.dept_name, t.title, t.from_date, t.to_date FROM employees e INNER JOIN dept_emp de ON e.emp_no=de.emp_no INNER JOIN departments d ON de.dept_no=d.dept_no INNER JOIN titles t ON e.emp_no=t.emp_no ORDER BY e.first_name, e.last_name, d.dept_name, t.from_date 140930 15:35:06 6294 Query SET GLOBAL general_log=0يظهر هذا السجل أوامر SQL والتوقيت الزمني الخاصّ بها. جملة SQL SELECT الموجودة بالقرب من نهاية الملفّ هي الجزء الذي نحن مهتمّون به. يجب أن يكون بالضبط هو الأمر الذي نقوم بتنفيذه على خادوم قاعدة البيانات الإنتاجية، حيث أننا قمنا من هناك بالتقاطه. في مثالنا هذا، كنّا نعرف عملية الاستعلام بالفعل، ولكن، في بيئة عمل إنتاجية، يمكن أن تكون هذه الطريقة نافعة جدًا لمعرفة الاستعلامات التي ربّما لا تعرف بالضرورة أنّه يتم تشغيلها على خادومك. لاحظ أنّه في حال قمت بطلب استعلامات مختلفة أثناء عملية التسجيل (logging)، فإنّ هذا الملفّ سيبدو مختلفًا تمامًا. في سيناريو حقيقي، يمكن أن يتمّ ملئ هذا الملفّ بالمئات من المُدخَلات القادمة من مختلف الاتصالات. هدفك هو العثور على الاستعلام أو الاستعلامات التي تسبب هذا الحِمل. يمكنك أن تبدأ عبر إنشاء قائمة بكلّ سطر يتضمّن الكلمة "Query". بعدها، ستمتلك قائمةً دقيقة بالاستعلامات التي تمّ تنفيذها على قاعدة البيانات الخاصّة بك أثناء الاختبار. قم بنسخ كلّ استعلام تريد اختباره إلى ملفٍّ ينتهي بالامتداد sql. كمثال: sudo vi /mysqlslap_tutorial/capture_queries.sqlيجب أن تكون المحتويات هي استعلامات MySQL التي تريد اختبارها، دون استخدام أيّ سطور إضافية (استخدم سطر واحد فقط) ودون فاصلة منقوطة بالنهاية: SELECT e.first_name, e.last_name, d.dept_name, t.title, t.from_date, t.to_date FROM employees e INNER JOIN dept_emp de ON e.emp_no=de.emp_no INNER JOIN departments d ON de.dept_no=d.dept_no INNER JOIN titles t ON e.emp_no=t.emp_no ORDER BY e.first_name, e.last_name, d.dept_name, t.from_dateبعدها، تأكّد أنّ نتائج الاستعلامات لم يتم تخزينها في ذاكرة الخبيئة (cache). ارجع إلى جلسة MySQL الخاصّة بالخادوم الاختباري وطبّق الأمر التالي: RESET QUERY CACHE;الآن، صار الوقت المناسب لتشغيل أداة mysqlslap مع ملفّ السكربت. تأكّد أنّك تستخدم اسم ملفّ السكربت الصحيح مع المُعامِل query--. سنستخدم فقط 10 اتصالات افتراضية وسنكرر العمليّة مرّتين فقط. قم بتشغيل الأمر التالي من خادومك الاختباري: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=10 --iterations=2 --create-schema=employees_backup --query="/mysqlslap_tutorial/capture_queries.sql" --verboseيجب أن يبدو خرج اختبار الأداء شيئًا كالتالي: Benchmark Average number of seconds to run all queries: 68.692 seconds Minimum number of seconds to run all queries: 59.301 seconds Maximum number of seconds to run all queries: 78.084 seconds Number of clients running queries: 10 Average number of queries per client: 1إذًا، كيف يمكننا الآن تحسين هذا الأداء؟ ستحتاج معرفةً جيّدة باستعلامات MySQL لتقييم ما تفعله عمليات الاستعلام. بالنظر مجددًا إلى الاستعلامات، يمكننا أن نرى أنّه تقوم بالعديد من عمليات الدمج على امتداد أكثر من جدول، تقوم الاستعلامات بإظهار تواريخ عمل الموظّف وأثناء القيام بذلك، تقوم بدمج أكثر من جدول عبر الحقل empno، كما أنّها تقوم باستخدام الحقل deptno للدمج، ولكن بما أنّه هناك أقسام سجلات قليلة فقط، فسنقوم بتجاهل هذا. بما أنّه هناك العديد من مُدخَلات empno في قاعدة البيانات، فمن المنطقي افتراضي أنّ إنشاء الفهارس في حقل empno يمكن أن يحسّن من أداء عمليات الاستعلام. مع القليل من التمرّن، بمجرّد أن تجد الاستعلامات التي تسبب ضغطًا على خادوم البيانات (وهو القسم الذي سيساعدك mysqlslap فيه!)، ستكون قادرًا على تقييم عمليات الاستعلام المتوفّرة بناءً على معرفتك بـMySQL وقاعدة بياناتك. بعدها، يمكنك محاولة تحسين قاعدة بياناتك أو الاستعلامات التي يتم تنفيذها عليها. في حالتنا، فلنضف الفهارس التي ذكرناه بالأعلى، سنقوم بإنشاء 3 فهارس فارغة لـempno. سيتم إنشاء فهرس واحد في حقل empno في جدول employees، وسيتم إنشاء فهرس آخر في حقل empno بالجدول deptemp، وسيتم إنشاء الأخير في حقل emp_no في جدول titles. فلنذهب إلى جلسة MySQL الاختبارية الخاصّة بنا ونطبّق الأوامر التالية: USE employees_backup; CREATE INDEX employees_empno ON employees(emp_no); CREATE INDEX dept_emp_empno ON dept_emp(emp_no); CREATE INDEX titles_empno ON titles(emp_no);بالعودة إلى نافذة الطرفية الرئيسية على خادومنا الاختباري، إذا قمنا بتشغيل mysqlslap بنفس المُعاملات، فسنرى اختلافًا في نتائج اختبار الأداء: sudo mysqlslap --user=sysadmin --password --host=localhost --concurrency=10 --iterations=2 --create-schema=employees_backup --query="/mysqlslap_tutorial/capture_queries.sql" --verbose Benchmark Average number of seconds to run all queries: 55.869 seconds Minimum number of seconds to run all queries: 55.706 seconds Maximum number of seconds to run all queries: 56.033 seconds Number of clients running queries: 10 Average number of queries per client: 1يمكن أن نرى وجود تحسّن فوري في متوسّط، أدنى وأقصى وقت لتنفيذ عمليات الاستعلام. عوضًا عن متوسّط 68 ثانية، يتم تنفيذ الاستعلامات الآن بغضون 55 ثانية. هذا تحسّن بحوالي 13 ثانية لنفس الاستعلامات التي تمّ تنفيذها. بما أنّ هذا التغيير في قاعدة البيانات أعادة نتيجةً جيّدة في البيئة الاختبارية، فإنّه يمكنك الآن أخذه بعين الاعتبار لتنفيذه على خادوم قاعدة البيانات الإنتاجية الخاصّة بك، ولكن لا تنسى أنّ تغييرات قاعدة البيانات لها دومًا إيجابيات وسلبيات عليك المفاضلة بينها. يمكنك إعادة عملية اختبار الأوامر والتحسينات مع جميع الاستعلامات التي جمعتها من ملفّ السجل الخاصّ بك. استكشاف الأخطاء وإصلاحها: mysqlslap لا يظهر الخرجإذا قمت بتنفيذ أمر اختبار ولم يرجع لك خرجًا، فهذا مؤشّرٌ جيّد إلى أنّ موارد خادومك قد تكون امتلأت بالفعل. قد تتضمّن أعراض هذه المشكلة نقصًا في خرج Benchmark، أو رسالة خطأ مثل: mysqlslap: Error when storing result: 2013 Lost connection to MySQL server during queryربّما قد تودّ إعادة الاختبار مجددًا مع عددٍ أقل للمُعامِل concurrency-- أو iterations--، أو يمكنك محاولة ترقية بيئة خادومك الاختباري لإصلاح المشكلة. يمكن أن يكون هذا طريقةً جيّدة لمعرفة حدود سعة خادوم قاعدة البيانات الخاصّ بك. الخاتمةmysqlslap هو أداة بسيطة وخفيفة يمكنها أن تندمج بسهولة مع محرّك قاعدة بيانات MySQL. وهي متوفّر لجميع إصدارات MySQL بدءًا من الإصدار 5.1.4. في هذا الدرس، رأينا كيفيّة استخدام mysqlslap مع خياراته المتعددة وجرّبنا الأمر مع قاعدة بيانات اختبارية كعيّنة. يمكنك تحميل قواعد بيانات عينية أخرى للاختبار من موقع MySQL والتمرّن عليها أيضًا. كما ذكرنا من قبل: رجاءًا لا تقم بتنفيذ الاختبارات على خادوم قاعدة بيانات إنتاجية. آخر حالة استخدام في درسنا هذا تعلّقت بعملية استعلام واحدة فقط. صحيحٌ أننا قمنا بتحسين أداء عملية الاستعلام تلك عبر إضافة فهارس إضافية إلى جميع الجداول الثلاث، إلّا أنّ العملية قد لا تكون بتلك البساطة في الحياة الحقيقية. إضافة المزيد من الفهارس قد يبطئ - في بعض الأحيان - أداء النظام وغالبًا مع يحتاج مُدراء قواعد البيانات إلى قياس إيجابيات إضافتها على حساب التغيير في الأداء الذي سيحصل. سيناريوهات الاختبار في الحياة الحقيقية أكثر تعقيدًا، ولكن هذا قد يعطيك الأدوات اللازمة للبدء في اختبار وتحسين أداء قاعدة البيانات الخاصّة بك. ترجمة -وبتصرف- للمقال: How To Measure MySQL Query Performance with mysqlslap لصاحبه: Sadequl Hussain.
×
×
  • أضف...