লক পাওয়ার চেষ্টা করার সময় কীভাবে মাইএসকিএল 'ডিডলক পাওয়া যায় তা এড়াতে হবে; লেনদেন পুনরায় চালু করার চেষ্টা করুন '


286

আমার কাছে একটি নির্দোষ ডিবি টেবিল রয়েছে যা অনলাইন ব্যবহারকারীদের রেকর্ড করে। এটি কোন পৃষ্ঠায় রয়েছে সেগুলি এবং সাইটে তাদের শেষ অ্যাক্সেসের তারিখের উপর নজর রাখার জন্য এটি প্রতিটি পৃষ্ঠার রিফ্রেশে আপডেট হয় on আমার তখন একটি ক্রোন রয়েছে যা প্রতি 15 মিনিটের মধ্যে পুরানো রেকর্ড মুছে ফেলতে চালিত হয়।

লক পাওয়ার চেষ্টা করার সময় আমি একটি 'ডেডলক পেয়েছি; গতরাতে প্রায় 5 মিনিটের জন্য লেনদেন পুনরায় চালু করার চেষ্টা করুন এবং এই টেবিলটিতে INSERT গুলি চালানোর সময় এটি প্রদর্শিত হবে। এই ত্রুটি এড়াতে কীভাবে কেউ পরামর্শ দিতে পারেন?

=== সম্পাদনা ===

এখানে চলছে এমন প্রশ্নগুলি:

সাইটে প্রথম ভিজিট করুন:

INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3

প্রতিটি পৃষ্ঠায় রিফ্রেশ করুন:

UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888

প্রতি 15 মিনিটে ক্রোন করুন:

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

এরপরে এটি কিছু পরিসংখ্যান লগ করার জন্য কিছু গণনা করে (যেমন: সদস্যগণ অনলাইনে, অনলাইনে দর্শক)।


আপনি কি টেবিলের কাঠামো সম্পর্কে আরও কিছু বিশদ সরবরাহ করতে পারেন? কোন ক্লাস্টারড বা অবিচ্ছিন্ন সূচক আছে?
অ্যান্ডারস হাবেল

13
dev.mysql.com/doc/refman/5.1/en/innodb-deadlocks.html - "শো ইঞ্জিন ইন্নাডব স্থিতি" চালানো দরকারী ডায়াগনস্টিক্স সরবরাহ করবে।
মার্টিন

ব্যবহারকারীরা যখন কোনও পৃষ্ঠায় পদক্ষেপ নেয় তখন একটি সিঙ্ক্রোনাস ডাটাবেস রাইটিং করা ভাল অভ্যাস নয়। এটি করার সঠিক উপায় হ'ল এটিকে স্মৃতিতে রাখা যেমন মেমক্যাস বা কিছু দ্রুত সারি এবং ক্রোন দিয়ে ডিবিতে লিখতে।
নীড়

উত্তর:


292

একটি সহজ কৌশল যা বেশিরভাগ অচলাবস্থায় সহায়তা করতে পারে সেগুলি একটি নির্দিষ্ট ক্রমে ক্রিয়াকলাপকে বাছাই করা।

দুটি লেনদেন বিপরীত আদেশে দুটি লক লক করার চেষ্টা করা হয় আপনি যখন একটি অচলাবস্থা পেতে, যেমন:

  • সংযোগ 1: লক কী (1), লক কী (2);
  • সংযোগ 2: লক কী (2), লক কী (1);

যদি উভয় একই সময়ে চলতে থাকে তবে সংযোগ 1 টি কী (1) লক করবে, সংযোগ 2 লক কী (2) লক করবে এবং প্রতিটি সংযোগ অপরের জন্য কী -> অচলাবস্থা প্রকাশ করার জন্য অপেক্ষা করবে।

এখন, আপনি যদি আপনার প্রশ্নের পরিবর্তন করে থাকেন যে সংযোগগুলি একই ক্রমে কীগুলি লক করে রাখে, অর্থাত:

  • সংযোগ 1: লক কী (1), লক কী (2);
  • সংযোগ 2: লক কী ( 1 ), লক কী ( 2 );

অচলাবস্থা পাওয়া সম্ভব হবে না।

সুতরাং এই আমি পরামর্শ:

  1. মুছে ফেলা বিবৃতি ব্যতীত আপনার কাছে এমন কোনও জিজ্ঞাসা নেই যা একবারে একাধিক কী লক করে রাখে। যদি আপনি করেন (এবং আমি সন্দেহ করি যে আপনি করেন) তবে তাদের যেখানে (কে 1, কে 2, .. ন) তে আরোহণের ক্রমে অর্ডার করুন।

  2. আরোহী ক্রমে কাজ করতে আপনার মোছার বিবৃতিটি ঠিক করুন:

পরিবর্তন

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

প্রতি

DELETE FROM onlineusers WHERE id IN (SELECT id FROM onlineusers
    WHERE datetime <= now() - INTERVAL 900 SECOND order by id) u;

আরেকটি বিষয় মনে রাখবেন যে মাইএসকিএল ডকুমেন্টেশনগুলি সুপারিশ করে যে কোনও অচলাবস্থার ক্ষেত্রে ক্লায়েন্টটি স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করা উচিত। আপনি আপনার ক্লায়েন্ট কোডে এই যুক্তি যুক্ত করতে পারেন। (বলুন, হাল ছেড়ে দেওয়ার আগে 3 এই বিশেষ ত্রুটির দিকে চেষ্টা করে)।


2
আমার যদি লেনদেন হয় (অটোকোমিট = মিথ্যা), ডেডলক ব্যতিক্রম ছোঁড়ে। কেবল একই স্টেটমেন্টটি আবার চেষ্টা করার জন্য কি যথেষ্ট? এক্সেকিউটআপডেট () বা পুরো লেনদেন এখন জিম্পড এবং রোলব্যাক করা উচিত + এতে যা চলছে তার সবই আবার চালু করা উচিত?
যিনি

5
যদি আপনার লেনদেন সক্ষম থাকে তবে এটি সব কিছুই বা কিছুই নয়। আপনার যদি কোনও ধরণের ব্যতিক্রম থাকে তবে এটির গ্যারান্টিযুক্ত যে পুরো লেনদেনের কোনও প্রভাব ছিল না। সেক্ষেত্রে আপনি পুরো জিনিসটি আবার চালু করতে চান।
ওমরি ইয়াদান

4
বিশাল টেবিলের একটি নির্বাচনের উপর ভিত্তি করে একটি মোছা সাধারণ মুছার চেয়ে খুব ধীর
থের্মেক

3
আপনাকে অনেক ধন্যবাদ, বন্ধু। 'সাজানো বিবৃতি' পরামর্শটি আমার মৃত লক সংক্রান্ত সমস্যাগুলি স্থির করে।
মিয়ার

4
@ ওমরিয়াদান আমি যা জানি তা থেকে, মাইএসকিউএল-তে আপনি একই টেবিল থেকে কোন উপ-বিভাগে নির্বাচন করতে পারবেন না যেখানে আপনি আপডেট করছেন DATE dev.mysql.com/doc/refman/5.7/en/update.html
আর্টএক্সেরেক্স

72

দুটি লেনদেন একে অপরের জন্য লক অর্জনের জন্য অপেক্ষা করলে ডিডলক ঘটে happen উদাহরণ:

  • টিএক্স 1: লক এ, তারপরে বি
  • টিএক্স 2: ল কে বি, তারপরে এ

ডেডলকগুলি সম্পর্কে অসংখ্য প্রশ্নোত্তর রয়েছে। প্রতিবার আপনি একটি সারি সন্নিবেশ / আপডেট / বা মুছলে প্রতিবার একটি লক অর্জিত হয়। অচলাবস্থা এড়াতে, আপনাকে অবশ্যই নিশ্চিত করতে হবে যে সমবর্তী লেনদেনগুলি এমন ক্রমে সারিতে আপডেট হয় না যার ফলে কোনও অচলাবস্থার সৃষ্টি হতে পারে। সাধারণভাবে বলতে গেলে, বিভিন্ন লেনদেনে এমনকি সর্বদা একই ক্রমে লক অর্জন করার চেষ্টা করুন (যেমন সর্বদা সর্বদা প্রথম টেবিল এ, তারপরে টেবিল বি)।

ডাটাবেসে ডেডলকের আরও একটি কারণ হ'ল সূচিগুলি অনুপস্থিত । যখন একটি সারি সন্নিবেশ করা / আপডেট / মুছে ফেলা হয়, তখন ডাটাবেসটিকে সম্পর্কিত সীমাবদ্ধতাগুলি পরীক্ষা করা প্রয়োজন, এটি নিশ্চিত করুন যে সম্পর্কগুলি সুসংগত। এটি করতে, ডাটাবেসগুলিকে সম্পর্কিত সারণীতে বিদেশী কীগুলি পরীক্ষা করা দরকার। এটি পরিবর্তিত সারির চেয়ে অন্য লকটি অধিগ্রহণের ফলাফল হতে পারে । বিদেশী কীগুলিতে (এবং অবশ্যই প্রাথমিক কীগুলি) সর্বদা সূচি রাখার বিষয়ে নিশ্চিত হন, অন্যথায় এটি সারি লকের পরিবর্তে একটি টেবিল লক তৈরি করতে পারে । যদি টেবিল লকটি ঘটে থাকে তবে লকের লড়াইটি বেশি এবং অচলাবরণের সম্ভাবনা বৃদ্ধি পায়।


3
সুতরাং সম্ভবত আমার সমস্যাটি হ'ল ব্যবহারকারী পৃষ্ঠাটি রিফ্রেশ করেছে এবং একইভাবে ক্রোনটি রেকর্ডটিতে একটি মুছে ফেলার চেষ্টা করছে একই সময়ে একটি রেকর্ডের একটি আপডেটের ট্রিগার করে। যাইহোক, আমি INSERTS এ ত্রুটি পেয়েছি, যাতে ক্রোনটি সবে তৈরি করা রেকর্ডগুলি মুছে ফেলা হবে না। সুতরাং যে recordোকানো এখনও রেকর্ডে একটি অচলাবস্থা ঘটতে পারে?
ডেভিড

আপনি কি টেবিল (গুলি) এবং লেনদেনগুলি ঠিক কী সম্পর্কে কিছুটা আরও তথ্য সরবরাহ করতে পারেন?
ইওর্নলি

প্রতি লেনদেনের জন্য কেবলমাত্র একটি বিবৃতি দেওয়া থাকলে আমি কীভাবে অচলাবস্থার ঘটনা ঘটতে দেখছি না। অন্যান্য টেবিলগুলিতে কোনও অপারেশন নেই? কোনও বিশেষ বিদেশী কী বা অনন্য বাধা নেই? কোনও ক্যাসকেড সীমাবদ্ধতা মুছবে না?
ইওর্নলি

নাহ, অন্য কিছু বিশেষ নয় ... আমি মনে করি এটি টেবিলের ব্যবহারের প্রকৃতি অনুসারে রয়েছে। দর্শনার্থীর কাছ থেকে প্রতিটি পৃষ্ঠা রিফ্রেশ করে একটি সারি সন্নিবেশ করা / আপডেট করা হচ্ছে। প্রায় একসময় 1000+ দর্শক যেকোন এক সময় চালু রয়েছে।
ডেভিড

12

সম্ভবত মুছে ফেলা বিবৃতিটি টেবিলের মোট সারিগুলির একটি বৃহত ভগ্নাংশকে প্রভাবিত করবে। অবশেষে এটি মুছে ফেলার সময় টেবিল লকটি অর্জন করা হতে পারে। একটি লক ধরে রাখা (এই ক্ষেত্রে সারি- বা পৃষ্ঠা লকগুলি) এবং আরও লকগুলি অর্জন করা সর্বদা একটি অচলাবস্থার ঝুঁকি। তবে আমি কেন ব্যাখ্যা করতে পারি না যে সন্নিবেশ বিবৃতিটি কোনও লক বর্ধনের দিকে নিয়ে যায় - এটি পৃষ্ঠা বিভক্তকরণ / যুক্তকরণের সাথে থাকতে পারে তবে মাইএসকিউএল আরও ভালভাবে জানা কেউ সেখানে পূরণ করতে হবে।

শুরু করার জন্য মুছে ফেলার বিবরণীর জন্য সরাসরি টেবিল লকটি অর্জন করার চেষ্টা করা উপযুক্ত। দেখুন লক টেবিল এবং টেবিল লকিং বিষয়


6

deleteএই সিউডোকোডের মতো টেম্পল টেবিলে মুছে ফেলার জন্য প্রতিটি সারিটির কীটি প্রথমে serুকিয়ে আপনি সেই কাজটি চালানোর চেষ্টা করতে পারেন

create temporary table deletetemp (userid int);

insert into deletetemp (userid)
  select userid from onlineusers where datetime <= now - interval 900 second;

delete from onlineusers where userid in (select userid from deletetemp);

এটির মতো ব্রেক করা কম কার্যকর তবে এটি চলাকালীন কী-রেঞ্জের লকটি ধরে রাখার প্রয়োজনীয়তা এড়িয়ে যায় delete

এছাড়াও, 900 সেকেন্ডের চেয়ে পুরানো সারিগুলি বাদ দিয়ে selectএকটি whereধারা যুক্ত করতে আপনার প্রশ্নের সংশোধন করুন । এটি ক্রোন কাজের উপর নির্ভরতা এড়িয়ে যায় এবং আপনাকে এটি প্রায়শই কম চালানোর জন্য পুনঃনির্ধারিত করতে দেয়।

ডেডলকগুলি সম্পর্কে তত্ত্ব: আমার মাইএসকিউএল তে অনেক পটভূমি নেই তবে এখানে যায় ... লেনদেনের মাঝামাঝি সময়ে deleteএর whereধারাটি মেলে সারিগুলি সারি আটকাতে প্রতিরোধ করার জন্য, ডেটটাইমের জন্য একটি কী-রেঞ্জের লক ধরে রাখতে চলেছে , এবং এটি মোছার জন্য সারিগুলি খুঁজে পাওয়ার সাথে সাথে এটি প্রতিটি পৃষ্ঠায় এটি সংশোধন করে একটি লক অর্জন করার চেষ্টা করবে। insertপৃষ্ঠাটি মধ্যে ঢোকাতে হয় একটি লক অর্জন যাচ্ছে, এবং তারপর কি লক অর্জন করতে যাবেন। সাধারণত insertকীটি লকটি খোলার জন্য ধৈর্য সহকারে অপেক্ষা করবে তবে এটি যদি deleteএকই পৃষ্ঠাটি insertব্যবহার করছে যা লক করার চেষ্টা করে তবে এটি অচল হয়ে যাবে কারণ সেই deleteপৃষ্ঠাটির লক প্রয়োজন এবং insertসেই কী লকটির প্রয়োজন। এটি সন্নিবেশকারীদের জন্য ঠিক বলে মনে হচ্ছে না, deleteএবংinsert ডেটটাইম রেঞ্জগুলি ব্যবহার করছে যা ওভারল্যাপ করে না তাই সম্ভবত অন্য কোনও কিছু চলছে।

http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html


4

যদি কেউ এখনও এই সমস্যা নিয়ে লড়াই করে চলেছে:

আমি অনুরূপ সমস্যার মুখোমুখি হয়েছিলাম যেখানে একই সাথে 2 টি অনুরোধগুলি সার্ভারে হিট হচ্ছে। নীচের মতো পরিস্থিতি ছিল না:

T1:
    BEGIN TRANSACTION
    INSERT TABLE A
    INSERT TABLE B
    END TRANSACTION

T2:
    BEGIN TRANSACTION
    INSERT TABLE B
    INSERT TABLE A
    END TRANSACTION

সুতরাং, আমি হতবাক হয়েছি কেন অচলাবস্থা হচ্ছে।

তারপরে আমি দেখতে পেলাম যে বিদেশী চাবির কারণে 2 টি টেবিলের মধ্যে পিতৃ সন্তানের সাথে সম্পর্কিত সম্পর্ক রয়েছে। আমি যখন শিশু টেবিলে একটি রেকর্ড সন্নিবেশ করছিলাম তখন লেনদেনটি পিতামাতার টেবিলের সারিটিতে একটি লক অর্জন করছিল। এর পরপরই আমি প্যারেন্ট সারিটি আপডেট করার চেষ্টা করছিলাম যা লকটির উচ্চতাটিকে এক্সক্লুসিভ একটিতে ট্রিগার করছে। যেহেতু ২ য় সমবর্তী লেনদেন ইতিমধ্যে একটি ভাগ করা লক ধরেছিল তাই এটি অচলাবস্থার কারণ হচ্ছিল।

দেখুন: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html


আমার ক্ষেত্রেও দেখে মনে হচ্ছে সমস্যাটি একটি বিদেশী কী সম্পর্ক। ধন্যবাদ 1
ক্রিস প্রিন্স

3

স্প্রিং ব্যবহার করে জাভা প্রোগ্রামারদের জন্য, আমি এওপি দিকটি ব্যবহার করে এই সমস্যাটি এড়ানো করেছি যা ক্ষণস্থায়ী ডেডলকগুলিতে স্বয়ংক্রিয়ভাবে লেনদেনগুলি পুনরায় চেষ্টা করে।

আরও তথ্যের জন্য @ রিটারি ট্রান্সজেকশন জাভাদোক দেখুন ।


0

আমার একটি পদ্ধতি রয়েছে, যার অভ্যন্তরীণগুলি মাইএসকিউএল ট্রান্সজেকশনে আবৃত।

আমার সাথে সমান্তরালে একই পদ্ধতি চালানোর সময় অচলাবস্থার বিষয়টি আমার কাছে উপস্থিত হয়েছিল।

পদ্ধতির একক দৃষ্টান্তটি চালানোর ক্ষেত্রে কোনও সমস্যা নেই।

আমি যখন মাইএসকিএলটিআরসিএশন সরিয়েছি তখন আমি কোনও সমস্যা ছাড়াই নিজের সাথে সমান্তরালে পদ্ধতিটি চালাতে সক্ষম হয়েছি।

শুধু আমার অভিজ্ঞতা ভাগ করে নিচ্ছি, আমি কোনও কিছুর পক্ষে কথা বলছি না।


0

cronবিপদজনক. পরবর্তী ক্রোনার আগে ক্রোনটির একটি উদাহরণ শেষ করতে ব্যর্থ হলে তারা একে অপরের সাথে লড়াইয়ের সম্ভাবনা রয়েছে।

অবিচ্ছিন্নভাবে কাজ করা ভাল যা কিছু সারি মুছে ফেলবে, কিছু ঘুমাবে, আবার পুনরাবৃত্তি করবে।

এছাড়াও, INDEX(datetime)অচলাবস্থা এড়াতে খুব গুরুত্বপূর্ণ।

তবে, যদি ডেটটাইম পরীক্ষায় 20% টেবিলের বেশি থাকে তবে এটি DELETEএকটি টেবিল স্ক্যান করবে। ক্ষুদ্রতর অংশগুলি প্রায়শই মুছে ফেলা হ'ল একটি কাজ।

ছোট অংশগুলির সাথে যাওয়ার আরেকটি কারণ হ'ল কম সারি লক করা।

শেষের সারি:

  • INDEX(datetime)
  • ক্রমাগত চলমান কাজ - মুছুন, এক মিনিট ঘুমান, পুনরাবৃত্তি করুন।
  • উপরের কাজটি মারা যায় নি তা নিশ্চিত করার জন্য, ক্রোন জব করুন যার একমাত্র উদ্দেশ্য ব্যর্থতার পরে এটি পুনরায় চালু করা।

অপসারণের অন্যান্য কৌশল: http://mysql.rjweb.org/doc.php/deletebig

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.