Detailed Example of MySQL InnoDB Locking Mechanism

Detailed Example of MySQL InnoDB Locking Mechanism

1. InnoDB locking mechanism

The InnoDB storage engine supports row-level locks and transaction processing. A transaction is a logical processing unit consisting of a group of SQL statements. Its ACID characteristics are as follows:

  • Atomicity: Transactions are atomic and indivisible, either executed together or not at all.
  • Consistency: Data remains in a consistent state at the beginning and end of a transaction.
  • Isolation: During the start and end of a transaction, the transaction maintains certain isolation characteristics to ensure that the transaction is not affected by external concurrent data operations.
  • Durability: After the transaction is completed, the data will be persisted to the database.

Concurrent transactions can improve the utilization of database resources and increase the transaction throughput of the database, but concurrent transactions also have some problems, mainly including:

  • Lost Update: Two transactions update the same data, but the second transaction fails and exits midway, causing both modifications to become invalid; because the database does not perform any locking operations at this time, concurrent transactions are not isolated. (This problem no longer exists in modern databases)
  • Dirty Reads: A transaction reads a row of data, but another transaction has already updated this row of data. This is very dangerous and may cause all operations to be rolled back.
  • Non-repeatable read: A transaction reads a row of data twice (or more) but obtains different results. During the two reads, it is possible that another transaction modified the data.
  • Phantom read: A transaction performs two queries during the operation, and the second query result contains data that did not appear in the first query. The main reason for phantom reads is that another transaction inserts new data during the two query processes.

"Update loss" in database concurrency should usually be completely avoided, but preventing update data loss cannot be solved by database transaction control alone. The application needs to add necessary locks to the data to be updated. The above database problems must be solved by the database providing a certain transaction isolation mechanism. In order to avoid problems caused by concurrent database transactions, four transaction isolation levels are defined in the standard SQL specification. Different isolation levels handle transactions differently.

Comparison of database isolation levels

Isolation Level Read data consistency Dirty Read Non-repeatable read Phantom Read
Read Uncommitted
(Read uncommitted)
The lowest level, which only guarantees that physically damaged data will not be read yes yes yes
Read Committed
(Read committed)
Statement level no yes yes
Repeatable Read
(Repeatable read)
Transaction Level no no yes
Serializable
(Serializable)
Highest level, transaction level no no no

The InnoDB storage engine implements four types of row locks: shared lock (S), exclusive lock (X), intention shared lock (IS), and intention exclusive lock (IX).

  • Shared lock: Everyone can read but cannot modify. Only one of them can modify when they hold the exclusive shared lock.
  • Exclusive lock: I want to change it, but you can’t change it or read it (but you can read it with MVCC snapshot)

Understanding Intention Locks

Intention locks do not conflict with row-level S and X locks, but only with table-level S and X locks.

Intention lock is to avoid traversing all row locks

Consider this example:

Transaction A locks a row in the table, making it readable but not writable.

Afterwards, transaction B applies for a write lock for the entire table.

If transaction B succeeds, then in theory it can modify any row in the table, which conflicts with the row lock held by A.

The database needs to avoid this conflict, which means that B's application must be blocked until A releases the row lock.

How does the database determine this conflict?

Step 1: Determine whether the table has been locked by other transactions

Step 2: Determine whether each row in the table is locked by a row lock.

Note step 2. This judgment method is not very efficient because it requires traversing the entire table.

So there is intention lock.

When an intention lock exists, transaction A must first apply for an intention shared lock on the table, and then apply for a row lock on a row if successful.

In the case of intention lock, the above judgment can be changed to

step1: unchanged

Step 2: It is found that there is an intentional shared lock on the table, which means that some rows in the table are locked by shared row locks. Therefore, transaction B's application for a write lock on the table will be blocked.

1.1 Retrieve data through index, apply shared lock, row lock (if not through index, table lock will be used)

1.1 Retrieve data through index, set shared lock and row lock SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
Shared lock on primary key index, other transactions can also obtain shared lock mysql> select * from test where         
id=1 lock in share mode;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 1 |
+----+------+-------+-------+
1 row in set (0.01 sec)
--------------------------------------------------------------------------------
                        Transaction B can also continue to add shared locksmysql> select * from test where         
                        id=1 lock in share mode;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom | 100 | 1 |
                        +----+------+-------+-------+
                        1 row in set (0.01 sec)
                        But it cannot be updated because transaction A also adds a shared lockmysql> update test set level=11 where id=1;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        MORE:
                        Unable to add exclusive lock select *from test where id=1 for update;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        You can update unlocked ones, for example, mysql> update test set level=11 where id=2;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
Transaction A cannot be updated either because transaction B adds a shared lockmysql> update test set level=11 where id=1;
ERROR 1205 (HY000): Lock wait timeout exceeded
ded; try restarting transaction
--------------------------------------------------------------------------------
                        If any one releases the shared lock, the transaction that exclusively holds the shared lock can update mysql> commit;
                        Query OK, 0 rows affected (0.00 sec)
--------------------------------------------------------------------------------
Transaction B releases the lock, and transaction A has exclusive ownership and can update.mysql> update test set level=11 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

1.2 Retrieve data through index, set exclusive lock and row lock

1.2 Retrieve data through index, set exclusive lock and row lock SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
Exclusive lock on the primary key index, other transactions can also obtain shared locks mysql> select * from test where
id=1 for update;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 1 |
+----+------+-------+-------+
1 row in set (0.01 sec)
--------------------------------------------------------------------------------
                        Transaction B cannot continue to acquire exclusive locks and will wait for mysql> select *from test where id=1 for update;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        MORE:
                        It cannot be updated either, because the update also sets an exclusive lock mysql> update test set level=2 where id=1;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        mysql> select * from test where level=1 lock in share mode;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
--------------------------------------------------------------------------------
Transaction A can updatemysql> update test set level=11 where id=1;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
Release the exclusive lock mysql> commit;
Query OK, 0 rows affected (0.00 sec)
--------------------------------------------------------------------------------
                        Transaction A releases the lock, and transaction B can add an exclusive lockmysql> select * from test where id=1 for update;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom | 100 | 1 |
                        +----+------+-------+-------+
                        1 row in set (0.00 sec)

1.3 Update data through index, also set exclusive lock and row lock

For update, insert, and delete statements, an exclusive lock is automatically added

1.3 Update data through index, also set exclusive lock, row lock SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
When updating the row with id=1, an exclusive lock is set on the row, and other transactions cannot update the row.mysql> update test set level=11 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
                        Transaction B cannot update the row with id=1 and will wait for mysql> update test set level=21 where id=1;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        MORE:
                        You cannot set an exclusive lock mysql> select *from test where id=1 for update;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        mysql> select * from test where level=1 lock in share mode;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
--------------------------------------------------------------------------------
Release the exclusive lock mysql> commit;
Query OK, 0 rows affected (0.00 sec)
--------------------------------------------------------------------------------
                        Transaction A releases the lock, and transaction B can add an exclusive lockmysql> select * from test where id=1 for update;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom | 100 | 11|
                        +----+------+-------+-------+
                        1 row in set (0.00 sec)

2.1 Dirty Read

//Dirty read //2.1 Dirty read SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
set session transaction isolation set session transaction isolation level read uncommitted;
level read uncommitted; Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
//Dirty readmysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        rollback;
                        Query OK, 0 rows affected (0.01 sec)
                        
                        mysql> select *from test where id=1;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom | 100 | 1 |
                        +----+------+-------+-------+
                        1 row in set (0.00 sec)

2.2 Non-repeatable read

2.2 Non-repeatable read // Dirty read SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
set session transaction isolation set session transaction isolation level read uncommitted;
level read uncommitted; Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=1000 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
//Non-repeatable reading//Read three times, the first time is level 1, the second time is 100, and the third time is 1000
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 1000|
+----+------+-------+-------+
1 row in set (0.00 sec)

2.3 Phantom Read

//2.3 Phantom reading SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
set session transaction isolation set session transaction isolation level read uncommitted;
level read uncommitted; Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom | 100 | 100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> insert into test (name, money, level) VALUES ('tim', 250, 4);
                        Query OK, 1 row affected (0.01 sec)
--------------------------------------------------------------------------------
//Phantom read//Read twice, the second time has more data of Tim//If it is rr level, you need to use current read select * from test lock in share mode; otherwise, because of MVCC, you can't read Tim's datamysql> select * from test;
+----+-------+-------+-------+
| id | name | money | level |
+----+-------+-------+-------+
| 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 |
| 4 | tim | 250 | 4 |
+----+-------+-------+-------+
4 rows in set (0.00 sec)

3 Gap lock (Net-Key lock)

MVCC enables transactions to read currently at the RR level to avoid phantom read problems in the read case, but what about when writing updates? What should I do if I want to insert new data into the range while updating the range?

So there is a gap lock. When updating the data in a certain interval, all records in this interval will be locked. For example, update XXX where id between 1 and 100 will lock all records with ids between 1 and 100. It is worth noting that if a record does not exist in this interval, the record will also be locked. At this time, if another transaction adds data to this interval, it must wait for the previous transaction to release the lock resources.

There are two purposes for using gap locks: one is to prevent phantom reading; the other is to meet the needs of recovery and assignment.

3.1 Range gap lock, explicit left open and right closed interval

//Gap lock (Net-Key lock) Range gap lock, left open right closed interval SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
     
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
mysql> update test set level=0
where money between 0 and 200;
Query OK, 2 rows affected (0.02 sec)
Rows matched: 2 Changed: 2 Warnings: 0
Theoretically, the range should be locked to [0,300)--------------------------------------------------------------------------------
                        insert money=0waitmysql> insert into test (name, money,level) VALUES ('tim',0,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        insert money=90waitmysql> insert into test (name, money,level) VALUES ('tim',90,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        INSERT money=100 WAIT mysql> insert into test (name, money,level) VALUES ('tim',100,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        INSERT money=299 WAIT mysql> insert into test (name, money,level) VALUES ('tim',299,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        Insert money=300 ok
                        mysql> insert into test (name, money, level) VALUES ('tim',300,0);
                        Query OK, 1 row affected (0.00 sec)

3.2 Single Gap Lock Implicit Interval

The previous section specifies updating a certain range, but what if only one value is updated? Will there still be gap locks?

//Gap lock (Net-Key lock) Single gap lock, left open right closed interval SessionA SessionB
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
     
mysql> select * from test; mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+ +----+-------+-------+-------+              
| id | name | money | level | | id | name | money | level |
+----+-------+-------+-------+ +----+-------+-------+-------+
| 1 | tom | 100 | 1 | | 1 | tom | 100 | 1 |
| 2 | jack | 200 | 2 | | 2 | jack | 200 | 2 |
| 3 | lucas | 300 | 3 | | 3 | lucas | 300 | 3 |
+----+-------+-------+-------+ +----+-------+-------+-------+
3 rows in set (0.00 sec) 3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
mysql> update test set level=0
where money = 200;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Theoretically, the range should be locked to [0,300)--------------------------------------------------------------------------------
                        Insert money=0 ok
                        mysql> insert into test (name, money, level) VALUES ('tim',0,0);
                        Query OK, 1 row affected (0.00 sec)
                        
                        Insert money=90 ok
                        mysql> insert into test (name, money, level) VALUES ('tim',90,0);
                        Query OK, 1 row affected (0.00 sec)
                        
                        INSERT money=100 WAIT mysql> insert into test (name, money,level) VALUES ('tim',100,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        INSERT money=150 WAIT mysql> insert into test (name, money,level) VALUES ('tim',150,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        INSERT money=200 WAITmysql> insert into test (name, money,level) VALUES ('tim',200,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        INSERT money=240 WAIT mysql> insert into test (name, money,level) VALUES ('tim',240,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        Insert money=300 ok
                        mysql> insert into test (name, money, level) VALUES ('tim',300,0);
                        Query OK, 1 row affected (0.00 sec)

When the interval is not specified, the implicit interval is the interval determined by the values ​​of the two nodes before and after the index B+number, which is also left open and right closed. For the above example, it is the interval [0,300).

Summarize

This is the end of this article about MySQL InnoDB locking mechanism. For more information about MySQL InnoDB locking mechanism, please search previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Summary of MySQL InnoDB architecture
  • A brief introduction to MySQL InnoDB ReplicaSet
  • Detailed explanation of memory management of MySQL InnoDB storage engine
  • MySQL Innodb key features insert buffer
  • Summary of MySQL InnoDB locks
  • How to distinguish MySQL's innodb_flush_log_at_trx_commit and sync_binlog
  • In-depth explanation of InnoDB locks in MySQL technology
  • Change the MySQL database engine to InnoDB
  • Briefly describe the MySQL InnoDB storage engine
  • MySQL InnoDB tablespace encryption example detailed explanation
  • MySQL InnoDB transaction lock source code analysis

<<:  404 error occurs when accessing the homepage of tomcat started in Docker mode

>>:  Summary of front-end knowledge in the Gokudō game

Recommend

Steps to use autoconf to generate Makefile and compile the project

Preface Under Linux, compilation and linking requ...

Example of removing json backslash in php

1. Remove backslashes through the "stripslas...

Is mysql a relational database?

MySQL is a relational database management system....

Some settings of Div about border and transparency

frame: Style=”border-style:solid;border-width:5px;...

Vue computed properties

Table of contents 1. Basic Examples 2. Computed p...

Vue two same-level components to achieve value transfer

Vue components are connected, so it is inevitable...

Implementation of waterfall layout + dynamic rendering

Table of contents Typical waterfall website Water...

Not all pop-ups are rogue. Tips on designing website pop-ups

Pop-up news is common in domestic Internet servic...

Briefly talk about mysql left join inner join

Preface I have been busy developing a cold chain ...

JS ES6 asynchronous solution

Table of contents Initially using the callback fu...

The difference between GB2312, GBK and UTF-8 in web page encoding

First of all, we need to understand that GB2312, ...

Docker data volume common operation code examples

If the developer uses Dockerfile to build the ima...