سؤال

لقد وجدت مؤخرا وثابت الخطأ في موقع كنت أعمل على ذلك أدى إلى ملايين الصفوف المكررة من البيانات في جدول سيكون كبيرا جدا حتى بدونها (لا يزال في الملايين). يمكنني بسهولة العثور على هذه الصفوف المكررة ويمكن تشغيل استعلام حذف واحد لقتلهم جميعا. المشكلة هي أن محاولة حذف هذه الصفوف العديدة في طلقة واحدة تقوم بإحباط الطاولة لفترة طويلة، والتي أود تجنبها إن أمكن. الطرق الوحيدة التي يمكنني رؤيتها للتخلص من هذه الصفوف، دون إيقاف الموقع (عن طريق قفل الجدول) هي:

  1. اكتب نصي سينفذ الآلاف من استفسارات حذف أصغر في حلقة. سيتجول هذا من الناحية النظرية حول مشكلة الجدول المقفلة لأن الاستفسارات الأخرى ستتمكن من تحقيق ذلك في قائمة الانتظار وتشغيلها بين الحذف. ولكن ما زالت تسبق الحمل على قاعدة البيانات قليلا جدا وسوف يستغرق التشغيل لفترة طويلة.
  2. إعادة تسمية الجدول وإعادة إنشاء الجدول الحالي (سيكون الآن فارغا). ثم قم بتنظيف بلدي على الطاولة المعاد تسميته. إعادة تسمية الجدول الجديد، اسم القديم مرة أخرى ودمج الصفوف الجديدة في الجدول المعاد تسميته. هذه طريقة أكثر بكثير من الخطوات، ولكن يجب أن تنجز المهمة مع الحد الأدنى من انقطاع. الجزء الصعود الوحيد هنا هو أن الجدول المعني هو جدول إعداد التقارير، لذلك بمجرد إعادة تسميته بعيدا عن الطريق ووضع واحد فارغ في مكانه، تختفي جميع التقارير التاريخية حتى أعودها في مكانها. بالإضافة إلى عملية الاندماج قد تكون قليلا من الألم بسبب نوع البيانات التي يتم تخزينها. عموما هذا هو خياري المحتمل الآن.

كنت أتساءل فقط إذا كان لدى أي شخص آخر هذه المشكلة من قبل، وإذا كان الأمر كذلك، فكيف تتعامل معها دون إلقاء أسفل الموقع، ونأمل أن يكون الحد الأدنى إذا أي مقاطعة للمستخدمين؟ إذا ذهبت مع رقم 2، أو طريقة مختلفة، مماثلة، يمكنني جدولة الأشياء التي يجب تشغيلها في وقت متأخر من الليل والقيام بالاندمج في وقت مبكر من صباح اليوم التالي ودع المستخدمين يعرفون وقتا طويلا، لذلك هذا ليس صفقة ضخمة. أنا فقط أتطلع إلى معرفة ما إذا كان لدى أي شخص أي أفكار لأفضل، أو أسهل، للقيام بالتنظيف.

هل كانت مفيدة؟

المحلول

DELETE FROM `table`
WHERE (whatever criteria)
ORDER BY `id`
LIMIT 1000

اغسل، شطف، كرر حتى تتأثر الصفوف الصفرية. ربما في برنامج نصي ينام لمدة ثانية أو ثلاثة بين التكرارات.

نصائح أخرى

أود أيضا أن أوصي بإضافة بعض القيود إلى طاولتك للتأكد من أن هذا لا يحدث لك مرة أخرى. سيستغرق مليون صف صفوف، عند 1000 لكل طلقة، 1000 تكرار من البرنامج النصي لإكماله. إذا تم تشغيل البرنامج النصي مرة واحدة كل 3.6 ثانية، فسيتم القيام به في غضون ساعة. لا قلق. من غير المرجح أن يلاحظ عملاؤك.

يحذف ما يلي 1،000،000 سجل، واحد في وقت واحد.

 for i in `seq 1 1000`; do 
     mysql  -e "select id from table_name where (condition) order by id desc limit 1000 " | sed 's;/|;;g' | awk '{if(NR>1)print "delete from table_name where id = ",$1,";" }' | mysql; 
 done

يمكنك تجميعها معا والقيام بحذف Table_Name حيث في (ID1، ID2، .. IDN) أنا متأكد من صعوبة ث / الكثير

كان لدي حالة استخدام حذف صفوف 1M + في جدول صفوف 25 مترا + في MySQL. حاول مناهج مختلفة مثل حذف الدفعات (الموصوفة أعلاه).
لقد اكتشفت أن أسرع طريقة (نسخة من السجلات المطلوبة إلى جدول جديد):

  1. إنشاء جدول مؤقت يحمل معرفات فقط.

إنشاء جدول ID_TEMP_TABLE (TEMP_ID INT)؛

  1. إدراج معرفات يجب إزالتها:

إدراج في ID_TEMP_TABLE (TEMP_ID) حدد .....

  1. إنشاء جدول جدول جديد_new

  2. أدخل جميع السجلات من الجدول إلى Table_New دون صفوف غير ضرورية موجودة في ID_TEMP_TABLE

إدراج في table_new .... حيث لا يقوم الجدول_ID (حدد متميزة (temp_id) من ID_TEMP_TABLE)؛

  1. إعادة تسمية الجداول

استغرق العملية برمتها ~ 1 ساعة. في حالة استخدام بلدي حذف بسيط من الدفعة على 100 سجل استغرق 10 دقائق.

كنت أستخدم MK-Archiver. من ممتاز ماتكيت حزمة المرافق (مجموعة من البرامج النصية بيرل لإدارة MySQL) Maatkit هي من Baron Schwartz، ومؤلف كتاب O'Reilly "High Performance Mysql".

الهدف هو وظيفة منخفضة التأثير، إلى الأمام فقط لإخراج البيانات القديمة من الجدول دون التأثير على استعلامات OLTP كثيرا. يمكنك إدراج البيانات في جدول آخر، والتي لا تحتاج إلى أن تكون على نفس الخادم. يمكنك أيضا كتابةه إلى ملف بتنسيق مناسب لتحميل بيانات Infile. أو لا يمكنك القيام به، وفي هذه الحالة، مجرد حذف تدريجي.

لقد تم بناؤها بالفعل لأرشفة صفوفك غير المرغوب فيها في دفعات صغيرة وكافأة، فيمكنها حفظ الصفوف المحذوفة إلى ملف في حالة تثبيت الاستعلام الذي يحدد الصفوف لإزالتها.

لا يوجد تثبيت مطلوب، مجرد الاستيلاء http://www.maatkit.org/get/mk-archiver. وتشغيل بيرلدوك حوله (أو اقرأ موقع الويب) للوثائق.

واجهت مشكلة مماثلة. كان لدينا طاولة كبيرة حقا، حوالي 500 جيجابايت في الحجم مع عدم وجود تقسيم واحد فهرس واحد فقط على عمود Primary_Key. كان سيدنا هكويت من آلة، 128 كلاص و 512 العربات من ذاكرة الوصول العشوائي وكان لدينا عبيد متعددة أيضا. لقد حاولنا بعض التقنيات لمعالجة الحذف على نطاق واسع من الصفوف. سأرسلهم جميعا هنا من الأسوأ إلى أفضل ما وجدناه

  1. جلب وحذف صف واحد في وقت واحد. هذا هو الأسوأ المطلق الذي يمكنك القيام به. لذلك، لم نحاول ذلك حتى ذلك.
  2. جلب أول صفوف "x" من قاعدة البيانات باستخدام استعلام الحد على عمود Primer_Key، ثم التحقق من معرفات الصف للحذف في التطبيق وإطلاق استعلام حذف واحد مع قائمة بمعرفات Primery_key. لذلك، 2 استفسار لكل صفوف "س". الآن، كان هذا النهج جيدا ولكن القيام بذلك باستخدام وظيفة دفاعية حذفت حوالي 5 ملايين صفوف في 10 دقائق أو نحو ذلك، بسبب التي تتخلف عن عبيد mysql db بمقدار 105 ثانية. تأخر 105 ثانية في نشاط 10 دقائق. لذلك، كان علينا أن نتوقف.
  3. في هذه التقنية، أدخلنا 50 مللي ثانية تأخر بين جلب الدفعة اللاحقة وحذف حجم "X". تم حل هذا مشكلة التأخر ولكننا نجذ الآن 1.2-1.3 مليون صف لكل 10 دقائق مقارنة ب 5 ملايين تقنية رقم 2.
  4. تقسيم جدول قاعدة البيانات ثم حذف الأقسام بأكملها عند عدم الحاجة إليها. هذا هو أفضل حل لدينا، لكنه يتطلب جدولا مسبقا. تابعنا الخطوة 3 لأننا كان لدينا طاولة قديمة غير مقسمة للغاية بفهرسة فقط على عمود Primary_Key. كان إنشاء قسم قد استغرق الكثير من الوقت ونحن في وضع الأزمات. فيما يلي بعض الروابط المتعلقة بالتقسيم الذي وجدت مفيدا مرجع MySQL الرسمي, تقسيم أوراكل ديسيبل يوميا.

لذلك، IMO، إذا كنت تستطيع تحمل تكاليف إنشاء قسم في طاولتك، انتقل إلى الخيار رقم 4، وإلا، فأنت عالق مع الخيار رقم 3.

تفعل ذلك في دفعات من دعونا يقول 2000 صفوف في وقت واحد. ارتكاب بينهما. مليون صف أليس كذلك وهذا سيكون سريعا، إلا إذا كان لديك العديد من الفهارس على الطاولة.

بحسب ال وثائق mysql, TRUNCATE TABLE هو بديل سريع ل DELETE FROM. وبعد جرب هذا:

اقتطاع الجدول table_name.

حاولت هذا على صفوف 50 متر وتمته في غضون دقيقتين.

ملاحظة: عمليات اقتطاع ليست آمنة المعاملات؛ يحدث خطأ عند محاولة واحدة في سياق معاملة نشطة أو قفل الجدول النشط

بالنسبة لنا، DELETE WHERE %s ORDER BY %s LIMIT %d لم يكن الجواب خيارا، لأن المعايير التي كانت بطيئة (عمود غير مفهرس)، وسوف تضغط على السيد.

اختر من قائمة "قراءة نسخة" قائمة من المفاتيح الأساسية التي ترغب في حذفها. تصدير مع هذا النوع من التنسيق:

00669163-4514-4B50-B6E9-50BA232CA5EB
00679DE5-7659-4CD4-A919-6426A2831F35

استخدم البرنامج النصي باش التالي للاستيلاء على هذا الإدخال والفصل إلى عبارات حذف يتطلب باش ≥ 4 بسبب mapfile مدمج]:

sql-chunker.sh (تذكر أن chmod +x أنا، وتغيير shebang للإشارة إلى باش 4 القابل للتنفيذ):

#!/usr/local/Cellar/bash/4.4.12/bin/bash

# Expected input format:
: <<!
00669163-4514-4B50-B6E9-50BA232CA5EB
00669DE5-7659-4CD4-A919-6426A2831F35
!

if [ -z "$1" ]
  then
    echo "No chunk size supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi

if [ -z "$2" ]
  then
    echo "No file supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi

function join_by {
    local d=$1
    shift
    echo -n "$1"
    shift
    printf "%s" "${@/#/$d}"
}

while mapfile -t -n "$1" ary && ((${#ary[@]})); do
    printf "DELETE FROM my_cool_table WHERE id IN ('%s');\n" `join_by "','" "${ary[@]}"`
done < "$2"

استدعاء مثل ذلك:

./sql-chunker.sh 1000 ids.txt > batch_1000.sql

هذا سيمنحك ملفا مع إخراج مهيأ مثل هذا (لقد استخدمت حجم دفعة من 2):

DELETE FROM my_cool_table WHERE id IN ('006CC671-655A-432E-9164-D3C64191EDCE','006CD163-794A-4C3E-8206-D05D1A5EE01E');
DELETE FROM my_cool_table WHERE id IN ('006CD837-F1AD-4CCA-82A4-74356580CEBC','006CDA35-F132-4F2C-8054-0F1D6709388A');

ثم تنفيذ العبارات مثل ذلك:

mysql --login-path=master billing < batch_1000.sql

لأولئك غير مألوفين login-path, ، إنه مجرد اختصار لتسجيل الدخول دون كتابة كلمة المرور في سطر الأوامر.

أعتقد أن البطء يرجع إلى "فهرس متفاوت المسافات" في MySQL حيث يتم تخزين السجلات الفعلية ضمن فهرس المفتاح الأساسي - حسب ترتيب مؤشر المفتاح الأساسي. هذا يعني الوصول إلى سجل عبر المفتاح الأساسي سريع للغاية لأنه يتطلب فقط إحضار قرص واحد لأن السجل الموجود على القرص موجود هناك حيث وجدت المفتاح الأساسي الصحيح في الفهرس.

في قواعد البيانات الأخرى دون فهارس متفاوت المسافات، لا يحمل الفهرس نفسه السجل ولكن مجرد "إزاحة" أو "موقع" يشير إلى المكان الذي يوجد فيه السجل في ملف الجدول، ثم يجب إجراء جلب آخر في هذا الملف لاسترداد البيانات الفعلية وبعد

يمكنك تخيل عند حذف سجل في فهرس متفاوت المسافات يتم نقل جميع السجلات أعلاه إلى أسفل إلى تجنب الثقوب الضخمة التي يتم إنشاؤها في الفهرس (حسنا، ما أذكره من قبل بضع سنوات على الأقل - إصدارات أحدث ربما قد غيرت هذا).

معرفة ما وجدناه ما وجدناه حقا حذف في MySQL هو إجراء حذف في ترتيب عكسي. هذا ينتج أقل قدر من الحركة القياسية لأنك تحذف السجلات من النهاية أي معنى لاحقة أن الحذف اللاحق لها كائنات أقل للانتقال.

لم أقم بتسجيل أي شيء للقيام بهذا، والقيام بذلك بشكل صحيح ستحتاج إلى برنامج نصي تماما، ولكن خيار آخر هو إنشاء جدول جديد ومكرر وحدد جميع الصفوف التي تريد الاحتفاظ بها فيها. استخدم الزناد للحفاظ عليه حتى الآن أثناء اكتمال هذه العملية. عندما يكون بمزامنة (ناقص الصفوف التي تريد إسقاطها)، أعد تسمية الجداول في المعاملة، بحيث يأخذ المرء الجديد مكان القديم. إسقاط الجدول القديم، وفويلا!

هذا (من الواضح) يتطلب الكثير من مساحة القرص الإضافية، وقد ضريبة موارد الإدخال / الإخراج، ولكن خلاف ذلك، يمكن أن يكون أسرع بكثير.

اعتمادا على طبيعة البيانات أو في حالة الطوارئ، يمكنك إعادة تسمية الجدول القديم وإنشاء جدول جديد وفريغ في مكانه، وحدد صفوف "الاحتفاظ" في الجدول الجديد في الترفيه الخاص بك ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top