মাইএসকিউএল InnoDB এমনকি রিড সংস্থায় মুছে ফেলার প্রাথমিক কীটি লক করে


11

মুখবন্ধ

আমাদের অ্যাপ্লিকেশনটি বেশ কয়েকটি থ্রেড চালায় যা DELETEসমান্তরালভাবে প্রশ্নগুলি চালায় । প্রশ্নগুলি বিচ্ছিন্ন ডেটাগুলিকে প্রভাবিত করে, অর্থাত DELETEপৃথক থ্রেড থেকে একই সারিগুলিতে সমবর্তী হওয়ার সম্ভাবনা নেই । যাইহোক, প্রতি ডকুমেন্টেশন মাইএসকিউএল DELETEবিবৃতিগুলির জন্য তথাকথিত 'নেক্সট-কী' লক ব্যবহার করে , যা মিলের কী এবং কিছু ফাঁক উভয়কেই লক করে। এই জিনিসটি মৃত-লকগুলিতে নিয়ে যায় এবং একমাত্র সমাধান যা আমরা পেয়েছি তা হল READ COMMITTEDবিচ্ছিন্নতা স্তর ব্যবহার করা ।

সমস্যাটি

বিশাল টেবিলগুলির DELETEসাথে জটিল বিবৃতি কার্যকর করার সময় সমস্যা দেখা দেয় JOIN। একটি বিশেষ ক্ষেত্রে আমাদের কাছে একটি সতর্কতা সহ একটি টেবিল রয়েছে যাতে মাত্র দুটি সারি রয়েছে, তবে ক্যোয়ারিতে দুটি পৃথক INNER JOINএড টেবিল থেকে কিছু নির্দিষ্ট সত্তার সাথে সম্পর্কিত সমস্ত সতর্কতাগুলি ফেলে দেওয়া দরকার । কোয়েরিটি নিম্নরূপ:

DELETE pw 
FROM proc_warnings pw 
INNER JOIN day_position dp 
   ON dp.transaction_id = pw.transaction_id 
INNER JOIN ivehicle_days vd 
   ON vd.id = dp.ivehicle_day_id 
WHERE vd.ivehicle_id=? AND dp.dirty_data=1

যখন দিন_ অবস্থানের টেবিলটি যথেষ্ট বড় হয় (আমার পরীক্ষার ক্ষেত্রে 1448 সারি রয়েছে) তবে READ COMMITTEDবিচ্ছিন্নতা মোডের সাথে কোনও লেনদেন পুরো proc_warnings টেবিলটিকে অবরুদ্ধ করে ।

এই নমুনা ডেটাতে ইস্যুটি সর্বদা পুনরুত্পাদন করা হয় - http://yadi.sk/d/QDuwBtpW1BxB9 উভয়ই মাইএসকিউএল 5.1 (5.1.59-এ চেক করা হয়েছে) এবং মাইএসকিউএল 5.5 (মাইএসকিউএল 5.5.24 এ পরীক্ষিত)।

সম্পাদনা: লিঙ্কযুক্ত নমুনা ডেটাতে কোয়েরি সারণীর জন্য স্কিমা এবং সূচি রয়েছে, সুবিধার্থে এখানে পুনরুত্পাদন করা:

CREATE TABLE  `proc_warnings` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `transaction_id` int(10) unsigned NOT NULL,
    `warning` varchar(2048) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `proc_warnings__transaction` (`transaction_id`)
);

CREATE TABLE  `day_position` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `transaction_id` int(10) unsigned DEFAULT NULL,
    `sort_index` int(11) DEFAULT NULL,
    `ivehicle_day_id` int(10) unsigned DEFAULT NULL,
    `dirty_data` tinyint(4) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `day_position__trans` (`transaction_id`),
    KEY `day_position__is` (`ivehicle_day_id`,`sort_index`),
    KEY `day_position__id` (`ivehicle_day_id`,`dirty_data`)
) ;

CREATE TABLE  `ivehicle_days` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `d` date DEFAULT NULL,
    `sort_index` int(11) DEFAULT NULL,
    `ivehicle_id` int(10) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `ivehicle_days__is` (`ivehicle_id`,`sort_index`),
    KEY `ivehicle_days__d` (`d`)
);

লেনদেনের জন্য প্রশ্নগুলি নিম্নরূপ:

  • লেনদেন ঘ

    set transaction isolation level read committed;
    set autocommit=0;
    begin;
    DELETE pw 
    FROM proc_warnings pw 
    INNER JOIN day_position dp 
        ON dp.transaction_id = pw.transaction_id 
    INNER JOIN ivehicle_days vd 
        ON vd.id = dp.ivehicle_day_id 
    WHERE vd.ivehicle_id=2 AND dp.dirty_data=1;
    
  • লেনদেন 2

    set transaction isolation level read committed;
    set autocommit=0;
    begin;
    DELETE pw 
    FROM proc_warnings pw 
    INNER JOIN day_position dp 
        ON dp.transaction_id = pw.transaction_id 
    INNER JOIN ivehicle_days vd 
        ON vd.id = dp.ivehicle_day_id 
    WHERE vd.ivehicle_id=13 AND dp.dirty_data=1;
    

এর মধ্যে একটি সর্বদা 'লক ওয়েট টাইমআউট অতিক্রম করে ...' ত্রুটি দিয়ে ব্যর্থ হয়। information_schema.innodb_trxনিম্নলিখিত সারি রয়েছে:

| trx_id     | trx_state   | trx_started           | trx_requested_lock_id  | trx_wait_started      | trx_wait | trx_mysql_thread_id | trx_query |
| '1A2973A4' | 'LOCK WAIT' | '2012-12-12 20:03:25' | '1A2973A4:0:3172298:2' | '2012-12-12 20:03:25' | '2'      | '3089'              | 'DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1' |
| '1A296F67' | 'RUNNING'   | '2012-12-12 19:58:02' | NULL                   | NULL | '7' | '3087' | NULL |

information_schema.innodb_locks

| lock_id                | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
| '1A2973A4:0:3172298:2' | '1A2973A4'  | 'X'       | 'RECORD'  | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
| '1A296F67:0:3172298:2' | '1A296F67'  | 'X'       | 'RECORD'  | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |

যেহেতু আমি দেখতে পাচ্ছি উভয় Xপ্রশ্নই প্রাথমিক কী = 53 দিয়ে একটি সারিতে একচেটিয়া লক চায় However তবে, তাদের উভয়েরই অবশ্যই proc_warningsটেবিল থেকে সারিগুলি মুছতে হবে না । আমি শুধু বুঝতে পারি না কেন সূচকটি লক করা আছে। তদুপরি, proc_warningsসারণিটি ফাঁকা থাকলে সূচীটি লক করা হয় না বা day_positionসারণীতে কম সংখ্যক সারি থাকে (যেমন একশত সারি)।

আরও তদন্ত EXPLAINএকই ধরণের SELECTজিজ্ঞাসা চালানো ছিল । এটি দেখায় যে ক্যোয়ারী অপ্টিমাইজার proc_warningsসারণী সন্ধানের জন্য সূচক ব্যবহার করে না এবং এটি কেবল প্রাথমিক কারণ কী সূচকটিকে কেন অবরুদ্ধ করে তা আমি কল্পনা করতে পারি।

সরলীকৃত মামলা

দু'টি রেকর্ডের সাথে কেবল দুটি টেবিল থাকলেই ইস্যুটিকে সরল ক্ষেত্রেও পুনরুত্পাদন করা যেতে পারে, তবে সন্তানের টেবিলটির মূল টেবিলে রেফ কলামে কোনও সূচক থাকে না।

parentটেবিল তৈরি করুন

CREATE TABLE `parent` (
  `id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

childটেবিল তৈরি করুন

CREATE TABLE `child` (
  `id` int(10) unsigned NOT NULL,
  `parent_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

টেবিলগুলি পূরণ করুন

INSERT INTO `parent` (id) VALUES (1), (2);
INSERT INTO `child` (id, parent_id) VALUES (1, NULL), (2, NULL);

দুটি সমান্তরাল লেনদেনে পরীক্ষা:

  • লেনদেন ঘ

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET AUTOCOMMIT=0;
    BEGIN;
    DELETE c FROM child c 
      INNER JOIN parent p ON p.id = c.parent_id 
    WHERE p.id = 1;
    
  • লেনদেন 2

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET AUTOCOMMIT=0;
    BEGIN;
    DELETE c FROM child c 
      INNER JOIN parent p ON p.id = c.parent_id 
    WHERE p.id = 2;
    

উভয় ক্ষেত্রেই সাধারণ অংশটি হ'ল মাইএসকিউএল সূচকগুলি ব্যবহার করে না। আমি বিশ্বাস করি এটি পুরো টেবিলটি লক করার কারণ।

আমাদের সমাধান

একমাত্র সমাধান যা আমরা এখন দেখতে পাচ্ছি তা থ্রেড পরিষ্কার শেষ হতে দিতে 50 ডিফল্ট থেকে 500 সেকেন্ডে ডিফল্ট লক ওয়েটআউট আউট করা। তারপরে আঙ্গুলগুলি অতিক্রম করতে হবে।

কোন সাহায্য প্রশংসা।


আমার একটি প্রশ্ন আছে: আপনি কোনও লেনদেনেই কমিট কার্যকর করেছিলেন?
রোল্যান্ডোমাইএসকিউএলডিবিএ

অবশ্যই. সমস্যাটি হ'ল অন্য সমস্ত লেনদেনের মধ্যে একটির পরিবর্তন না হওয়া পর্যন্ত অপেক্ষা করতে হবে। সাধারণ পরীক্ষার ক্ষেত্রে কীভাবে সমস্যাটি পুনরায় উত্পাদন করা যায় তা দেখানোর জন্য একটি প্রতিশ্রুতিবদ্ধ বিবৃতি ধারণ করে না। আপনি যদি অপেক্ষারত লেনদেনে প্রতিশ্রুতিবদ্ধ বা রোলব্যাক পরিচালনা করেন তবে এটি একই সাথে লক প্রকাশ করে এবং অপেক্ষার লেনদেনের কাজটি সম্পূর্ণ হয়।
50 এ জোরদার করুন

আপনি যখন বলেন যে মাইএসকিউএল উভয় ক্ষেত্রে সূচকগুলি ব্যবহার করে না, কারণ এটি সত্যিকারের দৃশ্যে নেই বলেই? যদি সূচকগুলি থাকে তবে আপনি কি তাদের জন্য কোড সরবরাহ করতে পারেন? নীচে পোস্ট করা সূচী পরামর্শগুলির মধ্যে কোনটি ব্যবহার করা সম্ভব? যদি কোনও সূচক না থাকে এবং কোনও যোগ করার চেষ্টা করা সম্ভব না হয় তবে মাইএসকিউএল প্রতিটি থ্রেড দ্বারা প্রক্রিয়াকৃত ডেটা সেটকে সীমাবদ্ধ করতে পারে না। যদি এটি হয় তবে এন থ্রেডগুলি কেবল N বার দ্বারা সার্ভারের কাজের চাপকে বহুগুণ করবে এবং would WHERE vd.ivehicle_id IN (2, 13) এবং dp.dirty_data = এর মতো পরামিতি তালিকার সাহায্যে একটি থ্রেড চালানো আরও দক্ষ হবে = 1;}।
জেএম হিক্স

ঠিক আছে, সংযুক্ত নমুনা ডেটা ফাইলে সূচিগুলি দূরে সন্ধান পেয়েছে।
জেএম হিক্স

আরও কয়েকটি প্রশ্ন: 1) day_positionসারণীতে সাধারণত কতগুলি সারি থাকে, যখন এটি এত ধীরে চলতে শুরু করে যে আপনাকে টাইম আউট সীমাটি 500 সেকেন্ডে গতিতে হবে? 2) আপনার কাছে কেবলমাত্র নমুনা ডেটা থাকলে চালানো কতক্ষণ সময় নেয়?
জেএম হিক্স

উত্তর:


3

নতুন উত্তর (মাইএসকিউএল-স্টাইলের ডায়নামিক এসকিউএল): ঠিক আছে, এটি অন্য পোস্টারের বর্ণিত পদ্ধতিতে সমস্যাটিকে মোকাবেলা করে - পরস্পরের সাথে বেমানান একচেটিয়া লকগুলি ক্রমবিন্যাসের বিপরীতে দেখা যায় যাতে যতই ঘটুক না কেন, কেবলমাত্র সেগুলির জন্য ঘটে লেনদেনের কার্যকারিতা শেষে সর্বনিম্ন পরিমাণ।

বিবৃতিটির পড়ার অংশটি তার নিজস্ব নির্বাচনী বিবৃতিতে পৃথক করে এবং গতিশীলভাবে মুছে ফেলার বিবরণ তৈরি করে তা সম্পন্ন করা হয় যা বিবৃতি উপস্থিতির ক্রম অনুসারে শেষদিকে চলতে বাধ্য হবে এবং যা কেবল প্রোক_ওয়ার্নিং সারণিকে প্রভাবিত করবে।

বর্গফুট ভাগে একটি ডেমো পাওয়া যায়:

এই লিঙ্কটি স্কিমা ডাব্লু / নমুনা ডেটা এবং সারিগুলির জন্য মিলছে এমন একটি সাধারণ কোয়েরি দেখায় ivehicle_id=2। 2 টি সারি ফলস্বরূপ, তাদের কোনওটি মোছা হয়নি।

এই লিঙ্কটি একই স্কিমা, নমুনা ডেটা দেখায়, তবে এসপিটির proc_warningsজন্য এন্ট্রি মুছতে বলার জন্য একটি মান 2 ডিলেটএন্ট্রিগুলি সঞ্চিত প্রোগ্রামে পাস করে ivehicle_id=2। সারিগুলির জন্য সাধারণ ক্যোয়ারী কোনও ফল দেয় না কারণ সেগুলি সফলভাবে মুছে ফেলা হয়েছে। ডেমো লিঙ্কগুলি কেবল ডিস্টেরেট করে যে কোডটি মোছার উদ্দেশ্যে হিসাবে কাজ করে। সঠিক পরীক্ষার পরিবেশ সহ ব্যবহারকারী এটির ফলে ব্লক করা থ্রেডের সমস্যা সমাধান হয় কিনা সে সম্পর্কে মন্তব্য করতে পারেন।

সুবিধার জন্য এখানে কোডটিও রয়েছে:

CREATE PROCEDURE DeleteEntries (input_vid INT)
BEGIN

    SELECT @idstring:= '';
    SELECT @idnum:= 0;
    SELECT @del_stmt:= '';

    SELECT @idnum:= @idnum+1 idnum_col, @idstring:= CONCAT(@idstring, CASE WHEN CHARACTER_LENGTH(@idstring) > 0 THEN ',' ELSE '' END, CAST(id AS CHAR(10))) idstring_col
    FROM proc_warnings
    WHERE EXISTS (
        SELECT 0
        FROM day_position
        WHERE day_position.transaction_id = proc_warnings.transaction_id
        AND day_position.dirty_data = 1
        AND EXISTS (
            SELECT 0
            FROM ivehicle_days
            WHERE ivehicle_days.id = day_position.ivehicle_day_id
            AND ivehicle_days.ivehicle_id = input_vid
        )
    )
    ORDER BY idnum_col DESC
    LIMIT 1;

    IF (@idnum > 0) THEN
        SELECT @del_stmt:= CONCAT('DELETE FROM proc_warnings WHERE id IN (', @idstring, ');');

        PREPARE del_stmt_hndl FROM @del_stmt;
        EXECUTE del_stmt_hndl;
        DEALLOCATE PREPARE del_stmt_hndl;
    END IF;
END;

লেনদেনের মধ্যে থেকেই প্রোগ্রামটি কল করার এটি সিনট্যাক্স:

CALL DeleteEntries(2);

মূল উত্তর (এখনও মনে হয় এটি খুব জঞ্জাল নয়) 2 টি সমস্যার মতো দেখায়: 1) ধীরে ধীরে প্রশ্ন 2) অপ্রত্যাশিত লকিং আচরণ

# 1 ইস্যু হিসাবে, ধীরে ধীরে প্রায়শই প্রায়শই একই দুটি কৌশল দ্বারা মুছে ফেলা প্রশ্নাবলী বিবৃতি সরলকরণ এবং দরকারী সংযোজন বা সূচকগুলিতে পরিবর্তনগুলি দ্বারা সমাধান করা হয়। আপনি নিজেই ইতিমধ্যে সূচকে সংযোগ তৈরি করেছেন - এগুলি ছাড়াই অপ্টিমাইজার প্রক্রিয়াজাতকরণের জন্য সীমাবদ্ধ সারিগুলির সন্ধান করতে পারে না এবং প্রতিটি সারণী থেকে প্রতিটি সারি অতিরিক্ত সারি প্রতি গুণিত করে অতিরিক্ত কাজের পরিমাণ স্ক্যান করে যা করা আবশ্যক।

পরিকল্পনা এবং সূচিপত্রগুলির সন্ধানের পোস্টের পরে পর্যালোচনা করা হয়েছে: তবে আমি কল্পনা করেছি যে আপনি আপনার ক্যোয়ারির জন্য সর্বাধিক পারফরম্যান্স সুবিধা পাবেন যাতে আপনার একটি ভাল সূচক কনফিগারেশন রয়েছে making এটি করার জন্য, আপনি আরও ভাল সূচকগুলি বন্ধ রেখে এবং আরও সূচক কাঠামো যুক্ত করা যেতে পারে একই টেবিলগুলিতে সম্ভবত লক্ষণীয়ভাবে ধীর সন্নিবেশ সম্পাদনাসহ আরও ভাল পারফরম্যান্স মুছে ফেলতে পারেন এবং সম্ভবত আরও ভাল পারফরম্যান্স মুছে ফেলতে পারেন।

সোমঘাট ভাল:

CREATE TABLE  `day_position` (
    ...,
    KEY `day_position__id_rvrsd` (`dirty_data`, `ivehicle_day_id`)

) ;


CREATE TABLE  `ivehicle_days` (
    ...,
    KEY `ivehicle_days__vid_no_sort_index` (`ivehicle_id`)
);

এখানে পর্যালোচনা করা হয়েছে: যেহেতু এটি চালাতে যতক্ষণ সময় লাগে, তাই আমি নোংরা_ডেটাকে সূচীতে রেখে দিয়েছি এবং আমি যখন এটি ivehicle_day_id এর পরে সূচী ক্রমে রেখেছিলাম তখন আমি অবশ্যই এটি ভুল হয়ে গিয়েছিলাম - এটি প্রথম হওয়া উচিত।

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

সেরা / কভারিং সূচি:

CREATE TABLE  `day_position` (
    ...,
    KEY `day_position__id_rvrsd_trnsid_cvrng` (`dirty_data`, `ivehicle_day_id`, `transaction_id`)
) ;

CREATE TABLE  `ivehicle_days` (
    ...,
    UNIQUE KEY `ivehicle_days__vid_id_cvrng` (ivehicle_id, id)
);

CREATE TABLE  `proc_warnings` (

    .., /*rename primary key*/
    CONSTRAINT pk_proc_warnings PRIMARY KEY (id),
    UNIQUE KEY `proc_warnings__transaction_id_id_cvrng` (`transaction_id`, `id`)
);

সর্বশেষ দুটি পরিবর্তনের প্রস্তাবনাগুলির দ্বারা দুটি কার্যকারিতা অপ্টিমাইজেশান লক্ষ্যগুলি অনুসন্ধান করা হয়েছে:
1) যদি ক্রমাগত অ্যাক্সেস করা টেবিলগুলির জন্য অনুসন্ধান কীগুলি বর্তমানে অ্যাক্সেস করা সারণীর জন্য ক্লাস্টারযুক্ত মূল ফলাফলগুলির মতো না হয় তবে আমরা কীটি করার দরকার পড়ে তা দূর করি ক্লাস্টারড ইনডেক্সে সূচক-সন্ধান-সহ-স্ক্যান অপারেশনগুলির একটি দ্বিতীয় সেট
2) যদি পরবর্তীটি এটি না হয় তবে কমপক্ষে কমপক্ষে সম্ভাবনা রয়েছে যে অপ্টিমাইজার আরও কার্যকরভাবে যোগসূতী নির্বাচন করতে পারে যেহেতু সূচকগুলি রাখবে বাছাই করা ক্রমে কীগুলি যুক্ত হওয়া প্রয়োজন।

আপনার ক্যোয়ারীটি যতটা সরল করা যেতে পারে তেমনটি মনে হয় (পরে এটি সম্পাদিত হওয়ার ক্ষেত্রে এখানে অনুলিপি করা হয়েছে):

DELETE pw 
FROM proc_warnings pw 
INNER JOIN day_position dp 
    ON dp.transaction_id = pw.transaction_id 
INNER JOIN ivehicle_days vd 
    ON vd.id = dp.ivehicle_day_id 
WHERE vd.ivehicle_id=2 AND dp.dirty_data=1;

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

DELETE FROM proc_warnings
FORCE INDEX (`proc_warnings__transaction_id_id_cvrng`, `pk_proc_warnings`)
WHERE EXISTS (
    SELECT 0
    FROM day_position
    FORCE INDEX (`day_position__id_rvrsd_trnsid_cvrng`)  
    WHERE day_position.transaction_id = proc_warnings.transaction_id
    AND day_position.dirty_data = 1
    AND EXISTS (
        SELECT 0
        FROM ivehicle_days
        FORCE INDEX (`ivehicle_days__vid_id_cvrng`)  
        WHERE ivehicle_days.id = day_position.ivehicle_day_id
        AND ivehicle_days.ivehicle_id = ?
    )
);

শ্রদ্ধা হিসাবে # 2, অপ্রত্যাশিত লক আচরণ।

যেহেতু আমি দেখতে পাচ্ছি উভয় কোয়েরিতে প্রাথমিক কী = 53 দিয়ে একটি সারিতে একচেটিয়া এক্স লক চাইছে। তবে, তাদের উভয়কেই অবশ্যই প্রো_ ওয়ার্নিং সারণী থেকে সারিগুলি মুছতে হবে। আমি শুধু বুঝতে পারি না কেন সূচকটি লক করা আছে।

আমার ধারণা এটি লক হওয়া সূচকটি হবে কারণ লক হওয়ার জন্য ডেটার সারিটি একটি ক্লাস্টারড ইনডেক্সে থাকে, অর্থাত্ একক সারির ডাটা নিজেই সূচীতে থাকে।

এটি লক হয়ে যাবে, কারণ:
1) http://dev.mysql.com/doc/refman/5.1/en/innodb-locks-set.html অনুসারে

... একটি মোছা সাধারণত প্রতিটি সূচক রেকর্ডে রেকর্ড লক সেট করে যা এসকিউএল স্টেটমেন্টের প্রসেসিংয়ে স্ক্যান করা হয়। বিবৃতিতে সর্বাধিক শর্ত রয়েছে যা সারিটি বাদ দিবে তা বিবেচ্য নয়। InnoDB সঠিক WHERE অবস্থাটি মনে রাখে না, তবে কেবলমাত্র সূচীর রেঞ্জগুলি স্ক্যান করা হয়েছিল তা কেবল তা জানে।

আপনি উপরেও উল্লেখ করেছেন:

... আমার কাছে পড়ার মূল বৈশিষ্ট্য হ'ল এটি কীভাবে লকগুলির সাথে কাজ করে। এটি মিলে না যাওয়া সারিগুলির সূচক লকগুলি প্রকাশ করা উচিত, তবে তা হয় না।

এবং এর জন্য নিম্নলিখিত রেফারেন্সটি সরবরাহ করেছেন:
http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_read-committed

যা আপনার হিসাবে একই হিসাবে বর্ণনা করে, সেই একই রেফারেন্স অনুসারে একটি শর্ত রয়েছে যার উপরে একটি লক প্রকাশ করা হবে:

এছাড়াও, মাইএসকিউএল WHERE শর্তটি মূল্যায়ন করার পরে ননম্যাচিং সারিগুলির রেকর্ড লকগুলি প্রকাশ করা হয়।

যা এই ম্যানুয়াল পৃষ্ঠাতেও পুনঃপ্রণোদিত হয়

READ COMMITTED বিচ্ছিন্নতা স্তরটি ব্যবহার করা বা ইনেডড_ব্লকস_অ্যানস্যাফ_ফর্ম_বিনলোগ সক্ষম করার অন্যান্য প্রভাবগুলিও রয়েছে: মাইএসকিউএল WHERE শর্তটি মূল্যায়ন করার পরে ননম্যাচিং সারিগুলির জন্য রেকর্ড লকগুলি প্রকাশ করা হয়।

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

তদ্ব্যতীত, প্রো-ওয়ার্নিংস সারণি খালি থাকলে সূচকটি লক করা হয় না

ডাটাবেস সূচকগুলির মধ্যে রেকর্ডগুলি লক করতে পারে না যদি সেখানে না থাকে।

তদুপরি, ... দিন_ অবস্থানের টেবিলটিতে কম সংখ্যক সারি থাকে (যেমন একশো সারি)।

এর অর্থ असंख्य জিনিস যেমন হতে পারে তবে সম্ভবত সীমাবদ্ধ নয়: পরিসংখ্যানের পরিবর্তনের কারণে একটি পৃথক সম্পাদন পরিকল্পনা, খুব অল্প সংখ্যক ডেটা সেট হওয়ার কারণে খুব দ্রুত সম্পাদনের কারণে খুব সংক্ষিপ্ত-হতে-পর্যবেক্ষণ-লক / অপারেশন যোগ দিন।


WHEREশর্ত মূল্যায়ন যখন ক্যোয়ারী সমাপ্ত করা হয়। তাই না? আমি ভেবেছিলাম যে তালাবদ্ধ কিছু সাম্প্রতিক অনুসন্ধানগুলি কার্যকর করার পরে ঠিক ছেড়ে দেওয়া হয়। এটাই স্বাভাবিক আচরণ। তবে, এটি হয় না। এই থ্রেডে প্রস্তাবিত প্রশ্নের কোনও একটিই proc_warningsটেবিলে ক্লাস্টারড ইনডেক্স লক এড়াতে সহায়তা করে না । আমি মনে করি আমি মাইএসকিউএলে একটি বাগ ফাইল করব। আপনার সাহায্যের জন্য ধন্যবাদ.
ভিভিডিজ দিন

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

যদিও আপনার {WHERE} দেখে মনে হচ্ছে এটি যোগদানের প্রক্রিয়া চলাকালীন কোন সারিগুলিকে জোড়ার গণনায় অন্তর্ভুক্ত করা হয়েছে তা সীমাবদ্ধ করার জন্য ব্যবহার করা যেতে পারে, তবে আমি দেখতে পাচ্ছি না যে সম্পূর্ণ j হিসাবে গণিত। এটি বলেছিল, আমাদের বিশ্লেষণের জন্য, আমি সন্দেহ করি যে আপনি ঠিক বলেছেন যে আমাদের সন্দেহ হওয়া উচিত "কোয়েরি শেষ হওয়ার পরে WHERE শর্তটি মূল্যায়ন করা হয়"। তবুও এটি আমাকে একই সামগ্রিক উপসংহারে নিয়ে যায়, যে পারফরম্যান্সটি সমাধান করা দরকার এবং তারপরে সম্মতিযুক্ত আপাতত ডিগ্রি আনুপাতিকভাবে বৃদ্ধি পাবে।
জেএম হিক্স

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

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

3

READ_COMMITTED কীভাবে এই পরিস্থিতির সৃষ্টি করতে পারে তা আমি দেখতে পাচ্ছি ।

READ_COMMITTED তিনটি জিনিস জন্য করতে পারবেন:

  • READ_COMMITTED বিচ্ছিন্নতা স্তর ব্যবহার করে অন্যান্য লেনদেন দ্বারা প্রতিশ্রুতিবদ্ধ পরিবর্তনের দৃশ্যমানতা ।
  • পুনরাবৃত্তিযোগ্য পাঠ নয়: প্রতিবার একটি পৃথক ফলাফল পাওয়ার সম্ভাবনা সহ লেনদেন একই পুনরুদ্ধার সম্পাদন করে।
  • ফ্যান্টমস: লেনদেনের সারিগুলি উপস্থিত থাকতে পারে যেখানে এটি আগে দেখা যায় নি।

এটি লেনদেনের জন্য নিজেই একটি অভ্যন্তরীণ দৃষ্টান্ত তৈরি করে কারণ লেনদেনের সাথে যোগাযোগ অবশ্যই বজায় রাখতে হবে:

  • InnoDB বাফার পুল (কমিট এখনও অপসারণযোগ্য)
  • সারণীর প্রাথমিক কী
  • সম্ভবত
    • ডাবল রাইট বাফার
    • টেবিলস্পেস পূর্বাবস্থায় ফেরান
  • চিত্রের উপস্থাপনা

যদি দুটি স্বতন্ত্র READ_COMMITTED লেনদেন একই পদ্ধতিতে আপডেট করা হচ্ছে একই টেবিল / সারিগুলিতে অ্যাক্সেস করে থাকে তবে একটি টেবিল লক না হয়ে জেন_ক্লাস্ট_ইন্ডেক্স (ওরফে ক্লাস্টারড ইনডেক্স) এর মধ্যে একটি এক্সক্লুসিভ লক আশা করতে প্রস্তুত থাকুন । আপনার সরলকৃত কেস থেকে প্রশ্নগুলি দেওয়া:

  • লেনদেন ঘ

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET AUTOCOMMIT=0;
    BEGIN;
    DELETE c FROM child c 
      INNER JOIN parent p ON p.id = c.parent_id 
    WHERE p.id = 1;
    
  • লেনদেন 2

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET AUTOCOMMIT=0;
    BEGIN;
    DELETE c FROM child c 
      INNER JOIN parent p ON p.id = c.parent_id 
    WHERE p.id = 2;
    

আপনি জেন_ক্লাস্ট_আইডেক্সে একই অবস্থানটি লক করছেন। কেউ বলতে পারেন, "তবে প্রতিটি লেনদেনের আলাদা প্রাথমিক কী থাকে" " দুর্ভাগ্যক্রমে, ইনোডিবি-র দৃষ্টিতে এটি ঘটেনি। এটি ঠিক তাই ঘটে যে আইডি 1 এবং আইডি 2 একই পৃষ্ঠায় থাকে।

information_schema.innodb_locksপ্রশ্নে সরবরাহিত আপনার দিকে ফিরে তাকাও

| lock_id                | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
| '1A2973A4:0:3172298:2' | '1A2973A4'  | 'X'       | 'RECORD'  | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
| '1A296F67:0:3172298:2' | '1A296F67'  | 'X'       | 'RECORD'  | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |

বাদে lock_id, lock_trx_idবাকী বাকী বিবরণটি অভিন্ন। যেহেতু লেনদেনগুলি একই স্তরের প্লেয়িং ফিল্ডে রয়েছে (একই লেনদেনের বিচ্ছিন্নতা), তাই এটি অবশ্যই হওয়া উচিত

বিশ্বাস করুন, আমি আগে এই ধরণের পরিস্থিতি সম্বোধন করেছি। এটিতে আমার আগের পোস্টগুলি এখানে:


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

যদি কোনও ত্রুটির ফলস্বরূপ কেবল একটি একক এসকিউএল বিবৃতি আবার ঘুরিয়ে দেওয়া হয়, তবে বিবৃতি দ্বারা সেট করা কয়েকটি লক সংরক্ষণ করা যেতে পারে। এটি ঘটেছিল কারণ InnoDB সারি লকগুলি একটি ফর্ম্যাটে সংরক্ষণ করে যাতে এটি পরে জানতে পারে না কোন লকটি কোন বিবৃতি দ্বারা সেট করা হয়েছিল: dev.mysql.com/doc/refman/5.5/en/innodb-deadlock-detection.html
RolandoMySQLDBA

দয়া করে নোট করুন যে লক করার জন্য একই পৃষ্ঠায় দুটি সারি বিদ্যমান থাকার সম্ভাবনাটি উল্লেখ করেছি (দেখুন Look back at information_schema.innodb_locks you supplied in the Question)
রোল্যান্ডোমাইএসকিউএলডিবিএ

একক বিবৃতি ফিরিয়ে আনার বিষয়ে - আমি এটাকে বুঝতে পারি যেন একক বিবরণটি যদি একক লেনদেনের মধ্যে ব্যর্থ হয় তবে এটি লকগুলি ধরে রাখতে পারে। ঠিক আছে. আমার বড় প্রশ্ন হ'ল সফলভাবে DELETEবিবৃতি প্রক্রিয়া করার পরে এটি কেন মিলে না যাওয়া সারি লকগুলি ছেড়ে দেয় না ।
ভায়িফাইড করুন

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

2

আমি জিজ্ঞাসা এবং ব্যাখ্যা তাকান। আমি নিশ্চিত নই, তবে অন্ত্রের অনুভূতি রয়েছে যে সমস্যাটি নিম্নলিখিত। কোয়েরিটি দেখুন:

DELETE pw 
FROM proc_warnings pw 
INNER JOIN day_position dp 
   ON dp.transaction_id = pw.transaction_id 
INNER JOIN ivehicle_days vd 
   ON vd.id = dp.ivehicle_day_id 
WHERE vd.ivehicle_id=? AND dp.dirty_data=1;

সমতুল্য নির্বাচনটি হ'ল:

SELECT pw.id
FROM proc_warnings pw
INNER JOIN day_position dp
   ON dp.transaction_id = pw.transaction_id
INNER JOIN ivehicle_days vd
   ON vd.id = dp.ivehicle_day_id
WHERE vd.ivehicle_id=16 AND dp.dirty_data=1;

আপনি যদি এর ব্যাখ্যার দিকে নজর রাখেন তবে দেখতে পাবেন যে কার্যকরকরণ পরিকল্পনাটি proc_warningsটেবিল দিয়ে শুরু হয় । তার মানে মাইএসকিউএল টেবিলের প্রাথমিক কীটি স্ক্যান করে এবং প্রতিটি সারি শর্তটি সত্য কিনা তা পরীক্ষা করে এবং এটি যদি হয় - সারিটি মুছে ফেলা হয়। এটি মাইএসকিউএলকে পুরো প্রাথমিক কীটি লক করতে হবে।

আপনার যা দরকার তা হল জয়েন্ট অর্ডারকে উল্টানো, এটি হল সমস্ত লেনদেনের আইডির সন্ধান vd.ivehicle_id=16 AND dp.dirty_data=1এবং তাদের proc_warningsটেবিলে যোগ দেওয়া।

এটি আপনাকে সূচকগুলির একটি প্যাচ করতে হবে:

ALTER TABLE `day_position`
 DROP INDEX `day_position__id`,
 ADD INDEX `day_position__id`
   USING BTREE (`ivehicle_day_id`, `dirty_data`, `transaction_id`);

এবং মুছুন কোয়েরিটি আবার লিখুন:

DELETE pw
FROM (
  SELECT DISTINCT dp.transaction_id
  FROM ivehicle_days vd
  JOIN day_position dp ON dp.ivehicle_day_id = vd.id
  WHERE vd.ivehicle_id=? AND dp.dirty_data=1
) as tr_id
JOIN proc_warnings pw ON pw.transaction_id = tr_id.transaction_id;

দুর্ভাগ্যক্রমে এটি সাহায্য করে না, অর্থাত্ সারিগুলি proc_warningsএখনও লক হয়ে যায়। যাই হোক ধন্যবাদ.
স্পন্দিত করুন vital

2

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

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
DELETE c FROM child c
INNER JOIN parent p ON
    p.id = c.parent_id
WHERE p.id = 1;

কোয়েরি করে আপনি কোন বিচ্ছিন্ন স্তরে রয়েছেন তা পরীক্ষা করতে পারেন

SELECT @@tx_isolation;

এটা সত্যি না. SET AUTOCOMMIT=0পরবর্তী লেনদেনের জন্য কেন বিচ্ছিন্নতা স্তরটি পুনরায় সেট করা উচিত? আমি বিশ্বাস করি এর আগে নতুন কিছু শুরু না হলে এটি নতুন লেনদেন শুরু করে (যা আমার ক্ষেত্রে)। সুতরাং, পরবর্তী START TRANSACTIONবা BEGINবিবৃতি আরও সুনির্দিষ্ট হওয়ার জন্য প্রয়োজনীয় নয়। অটোকোমিট অক্ষম করার আমার উদ্দেশ্যটি হল DELETEবিবৃতি কার্যকর করার পরে লেনদেনটি খোলা ছেড়ে দেওয়া ।
ভায়িফাইড করুন

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