Rumah  >  Artikel  >  pangkalan data  >  Apakah mekanisme pelaksanaan dalaman kunci Mysql?

Apakah mekanisme pelaksanaan dalaman kunci Mysql?

王林
王林ke hadapan
2023-06-03 18:04:481203semak imbas

Gambaran Keseluruhan

Walaupun pangkalan data hubungan moden semakin serupa, pangkalan data tersebut mungkin mempunyai mekanisme yang sama sekali berbeza di sebalik pelaksanaannya. Dari segi penggunaan sebenar, kerana kewujudan spesifikasi sintaks SQL, tidak sukar untuk kita membiasakan diri dengan pelbagai pangkalan data hubungan, tetapi mungkin terdapat banyak kaedah pelaksanaan kunci seperti yang terdapat pangkalan data.

Microsoft Sql Server hanya menyediakan kunci halaman sebelum tahun 2005. Hanya pada versi 2005 ia mula menyokong konkurensi optimistik dan kunci peringkat barisan dibenarkan dalam mod optimistik , kunci adalah sumber yang terhad Lebih besar bilangannya, lebih besar overhed Untuk mengelakkan penurunan prestasi seperti tebing disebabkan peningkatan pesat dalam bilangan kunci, ia menyokong mekanisme yang dipanggil peningkatan kunci kunci baris dinaik taraf kepada kunci halaman, prestasi serentak akan kembali ke titik asal.

Malah, masih terdapat banyak pertikaian mengenai tafsiran fungsi penguncian oleh enjin pelaksanaan yang berbeza dalam pangkalan data yang sama. MyISAM hanya menyokong penguncian peringkat jadual, yang sesuai untuk bacaan serentak, tetapi mempunyai batasan tertentu dalam pengubahsuaian serentak. Innodb sangat serupa dengan Oracle, menyediakan bacaan konsisten dan sokongan kunci baris yang tidak mengunci Perbezaan yang jelas daripada Pelayan Sql ialah apabila jumlah kunci meningkat, Innodb hanya perlu membayar harga yang kecil.

Struktur kunci baris

Innodb menyokong kunci baris dan tiada overhed yang sangat besar dalam menerangkan kunci. Oleh itu, tidak perlu mekanisme naik taraf kunci sebagai langkah menyelamat selepas sejumlah besar kunci menyebabkan kemerosotan prestasi.

Diekstrak daripada fail lock0priv.h, Innodb mentakrifkan kunci baris seperti berikut:

/** Record lock for a page */
struct lock_rec_t {
    /* space id */
    ulint  space;	
    /* page number */
    ulint  page_no;
    /**
     * number of bits in the lock bitmap; 
     * NOTE: the lock bitmap is placed immediately after the lock struct 
     */
    ulint  n_bits;			
};

Walaupun kawalan konkurensi boleh diperhalusi pada peringkat baris, pengurusan kunci disusun dalam unit halaman. daripada. Dalam reka bentuk Innodb, satu-satunya halaman data boleh ditentukan melalui dua syarat yang diperlukan iaitu id ruang dan nombor halaman n_bits menunjukkan bilangan bit yang diperlukan untuk menerangkan maklumat kunci baris halaman.

Setiap rekod dalam halaman data yang sama diberikan nombor urutan peningkatan berterusan yang unik: heap_no Jika anda ingin mengetahui sama ada baris rekod tertentu dikunci, anda hanya perlu menentukan sama ada nombor itu dalam kedudukan heap_no. daripada peta bit adalah satu. Oleh kerana peta bit kunci memperuntukkan ruang memori berdasarkan bilangan rekod dalam halaman data, ia tidak ditakrifkan secara eksplisit dan rekod halaman mungkin terus meningkat, jadi ruang bersaiz LOCK_PAGE_BITMAP_MARGIN dikhaskan.

/** 
 * Safety margin when creating a new record lock: this many extra records
 * can be inserted to the page without need to create a lock with 
 * a bigger bitmap
 */
#define LOCK_PAGE_BITMAP_MARGIN	 64

Andaikan bahawa halaman data dengan id ruang = 20, nombor halaman = 100 pada masa ini mempunyai 160 rekod dan rekod dengan heap_no 2, 3 dan 4 telah dikunci, kemudian struktur dan data lock_rec_t yang sepadan halaman hendaklah seperti ini Pencirian:

Apakah mekanisme pelaksanaan dalaman kunci Mysql?

Nota:

  • Peta bit kunci dalam ingatan hendaklah diedarkan secara linear. struktur dimensi yang ditunjukkan dalam rajah ialah Untuk memudahkan penerangan

  • struktur bitmap dan lock_rec_t ialah ingatan berterusan, dan hubungan rujukan dalam rajah juga diperlukan untuk melukis

Anda boleh melihat yang sepadan pada halaman ini Kedudukan kedua, ketiga dan keempat bitmap semuanya ditetapkan kepada 1. Memori yang digunakan dengan menerangkan kunci baris halaman data agak terhad dari perspektif persepsi. Kita boleh mengira:

160 / 8 + 8 + 1 = 29bait.

  • 160 rekod sepadan dengan 160bit

  • +8 adalah kerana 64bit perlu ditempah

  • +1 adalah kerana 1 bait dikhaskan dalam kod sumber

Untuk mengelakkan masalah nilai hasil yang kecil, +1 tambahan ditambahkan di sini. Ini boleh mengelakkan ralat yang disebabkan oleh pembahagian integer. Jika terdapat 161 rekod, jika tidak +1, 20 bait yang dikira tidak mencukupi untuk menerangkan maklumat kunci semua rekod (tanpa menggunakan bit terpelihara).

Diekstrak daripada fail lock0priv.h:

/* lock_rec_create函数代码片段 */
n_bits = page_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN;
n_bytes = 1 + n_bits / 8;
/* 注意这里是分配的连续内存 */
lock = static_cast<lock_t*>(
    mem_heap_alloc(trx->lock.lock_heap, sizeof(lock_t) + n_bytes)
);
/**
 * Gets the number of records in the heap.
 * @return number of user records 
 */
UNIV_INLINE ulint page_dir_get_n_heap(const page_t* page)	
{
    return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
}

Struktur kunci meja

Innodb juga menyokong kunci meja boleh dibahagikan kepada dua kategori: kunci niat dan auto-. kunci kenaikan. Struktur data ditakrifkan seperti berikut:

Diekstrak daripada fail lock0priv.h

struct lock_table_t {
    /* database table in dictionary cache */
    dict_table_t*  table;
    /* list of locks on the same table */
    UT_LIST_NODE_T(lock_t)  locks;
};

Diekstrak daripada fail ut0lst.h

struct ut_list_node {
    /* pointer to the previous node, NULL if start of list */
    TYPE*  prev;
    /* pointer to next node, NULL if end of list */
    TYPE*  next;
};
#define UT_LIST_NODE_T(TYPE)  ut_list_node<TYPE>

Penerangan tentang kunci dalam transaksi

Lock_rec_t di atas, lock_table_t Struktur hanyalah definisi berasingan Kunci dijana dalam urus niaga Oleh itu, kunci baris dan kunci meja yang sepadan dengan setiap transaksi akan mempunyai struktur kunci yang sepadan berikut:

Dipetik daripada fail lock0priv.h

/** Lock struct; protected by lock_sys->mutex */
struct lock_t {
    /* transaction owning the lock */
    trx_t*  trx;
    /* list of the locks of the transaction */
    UT_LIST_NODE_T(lock_t)  trx_locks;	
    /** 
     * lock type, mode, LOCK_GAP or LOCK_REC_NOT_GAP,
     * LOCK_INSERT_INTENTION, wait flag, ORed 
     */
    ulint  type_mode;
    /* hash chain node for a record lock */
    hash_node_t  hash;	
    /*!< index for a record lock */
    dict_index_t*  index;
    /* lock details */
    union {
        /* table lock */
        lock_table_t  tab_lock;
        /* record lock */
        lock_rec_t  rec_lock;
    } un_member;
};

lock_t ditakrifkan berdasarkan setiap halaman (atau jadual) setiap transaksi, tetapi transaksi selalunya melibatkan berbilang halaman, jadi senarai terpaut trx_locks diperlukan untuk menggabungkan semua maklumat kunci yang berkaitan dengan transaksi. Selain menanyakan semua maklumat kunci berdasarkan transaksi, senario sebenar juga memerlukan sistem mesti dapat mengesan dengan cepat dan cekap sama ada rekod baris telah dikunci. Oleh itu, mesti ada pembolehubah global untuk menyokong pertanyaan maklumat kunci untuk rekod baris. Innodb memilih jadual cincang, yang ditakrifkan seperti berikut:

Diekstrak daripada fail lock0lock.h

/** The lock system struct */
struct lock_sys_t {
    /* Mutex protecting the locks */
    ib_mutex_t  mutex;		
    /* 就是这里: hash table of the record locks */
    hash_table_t*  rec_hash;	
    /* Mutex protecting the next two fields */
    ib_mutex_t  wait_mutex;
    /** 
     * Array  of user threads suspended while waiting forlocks within InnoDB,
     * protected by the lock_sys->wait_mutex 
     */
    srv_slot_t*  waiting_threads;
    /*
     * highest slot ever used in the waiting_threads array,
     * protected by lock_sys->wait_mutex 
     */
    srv_slot_t*  last_slot;
    /** 
     * TRUE if rollback of all recovered transactions is complete. 
     * Protected by lock_sys->mutex 
     */
    ibool  rollback_complete;
    /* Max wait time */
    ulint  n_lock_max_wait_time;
    /**
     * Set to the event that is created in the lock wait monitor thread.
     * A value of 0 means the thread is not active
     */
    os_event_t	timeout_event;		
    /* True if the timeout thread is running */
    bool  timeout_thread_active;
};

Fungsi lock_sys_create bertanggungjawab untuk memulakan struktur lock_sys_t apabila pangkalan data bermula. Pembolehubah srv_lock_table_size menentukan saiz bilangan slot cincang dalam rec_hash. Nilai utama jadual cincang rec_hash dikira dengan menggunakan id ruang dan nombor halaman halaman.

Dipetik daripada fail lock0lock.ic, ut0rnd.ic

/**
 * Calculates the fold value of a page file address: used in inserting or
 * searching for a lock in the hash table.
 *
 * @return folded value 
 */
UNIV_INLINE ulint lock_rec_fold(ulint space, ulint page_no)
{
    return(ut_fold_ulint_pair(space, page_no));
}
/**
 * Folds a pair of ulints.
 *
 * @return folded value 
 */
UNIV_INLINE ulint ut_fold_ulint_pair(ulint n1, ulint n2)
{
    return (
        (
            (((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
            ^ UT_HASH_RANDOM_MASK
        ) 
        + n2
    );
}

Ini bermakna tiada cara untuk menyediakan cara untuk kita mengetahui secara langsung sama ada baris tertentu dikunci. Sebaliknya, anda harus terlebih dahulu mendapatkan id ruang dan nombor halaman melalui halaman di mana ia berada, dapatkan nilai kunci melalui fungsi lock_rec_fold, dan kemudian dapatkan lock_rec_t melalui pertanyaan cincang, dan kemudian imbas peta bit mengikut heap_no untuk akhirnya menentukan maklumat kunci. Fungsi lock_rec_get_first melaksanakan logik di atas:

这里返回的其实是lock_t对象,摘自lock0lock.cc文件

/**
 * Gets the first explicit lock request on a record.
 *
 * @param block   : block containing the record 
 * @param heap_no : heap number of the record 
 *
 * @return first lock, NULL if none exists 
 */
UNIV_INLINE lock_t* lock_rec_get_first(const buf_block_t* block, ulint heap_no)
{
    lock_t*  lock;
    ut_ad(lock_mutex_own());
    for (lock = lock_rec_get_first_on_page(block); lock;
         lock = lock_rec_get_next_on_page(lock)
    ) {
        if (lock_rec_get_nth_bit(lock, heap_no)) {
            break;
        }
    }
    return(lock);
}

以页面为粒度进行锁维护并非最直接有效的方式,它明显是时间换空间,不过这种设计使得锁开销很小。某一事务对任一行上锁的开销都是一样的,锁数量的上升也不会带来额外的内存消耗。

对应每个事务的内存对象trx_t中,包含了该事务的锁信息链表和等待的锁信息。因此存在如下两种途径对锁进行查询:

  • 根据事务: 通过trx_t对象的trx_locks链表,再通过lock_t对象中的trx_locks遍历可得某事务持有、等待的所有锁信息。

  • 根据记录: 根据记录所在的页,通过space id、page number在lock_sys_t结构中定位到lock_t对象,扫描bitmap找到heap_no对应的bit位。

上述各种数据结构,对其整理关系如下图所示:

Apakah mekanisme pelaksanaan dalaman kunci Mysql?

注:

lock_sys_t中的slot颜色与lock_t颜色相同则表明lock_sys_t slot持有lock_t 指针信息,实在是没法连线,不然图很混乱

Atas ialah kandungan terperinci Apakah mekanisme pelaksanaan dalaman kunci Mysql?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam