Rumah > Artikel > pangkalan data > Artikel untuk membincangkan mekanisme pelaksanaan dalaman kunci Mysql
Artikel ini akan membincangkan tentang mekanisme pelaksanaan dalaman kunci Mysql. Saya harap ia akan membantu semua orang.
Nota: Kod yang disenaraikan semuanya daripada Mysql-5.6
Walaupun pangkalan data hubungan semakin banyak dan lebih popular pada masa kini Semakin serupa, tetapi mekanisme pelaksanaan di belakangnya mungkin agak berbeza. 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 2005 hanya menyediakan kunci halaman sebelum ini sehingga versi 2005 ia mula menyokong konkurensi optimistik dan konkurensi pesimis dibenarkan dalam mod optimistik Dalam reka bentuk Pelayan, kunci adalah sumber yang terhad daripada kunci, ia menyokong kaedah yang dipanggil mekanisme Naik Taraf Kunci, sebaik sahaja kunci baris dinaik taraf kepada kunci halaman, prestasi serentak kembali ke titik asal.
Malah, walaupun dalam pangkalan data yang sama, enjin pelaksanaan yang berbeza masih mempunyai tafsiran yang berbeza tentang fungsi kunci. Untuk MyISAM, ia hanya menyokong kunci meja Bacaan serentak boleh diterima, tetapi pengubahsuaian serentak tidak mencukupi. Innodb sangat serupa dengan Oracle, memberikan sokongan tidak mengunci bacaan dan kunci baris Perbezaan yang jelas daripada Pelayan Sql ialah apabila jumlah kunci bertambah, Innodb hanya perlu bayar Harga yang kecil untuk dibayar.
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.
Dipetik 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; };
Tidak sukar untuk melihat bahawa walaupun kawalan konkurensi boleh diperhalusi untuk peringkat baris, kunci Susun dan urus pada kebutiran halaman. 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. Memandangkan 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 saiz 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 ruang id = 20, nombor halaman = 100 pada masa ini mempunyai 160 rekod dan rekod dengan heap_no 2, 3 dan 4 telah dikunci, maka struktur lock_rec_t yang sepadan dan halaman data hendaklah Ia dicirikan seperti ini:
Nota:
- Peta bit kunci dalam memori hendaklah diedarkan secara linear, seperti yang ditunjukkan dalam rajah dalam dua dimensi Struktur adalah untuk kemudahan penerangan
- Struktur bitmap dan lock_rec_t ialah sekeping memori berterusan Hubungan rujukan dalam rajah juga diperlukan untuk melukis
Anda boleh melihat peta bit kedua dan ketiga yang sepadan dengan halaman ini Menetapkan keempat-empat kedudukan kepada satu bermakna bahawa memori yang digunakan oleh kunci baris halaman data agak terhad dari perspektif persepsi ? Kita boleh mengira:
160 / 8 8 1 = 29bait.
Terdapat tambahan 1 di sini, mungkin untuk mengelakkan masalah nilai hasil kecil akibat pembahagian integer. Jika terdapat 161 rekod, jika bukan 1, 20 bait yang dikira tidak mencukupi untuk menerangkan maklumat kunci semua rekod (tanpa menggunakan bit terpelihara).
Dipetik 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); }</lock_t>
Innodb juga menyokong kunci meja, yang boleh dibahagikan kepada dua kategori :Kunci niat, struktur data kunci meningkat sendiri ditakrifkan seperti berikut:
Dipetik daripada lock0priv.h fail
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; };
Dipetik daripada ut0lst.h fail
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></type>
Struktur lock_rec_t dan lock_table_t di atas hanyalah takrifan berasingan Kunci dijana dalam urus niaga, jadi terdapat kunci baris dan kunci jadual yang sepadan dengan setiap transaksi. Struktur kunci yang sepadan ditakrifkan seperti berikut:
Dipetik daripada lock0priv.h fail
/** 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; /*!<p> lock_t是根据每个事务每个页(或表)来定义的,但是一个事务往往涉及到多个页,因此需要链表<strong>trx_locks</strong>串联起一个事务相关的所有锁信息。除了需要根据事务查询到所有锁信息,实际场景还要求系统必须能够快速高效的检测出某个行记录是否已经上锁。因此必须有一个全局变量支持对行记录进行锁信息的查询。Innodb选择了哈希表,其定义如下:</p><p> 摘自<strong>lock0lock.h</strong>文件</p><pre class="brush:php;toolbar:false">/** 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; };
函数lock_sys_create在database start之际负责初始化lock_sys_t结构。rec_hash的hash slot数量由srv_lock_table_size变量决定。rec_hash哈希表的key值通过页的space id,page number计算得出。
摘自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) <p> 这将意味着无法提供一个手段使得我们可以直接得知某一行是否上锁。而是应该先通过其所在的页得到space id、page number通过<strong>lock_rec_fold</strong>函数得出key值而后经过hash查询得到lock_rec_t,而后根据heap_no扫描bit map,最终确定锁信息。<strong>lock_rec_get_first</strong>函数实现了上述逻辑:</p><p> 这里返回的其实是<strong>lock_t</strong>对象,摘自<strong>lock0lock.cc</strong>文件</p><pre class="brush:php;toolbar:false">/** * 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的内存对象,其中保存着该事务锁信息链表和正在等待的锁信息。因此存在如下两种途径对锁进行查询:
上述各种数据结构,对其整理关系如下图所示:
注:
- lock_sys_t中的slot颜色与lock_t颜色相同则表明lock_sys_t slot持有lock_t
指针信息,实在是没法连线,不然图很混乱
【相关推荐:mysql视频教程】
Atas ialah kandungan terperinci Artikel untuk membincangkan mekanisme pelaksanaan dalaman kunci Mysql. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!