Rumah  >  Artikel  >  pangkalan data  >  Bagaimana untuk menyelesaikan bacaan mysql phantom

Bagaimana untuk menyelesaikan bacaan mysql phantom

WBOY
WBOYke hadapan
2023-06-02 19:13:241654semak imbas

Tahap pengasingan transaksi (tx_isolation)

mysql mempunyai empat tahap pengasingan transaksi Setiap tahap mempunyai aksara atau nombor berangka

级别 symbol 描述
读未提交 READ-UNCOMMITTED 0 存在脏读、不可重复读、幻读的问题
读已提交 READ-COMMITTED 1 解决脏读的问题,存在不可重复读、幻读的问题
可重复读 REPEATABLE-READ 2 mysql 默认级别,解决脏读、不可重复读的问题,存在幻读的问题。使用 MMVC机制 实现可重复读
序列化 SERIALIZABLE 3 解决脏读、不可重复读、幻读,可保证事务安全,但完全串行执行,性能最低

Kita boleh menggunakan arahan berikut untuk melihat/menetapkan tahap pengasingan transaksi global/sesi

mysql> SELECT @@global.tx_isolation, @@tx_isolation;
+-----------------------+------------------+
| @@global.tx_isolation | @@tx_isolation   |
+-----------------------+------------------+
| REPEATABLE-READ       | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.00 sec)

# 设定全局的隔离级别 设定会话 global 替换为 session 即可 把set语法温习一下
# SET [GLOABL] config_name = 'foobar';
# SET @@[session.|global.]config_name = 'foobar';
# SELECT @@[global.]config_name;

SET @@gloabl.tx_isolation = 0;
SET @@gloabl.tx_isolation = 'READ-UNCOMMITTED';

SET @@gloabl.tx_isolation = 1;
SET @@gloabl.tx_isolation = 'READ-COMMITTED';

SET @@gloabl.tx_isolation = 2;
SET @@gloabl.tx_isolation = 'REPEATABLE-READ';

SET @@gloabl.tx_isolation = 3;
SET @@gloabl.tx_isolation = 'SERIALIZABLE';

Bacaan hantu

Pertama sekali, kita perlu memahami apa itu bacaan hantu Pada masa ini terdapat banyak penjelasan mengenai pembacaan hantu di Internet. Catatan blog secara peribadi merasakan bahawa jika anda memikirkannya dengan teliti, anda boleh menemui contoh untuk menterbalikkannya Sama seperti catatan blog yang menyamakan IO tidak menyekat dengan IO asynchronous, dan kemudian banyak artikel. meminjamnya, sebenarnya, kedua-duanya adalah IO tidak menyekat adalah sebahagian daripada IO segerak. Kesalahpahaman orang ramai telah "diperbetulkan", jadi mari kita kembali kepada topik.

Bacaan hantu akan muncul di peringkat RU / RC / RR SERIALIZABLE menghapuskan bacaan hantu bagaimanapun, bacaan kotor dan bacaan tidak boleh diulang akan tetap wujud di bawah RU / RC, jadi kami akan mengkaji hantu di peringkat RR. . Baca dan hapuskan gangguan lain.

Nota: Terdapat kemungkinan bacaan hantu pada tahap RR, tetapi anda juga boleh menggunakan kaedah menambah kunci X secara manual pada rekod untuk menghapuskan bacaan hantu. SERIALIZABLE menghalang bacaan hantu dengan menambahkan kunci X pada semua transaksi, tetapi dalam banyak senario perniagaan kami SQL tidak mempunyai risiko bacaan hantu. Walaupun menggunakan SERIALIZABLE boleh memastikan keselamatan mutlak transaksi, ia akan menyebabkan banyak kerugian yang tidak perlu dalam prestasi. Oleh itu, anda boleh memutuskan sama ada untuk mengunci mengikut keperluan perniagaan di bawah RR Jika terdapat risiko pembacaan hantu, kami akan menguncinya Jika ia tidak wujud, kami tidak akan menguncinya Itulah sebabnya RR, sebagai tahap pengasingan lalai mysql, adalah tahap pengasingan transaksi, jadi adalah perlu Pemahaman yang betul tentang bacaan hantu.

Pemahaman tentang ralat bacaan hantu: Dikatakan bacaan hantu ialah apabila transaksi A melakukan dua operasi pilih untuk mendapatkan set data yang berbeza, iaitu pilih 1 memperoleh 10 rekod, dan pilih 2 memperoleh 11 rekod. Ini sebenarnya bukan bacaan hantu Ini adalah jenis bacaan tidak boleh berulang, yang hanya akan berlaku pada tahap R-U R-C, tetapi tidak akan berlaku pada tahap pengasingan RR lalai mysql.

Berikut adalah pemahaman vernakular saya tentang bacaan hantu:

Bacaan hantu tidak bermakna set hasil yang diperolehi oleh dua bacaan adalah berbeza Fokus bacaan hantu status data yang diwakili oleh hasil operasi pilih tidak boleh menyokong operasi perniagaan berikutnya. Untuk lebih spesifik: pilih sama ada rekod tertentu wujud Jika ia tidak wujud, sediakan untuk memasukkan rekod Walau bagaimanapun, apabila melaksanakan sisipan, didapati rekod itu sudah wujud dan tidak boleh dimasukkan pada masa ini berlaku.

Berikut ialah senario bacaan hantu mysql yang lebih jelas (dipinjam daripada jawapan saya di Zhihu):

table users: id primary key

Transaksi T1

Bagaimana untuk menyelesaikan bacaan mysql phantom


Transaksi T2

Bagaimana untuk menyelesaikan bacaan mysql phantom

step1 T1: PILIH * DARI `pengguna` DI MANA `id` = 1;
step2 T2: MASUKKAN KE DALAM NILAI `pengguna` (1, 'kucing besar');
langkah3 T1: MASUKKAN KE DALAM NILAI `pengguna` (1, 'kucing besar');
langkah4 T1: PILIH * DARI `pengguna` DI MANA `id` = 1;

T1: Transaksi utama, mengesan sama ada terdapat rekod dengan id 1 dalam jadual, dan memasukkannya jika tidak Ini adalah logik perniagaan biasa yang kami harapkan.

T2: Transaksi gangguan, tujuannya adalah untuk mengganggu pelaksanaan transaksi biasa T1.

Di bawah tahap pengasingan RR, langkah1 dan langkah2 akan dilaksanakan seperti biasa, tetapi langkah3 akan melaporkan ralat konflik utama utama Untuk perniagaan T1, pelaksanaan di sini T1 mempunyai baca hantu

Malah, RR juga boleh mengelakkan bacaan hantu dengan menambah baris secara manual Anda juga perlu tahu bahawa walaupun rekod semasa tidak wujud, contohnya, id=1 tidak wujud, transaksi semasa akan memperoleh kunci rekod (kerana kunci baris InnoDB mengunci indeks, tidak kira sama ada entiti rekod wujud atau tidak, jika wujud, tambah kunci Baris X, jika tidak wujud, tambah jurang kunci kunci seterusnya

Di bawah tahap pengasingan SERIALIZABLE, kunci baris (X)/jurang (X) akan ditambah secara tersirat apabila langkah1 dilaksanakan, jadi langkah2 akan disekat dan langkah3 akan dilaksanakan seperti biasa Selepas T1 diserahkan, T2 boleh teruskan melaksanakan (pelaksanaan konflik utama gagal).

所以 mysql 的幻读并非什么读取两次返回结果集不同,而是事务在插入事先检测不存在的记录时,惊奇的发现这些数据已经存在了,之前的检测读获取到的数据如同鬼影一般。

这里要灵活的理解读取的意思,第一次select是读取,第二次的 insert 其实也属于隐式的读取,只不过是在 mysql 的机制中读取的,插入数据也是要先读取一下有没有主键冲突才能决定是否执行插入。

不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。

RR级别下防止幻读

RR级别下只要对 SELECT 操作也手动加行(X)锁即可类似 SERIALIZABLE 级别(它会对 SELECT 隐式加锁),即大家熟知的:

# 这里需要用 X锁, 用 LOCK IN SHARE MODE 拿到 S锁 后我们没办法做 写操作
SELECT `id` FROM `users` WHERE `id` = 1 FOR UPDATE;

如果 id = 1 的记录存在则会被加行(X)锁,如果不存在,则会加 next-lock key / gap 锁(范围行锁),即记录存在与否,mysql 都会对记录应该对应的索引加锁,其他事务是无法再获得做操作的。

这里我们就展示下 id = 1 的记录不存在的场景,FOR UPDATE 也会对此 “记录” 加锁,要明白,InnoDB 的行锁(gap锁是范围行锁,一样的)锁定的是记录所对应的索引,且聚簇索引同记录是直接关系在一起的。

Bagaimana untuk menyelesaikan bacaan mysql phantom

id = 1 的记录不存在,开始执行事务:
step1: T1 查询 id = 1 的记录并对其加 X锁
step2: T2 插入 id = 1 的记录,被阻塞
step3: T1 插入 id = 1 的记录,成功执行(T2 依然被阻塞中),T1 提交(T2 唤醒但主键冲突执行错误)
T1事务符合业务需求成功执行,T2干扰T1失败。

SERIALIZABLE级别杜绝幻读

在这个层面上,我们不必对 SELECT 操作进行显式加锁,因为InnoDB会自动加锁以确保事务的安全性,但是这会导致性能较低

Bagaimana untuk menyelesaikan bacaan mysql phantom

step1: T1 查询 id = 2 的记录,InnoDB 会隐式的对齐加 X锁
step2: T2 插入 id = 2 的记录,被阻塞
step3: T1 插入 id = 2 的记录,成功执行(T2 依然被阻塞中)
step4: T1 成功提交(T2 此时唤醒但主键冲突执行错误)
T1事务符合业务需求成功执行,T2干扰T1失败。

Atas ialah kandungan terperinci Bagaimana untuk menyelesaikan bacaan mysql phantom. 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