MySQL MyISAM how to perform a read without locking a table? - mysql

My question is a follow up to this answer. I want to find out how to perform a select statement without locking a table with MyISAM engine.
The answer states the following if you have InnoDB but not MyISAM. What is the equivalent for MyISAM engine?
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;

This is the default behaviour with MyISAM tables. If one actually wants to lock a MyISAM table, one must manually acquire a table-level lock. Transaction isolation level, START TRANSACTION, COMMIT, ROLLBACK have no effect on MyISAM tables behaviour since MyISAM does not support transactions.
More about internal locking mechanisms
A READ lock is implicitely acquired before, and released after execution of a SELECT statement. Notice that several concurrent, simultaneous, SELECT statements could be running at the same time, because several sessions may hold a READ lock on the same table.
Conversely, a WRITE lock is implicitely acquired before executing an INSERT or UPDATE or DELETE statement. This means that no read (let alone a concurrent write) can take place as long as a write is in progress*.
The above applies to MyISAM, MEMORY, and MERGE tables only.
You might want to read more about this here:
Internal locking methods
Read vs Write locks
* However, these locks are not always required thanks to this clever trick:
The MyISAM storage engine supports concurrent inserts to reduce contention between readers and writers for a given table: If a MyISAM table has no free blocks in the middle of the data file, rows are always inserted at the end of the data file. In this case, you can freely mix concurrent INSERT and SELECT statements for a MyISAM table without locks.

MyISAM does indeed use a read lock during SELECT. An INSERT at the end of the table can get around that.
But try doing an UPDATE, DELETE, or ALTER TABLE while a long-running SELECT is in progress. Or vice-versa, reading from a table while a change to that table is running. It's first-come, first-serve, and the later thread blocks until the first thread is done.
MyISAM doesn't have any support for transactions, so it must work this way. If a SELECT were reading rows from a table, and a concurrent thread changes some of those rows, you would get a race condition. The SELECT may read some of the rows before the change, and some of the rows after the change, resulting in a completely mixed-up view of the data.
Anything you do with SET TRANSACTION ISOLATION LEVEL has no effect with MyISAM.
For these reasons, it's recommended to use InnoDB instead.

Related

MySQL - Update table rows without locking the rows

I have requirement where we need to update the row without holding the lock for the while updating.
Here is the details of the requirements, we will be running a batch processing on a table every 5 mins update blogs set is_visible=1 where some conditions this query as to run on millions of records so we don't want to block all the rows for write during updates.
I totally understand the implications of not having write locks which is fine for us because is_visible column will be updated only by this batch process no other thread wil update this column. On the other hand there will be lot of updates to other columns of the same table which we don't want to block
First of all, if you default on the InnoDB storage engine of MySQL, then there is no way you can update data without row locks except setting the transaction isolation level down to READ UNCOMMITTED by running
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
However, I don't think the database behavior is what you expect since the dirty read is allowed in this case. READ UNCOMMITTED is rarely useful in practice.
To complement the answer from #Tim, it is indeed a good idea to have a unique index on the column used in the where clause. However, please note as well that there is no absolute guarantee that the optimizer will eventually choose such execution plan using the index created. It may work or not work, depending on the case.
For your case, what you could do is to split the long transaction into multiple short transactions. Instead of updating millions of rows in one shot, scanning only thousands of rows each time would be better. The X locks are released when each short transaction commits or rollbacks, giving the concurrent updates the opportunity to go ahead.
By the way, I assume that your batch has lower priority than the other online processes, thus it could be scheduled out of peak hours to further minimize the impact.
P.S. The IX lock is not on the record itself, but attached to the higher-granularity table object. And even with REPEATABLE READ transaction isolation level, there is no gap lock when the query uses a unique index.
Best practice is to always acquire a specific lock when there is a chance that an update could happen concurrently with other transactions. If your storage engine be MyISAM, then MySQL will lock the entire table during an update, and there isn't much you can do about that. If the storage engine be InnoDB, then it is possible that MySQL would only put an exclusive IX lock on the records targeted by the update, but there are caveats to this being the case. The first thing you would do to try to achieve this would be a SELECT ... FOR UPDATE:
SELECT * FROM blogs WHERE <some conditions> FOR UPDATE;
In order to ensure that InnoDB only locks the records being updated, there needs to be a unique index on the column which appears in the WHERE clause. In the case of your query, assuming id were the column involved, it would have to be a primary key, or else you would need to create a unique index:
CREATE UNIQUE INDEX idx ON blogs (id);
Even with such an index, InnoDB may still apply gap locks on the records in between index values, to ensure that the REPEATABLE READ contract is enforced.
So, you may add an index on the column(s) involved in your WHERE clause to optimize the update on InnoDB.

Stopping table locks for MYISAM tables

I have a MYISAM table on which long-running SELECT statements are conducted. Do these statements cause locks on the tables that are being read? If so, how do you stop the locks from occurring?
According to https://dev.mysql.com/doc/refman/5.7/en/internal-locking.html:
MySQL uses table-level locking for MyISAM, MEMORY, and MERGE tables,
permitting only one session to update those tables at a time. This
locking level makes these storage engines more suitable for read-only,
read-mostly, or single-user applications.
Does this also mean that SELECT statements create locks?
Do these [long-running SELECT] statements cause locks on the tables that are being read?
Yes. A MyISAM table cannot be updated while it is being read from. If there is a long-running read (i.e, SELECT) query on the table, any UPDATE or DELETE queries will have to wait until the read query is complete.
(INSERT queries are sometimes an exception. So long as there are no internal holes in the table structure, a writer may be allowed to insert a row at the end of a table without obtaining a lock. However, if any rows are ever updated or deleted, this may create holes, which will require INSERT queries to obtain a write lock as well.)
For more information on the internals, read up on table-level locks. But the short and the long of matters is, you probably shouldn't use MyISAM if concurrency is a concern.

Does COUNT(*) wait for row locks in InnoDB?

Does MySQL InnoDB table wait for write locks even for query such as SELECT COUNT(*) FROM t?
My situation:
I've got table with 50000 rows with many updates (views count in every row). InnoDB should put a write lock on the updated row. But when I make a query with only COUNT(*) on this table, MySQL could answer this query even without waiting for write locks because no UPDATE will change the number of rows.
Thanks a lot!
No, MySql doesn't lock InnoDb tables for queries that only read data from tables.
This is only the case for old MyIsam tables, where all readers must wait until the writer is done and vice versa.
For InnoDb tables they implemented Multiversion concurrency control
In MySql terminology it is called Consistent Nonlocking Reads
In short - when the reader starts the query, the database makes a snapshot of the database at a point in time when the query was started, and the reader (the query) sees only changes made visible (commited) up to this point in time, but doesn't see changes made by later transactions. This allows readers to read data without locking and waiting for writers, but still keeping ACID
There are subtle differences depending on the transaction isolation level, you can find detailed description here: http://dev.mysql.com/doc/refman/5.6/en/set-transaction.html
In short - in read uncommited, read commited and repeatable read modes, all SELECT statements that only read data (SELECTs without FOR UPDATE or LOCK IN SHARE MODE clasues) are performed in a nonlocking fashion.
In serializable mode all transacions are serialized and, depending on autocommit mode, SELECT can be blocked when conflicts with other transactions (when autocommit=true), or is automatically converted to SELECT ... LOCK IN SHARE MODE (when autocommit=false). All details are explained in the above links.

Is MyISAM Table locking in MySQL automatic?

Does MySQL automatically peform read / write table locks on MyIsam tables, or do I have to explicitly lock the tables?
Beyond making reasonable efforts to make certain statements atomic, MyISAM doesn't have the concept of transactions and its related row level locking.
Therefore, you should use LOCK TABLES to avoid race conditions or data inconsistencies when you use multiple statements (e.g. a SELECT statement followed by multiple related UPDATE statements).
See here about Pros and Cos Of MyISAM
MyISAM's About Internal Locking
MyISAM uses table-level locking. When a row is inserted or updated, all other changes to that table are held up until that request has been completed.
Correct me if 'm wrong

MYSQL - Locking - InnoDB

I am using mysql with InnoDB databases.
If all my transactions are Inserts and Selects (no updates), I assume I would not have to worry about SQL deadlocking.
I can't see a scenario where deadlocking would occur. Am I correct to assume deadlocking cannot occur if I only do Inserts and Selects?
May not be relevant but everything transaction is done with PDO
No. You still have to worry about SQL deadlocking.
You can get deadlocks even in the case of a transaction that inserts a single row. This is because the insert operation is not really atomic and locks are set automatically on the (possibly several) index records of the inserted row.
InnoDB MySQL storage engine has row level locks while the MyISAM MySQL storage engine has table level locks. MyISAM simply locks entire tables, and doesn't support transactions, so it's not possible to have database-level deadlocks. Note that an app can lock up another app by sitting on a table lock on the table they are both trying to access, but this is a code error, not a db-level "deadlock".
InnoDB supports transactions and has row-level locks, so db-level deadlocks are possible (and can happen occasionally in a busy system so you do need to code around them). Many of what MySQL will call "deadlocks" aren't "true deadlocks" as much as they're the result of slow UPDATEs causing other queries to time out on row locks.