Why does the MySQL workbench show uncommitted changes?
-- create a new empty table
DROP TABLE IF EXISTS X;
CREATE TABLE X (val varchar(10));
-- disable autocommit
SET AUTOCOMMIT = 0;
-- insert a row without committing
INSERT INTO X (val) VALUES ('text');
At this point
SELECT ##autocommit, ##tx_isolation;
returns
| 0 | REPEATABLE-READ |
However, a query shows the value which has not yet been committed:
SELECT * FROM X;
| text |
Why does MySQL return a result set with uncommitted data?
If I roll back the transaction using
ROLLBACK;
then MySQL returns an empty table when querying X. (That confirms that the transaction hasn't been committed automatically at some point of time.)
The reason is that I am always in the same transaction (regardless the window tab).
Related
I'm using a MySQL DB for my site, which is hosted on a Linux shared server.
I wrote a test script which I run using 'mysql' to test if transactions are working ok. Running the script, I do not get any error, but the result of executing the scripts is as if transaction is not enabled.
I also made sure to grant ALL privileges to the admin MySQL user which runs the script.
In order to double check, I tried the same test script on PostgreSQL, and there - the result of the script indicated that transaction does work. So it's definitely something which is specific to MySQL.
The script runs on a simple table which I created as follows:
create table a ( id serial primary key);
Following is the test script:
delete from a;
set autocommit = 0;
start transaction;
insert into a(id) values(1);
rollback work;
select count(*) from a;
So the script makes sure the table is empty, Then it starts a transaction, insert a row and rollback the insert. As the "insert" is rolled back, the "select" should indicate that table contains 0 rows.
Running this on PostgreSQL:
$ psql db admin < test1
DELETE 0
START TRANSACTION
INSERT 0 1
ROLLBACK
count
-------
0
This is the expected behavior, 0 rows in the table as the insert was rolled back.
Running the same on my MySQL DB:
$ mysql db -u admin < test1
count(*)
1
Having 1 row following the rollback indicate that the "insert" was not rolled back, just as in non-transaction mode.
As mentioned, admin is granted with ALL privileges to the DB.
Anything I've missed?
Probably the table is created with the MyISAM storage engine as default.
MyISAM storage engine doesnt support transactions.
Create table
CREATE TABLE a ( id SERIAL PRIMARY KEY) ENGINE = MYISAM;
Query
DELETE FROM a;
SET autocommit = 0;
START TRANSACTION;
INSERT INTO a(id) VALUES(1);
ROLLBACK WORK;
SELECT COUNT(*) FROM a;
Result
count(*)
1
Making the table InnoDB
Query
ALTER TABLE a ENGINE=INNODB;
Query
DELETE FROM a;
SET autocommit = 0;
START TRANSACTION;
INSERT INTO a(id) VALUES(1);
ROLLBACK WORK;
SELECT COUNT(*) FROM a;
Result
count(*)
----------
0
I want to create a Lost Update with MySQL Workbench. Therefore, I have 2 connections to my database and 2 transactions. I also changed the transaction isolation level to read uncommitted but transaction A uses the current data when the update statement starts. It never uses the data from the first select statement and with select ... for update the transaction b is blocked.
Transaction A (starts first):
Start transaction;
SELECT * FROM table;
Select sleep(10); -- <- Transaction B executes in this 10 seconds
UPDATE table SET Number = Number + 10 WHERE FirstName = "Name1";
COMMIT;
Transaction B:
Start transaction;
UPDATE table SET Number = Number - 5 WHERE FirstName = "Name1";
COMMIT;
Is it possible to create this failure with MySQL Workbench. What´s wrong with my code?
Thanks for your help
The update in A work with data after the sleep is executed. Select before does nothing in the transaction.
I'm using a MySQL DB for my site, which is hosted on a Linux shared server.
I wrote a test script which I run using 'mysql' to test if transactions are working ok. Running the script, I do not get any error, but the result of executing the scripts is as if transaction is not enabled.
I also made sure to grant ALL privileges to the admin MySQL user which runs the script.
In order to double check, I tried the same test script on PostgreSQL, and there - the result of the script indicated that transaction does work. So it's definitely something which is specific to MySQL.
The script runs on a simple table which I created as follows:
create table a ( id serial primary key);
Following is the test script:
delete from a;
set autocommit = 0;
start transaction;
insert into a(id) values(1);
rollback work;
select count(*) from a;
So the script makes sure the table is empty, Then it starts a transaction, insert a row and rollback the insert. As the "insert" is rolled back, the "select" should indicate that table contains 0 rows.
Running this on PostgreSQL:
$ psql db admin < test1
DELETE 0
START TRANSACTION
INSERT 0 1
ROLLBACK
count
-------
0
This is the expected behavior, 0 rows in the table as the insert was rolled back.
Running the same on my MySQL DB:
$ mysql db -u admin < test1
count(*)
1
Having 1 row following the rollback indicate that the "insert" was not rolled back, just as in non-transaction mode.
As mentioned, admin is granted with ALL privileges to the DB.
Anything I've missed?
Probably the table is created with the MyISAM storage engine as default.
MyISAM storage engine doesnt support transactions.
Create table
CREATE TABLE a ( id SERIAL PRIMARY KEY) ENGINE = MYISAM;
Query
DELETE FROM a;
SET autocommit = 0;
START TRANSACTION;
INSERT INTO a(id) VALUES(1);
ROLLBACK WORK;
SELECT COUNT(*) FROM a;
Result
count(*)
1
Making the table InnoDB
Query
ALTER TABLE a ENGINE=INNODB;
Query
DELETE FROM a;
SET autocommit = 0;
START TRANSACTION;
INSERT INTO a(id) VALUES(1);
ROLLBACK WORK;
SELECT COUNT(*) FROM a;
Result
count(*)
----------
0
MySQL provides an automatic mechanism to increment record IDs. This is OK for many purposes, but I need to be able to use sequences as offered by ORACLE. Obviously, there is no point in creating a table for that purpose.
The solution SHOULD be simple:
1) Create a table to hosts all the needed sequences,
2) Create a function that increases the value of a specific sequence and returns the new value,
3) Create a function that returns the current value of a sequence.
In theory, it looks simple... BUT...
When increasing the value of a sequence (much the same as nextval in Oracle), you need to prevent other sessions to perform this operation (or even fetch the current value) till the updated is completed.
Two theoretical options:
a - Use an UPDATE statement that would return the new value in a single shot, or
b - Lock the table between the UPDATE and SELECT.
Unfortunately, it would appear that MySQL does not allow to lock tables within functions / procedures, and while trying to make the whole thing in a single statement (like UPDATE... RETURNING...) you must use #-type variables which survive the completion of the function/procedure.
Does anyone have an idea/working solution for this?
Thanks.
The following is a simple example with a FOR UPDATE intention lock. A row-level lock with the INNODB engine. The sample shows four rows for next available sequences that will not suffer from the well-known INNODB Gap Anomaly (the case where gaps occur after failed usage of an AUTO_INCREMENT).
Schema:
-- drop table if exists sequences;
create table sequences
( id int auto_increment primary key,
sectionType varchar(200) not null,
nextSequence int not null,
unique key(sectionType)
) ENGINE=InnoDB;
-- truncate table sequences;
insert sequences (sectionType,nextSequence) values
('Chassis',1),('Engine Block',1),('Brakes',1),('Carburetor',1);
Sample code:
START TRANSACTION; -- Line1
SELECT nextSequence into #mine_to_use from sequences where sectionType='Carburetor' FOR UPDATE; -- Line2
select #mine_to_use; -- Line3
UPDATE sequences set nextSequence=nextSequence+1 where sectionType='Carburetor'; -- Line4
COMMIT; -- Line5
Ideally you do not have a Line3 or bloaty code at all which would delay other clients on a Lock Wait. Meaning, get your next sequence to use, perform the update (the incrementing part), and COMMIT, ASAP.
The above in a stored procedure:
DROP PROCEDURE if exists getNextSequence;
DELIMITER $$
CREATE PROCEDURE getNextSequence(p_sectionType varchar(200),OUT p_YoursToUse int)
BEGIN
-- for flexibility, return the sequence number as both an OUT parameter and a single row resultset
START TRANSACTION;
SELECT nextSequence into #mine_to_use from sequences where sectionType=p_sectionType FOR UPDATE;
UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType;
COMMIT; -- get it and release INTENTION LOCK ASAP
set p_YoursToUse=#mine_to_use; -- set the OUT parameter
select #mine_to_use as yourSeqNum; -- also return as a 1 column, 1 row resultset
END$$
DELIMITER ;
Test:
set #myNum:= -1;
call getNextSequence('Carburetor',#myNum);
+------------+
| yourSeqNum |
+------------+
| 4 |
+------------+
select #myNum; -- 4
Modify the stored procedure accordingly for you needs, such as having only 1 of the 2 mechanisms for retrieving the sequence number (either the OUT parameter or the result set). In other words, it is easy to ditch the OUT parameter concept.
If you do not adhere to ASAP release of the LOCK (which obviously is not needed after the update), and proceed to perform time consuming code, prior to the release, then the following can occur after a timeout period for other clients awaiting a sequence number:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting
transaction
Hopefully this is never an issue.
show variables where variable_name='innodb_lock_wait_timeout';
MySQL Manual Page for innodb_lock_wait_timeout.
On my system at the moment it has a value of 50 (seconds). A wait of more than a second or two is probably unbearable in most situations.
Also of interest during TRANSACTIONS is that section of the output from the following command:
SHOW ENGINE INNODB STATUS;
update my_table set limit_id = 2 where id='176846';
start transaction;
update my_table set limit_id = 1 where id='176846';
update my_table set limit_id = 4 where id='176846'; -- <- this one fails
commit;
select limit_id from my_table where id='176846';
I would like to roll this back automatically - I want the script to output 2, not 1. I have no access to the connection policy in use.
reading here:
http://dev.mysql.com/doc/refman/5.5/en/commit.html
By default, MySQL runs with autocommit mode enabled. This means that
as soon as you execute a statement that updates (modifies) a table,
MySQL stores the update on disk to make it permanent. The change
cannot be rolled back.
try something like
SET autocommit = 0;
start transaction;
(...)
commit;
It depends on why a limit_id value of 4 causes an error, but MySql does not always roll back the entire transaction. See: http://dev.mysql.com/doc/refman/5.7/en/innodb-error-handling.html for more information, but in several cases, MySql will only implicitly rollback the last statement, then continue with the transaction.