can primary key cause mysql deadlock? - mysql

I suffer a strange mysql deadlock recently, my tables look like(for simplicity I deleted unrelevant columns):
CREATE TABLE Node (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
`nodeId` varchar(128) NOT NULL UNIQUE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE JobQueue (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
`workerManagementNodeId` varchar(32) DEFAULT NULL,
CONSTRAINT `fkJbqMgmtNodeId` FOREIGN KEY (`workerManagementNodeId`) REFERENCES `Node` (`nodeId`) ON DELETE SET NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
when my node goes down, it will delete record in Node table. At this point, job queue may be in process of deleting a queue in JobQueue table which has a foreign key to Node.nodeId. Then mysql throws out an exception:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
I checked the database, JobQueue was successfully deleted but Node wasn't. I understand order of foreign key may cause deadlock, but in my case Node table has no foreign key only primary key. Then how can the deadlock happen?
BTW: I am pretty sure the deadlock is caused by JobQueue, I spent much time narrowing down this problem so in my test only these two tables will be used.
UPDATE:
CREATE TABLE JobQueueEntry (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`jobQueueId` bigint unsigned NOT NULL,
`issuerManagementNodeId` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fkJbqEtryMgmtNodeId` FOREIGN KEY (`issuerManagementNodeId`) REFERENCES `Node` (`nodeId`) ON DELETE SET NULL,
CONSTRAINT `fkJobQueueId` FOREIGN KEY (`jobQueueId`) REFERENCES `JobQueue` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I finally notice it's still an issue caused by foreign key order. there is actually another table JobQueueEntry which has both foreign key to Node and JobQueue in reversed order. so when deleting a node it's trying to update JobQueue and JobQueueEntry. the deadlock happens because JobQueueEntry has foreign key to JobQueue before node.
thanks #ctrl's answer!

First of all this should be a comment but I don't have enough rep for now, so... I base my "comment" on my Oracle experience, but I think it's a common issue and also mysql can behave in the same way.
Since you have a fk on delete set null, when you remove something from Node the db engine has to go thru JobQueue to update it, and it probably acquires a table lock to do this (oracle does it in your situation). If you have multiple actors, some updating/deleting Jobs table and some updating/deleting JobsQueue table, you can end up with a deadlock.
In Oracle, to fix this (and to get better performance) you usually create an index on the fk columns of the child table, in you case workerManagementNodeId.
In case mysql does this in a different and smarter way, I beg your pardon :)

Related

How can I delete the rows of a table which stores foreign keys?

THE SOLUTION IS BELOW
I have three tables like the following:
CREATE TABLE `t_arch_layer` (
`arch_layer_id` int(11) NOT NULL AUTO_INCREMENT,
`arch_layer_name` varchar(45) NOT NULL,
PRIMARY KEY (`arch_layer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
CREATE TABLE `t_tech` (
`tech_id` int(11) NOT NULL AUTO_INCREMENT,
`tech_name` varchar(45) DEFAULT NULL,
`tech_type_id` int(11) NOT NULL,
`tech_icon` text,
PRIMARY KEY (`tech_id`),
KEY `fk_t_tech_t_tech_type1_idx` (`tech_type_id`),
CONSTRAINT `fk_t_tech_t_tech_type1` FOREIGN KEY (`tech_type_id`) REFERENCES `t_tech_type` (`tech_type_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
CREATE TABLE `t_arch_layer_tech` (
`arch_layer_id` int(11) NOT NULL,
`tech_id` int(11) NOT NULL,
PRIMARY KEY (`tech_id`,`arch_layer_id`),
KEY `fk_t_layer_has_t_tech_t_tech1_idx` (`tech_id`),
KEY `fk_t_layer_has_t_tech_t_layer1_idx` (`arch_layer_id`),
CONSTRAINT `fk_t_layer_has_t_tech_t_layer1` FOREIGN KEY (`arch_layer_id`) REFERENCES `t_arch_layer` (`arch_layer_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_t_layer_has_t_tech_t_tech1` FOREIGN KEY (`tech_id`) REFERENCES `t_tech` (`tech_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Basically it's a tipical situation where one table use two foreign keys from another two different tables. This table stores the possible combinations between the layers and technologies so it can't store any combination of layer_id and tech_id which is not in both.
But there is a problem, I need to delete whenever I want some row from t_arch_layer_tech. This it's impossible due to the foreign keys, I know it.
My question is, is there something to use the foreign key as a reference to forbide insert values that there aren't into t_tech or t_arch_layer and also to be consider as "own fields" (I can't explain better) of the table in order to delete any row of the t_arch_layer_tech table? Delete t_tech and t_arch_layer tables to avoid the foreign keys and then set the limits into the t_arch_layer_tech is not a solution.
SOLUTION
When that error appears it's neccesary to check the DB relationships and read carefully the provided message. It seems useless but it helped me to understand what's happening with the t_arch_layer_tech FK. I was using them into another table BUT separately, not as a compound FK. This is the reason because I could insert some rows into t_arch_layer_tech and delete only specific pairs.
So, summarizing, if you are going to use FKs that exist together (as my pair "arch_layer_id, tech_id") create ONLY ONE FK which is a compound FK that uses the mentioned.

Odd Error in mariaDB Foreign Keys

Hi i hope some one can help my problem is that when i try to add a foreign key constraint i get this error.
My database name is "hazard"
Child:
CREATE TABLE `child` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`a` INT(11) NULL DEFAULT NULL,
`b` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
Parent:
CREATE TABLE `parent` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`alfa` INT(11) NULL DEFAULT NULL,
`beta` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
Those are the create codes (using HeidiSQL)
and when i try to add a foreign key
with
ALTER TABLE CHILD MODIFY COLUMN A INT,add constraint fk_parent_child FOREIGN KEY(A) REFERENCES PARENT(ALFA);
or
ALTER TABLE CHILD add constraint fk1 foreign key (a) references parent(alfa);
I get the same error
Can't create table 'hazard.#sql-d04_53' (errno: 150)
this is happening to many of my classmates using MariaDB and mySQL
Beforehand an apology for the inconvenience and I hope you guys can help us.
Add
KEY (`alfa`)
to the parent table. "The referenced columns must be a PRIMARY KEY or a UNIQUE index." – https://mariadb.com/kb/en/mariadb/foreign-keys/
http://sqlfiddle.com/#!9/b4c12
Error 150 usually means you are updating the tables in the wrong order. That is, your first INSERT violates the FOREIGN KEY constraint that your second INSERT will fix.
In your case you are doing ALTER instead of INSERT. Swap the order of the ALTERs. If that does not work, check the data to see that you won't be violating FK constraints. If you get past that, read on...
In extreme cases, you can turn off foreign key constraints while doing the inserts, then turn them back on. (But that leaves you vulnerable to screw-ups.)

SQL issue, mutual constraints, "a foreign key constraint fails"

I have a problem with mutual constraints.
I want to have two tables each having a constraint on the other one.
I'm working with Doctrine2 (but it's not related to the problem), here is my simplified code:
SQL:
CREATE TABLE IF NOT EXISTS `thread` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`last_message_id` int(11) DEFAULT NULL,
`subject` varchar(255) NOT NULL
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_C023F2BBBA0E79C3` (`last_message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `thread`
ADD CONSTRAINT `FK_C023F2BBBA0E79C3` FOREIGN KEY (`last_message_id`) REFERENCES `message` (`id`);
CREATE TABLE IF NOT EXISTS `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`thread_id` int(11) DEFAULT NULL,
`body` longtext NOT NULL
PRIMARY KEY (`id`),
KEY `IDX_9E4E8B5FA76ED395` (`user_id`),
KEY `IDX_9E4E8B5FE2904019` (`thread_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `message`
ADD CONSTRAINT `FK_9E4E8B5FE2904019` FOREIGN KEY (`thread_id`) REFERENCES `thread` (`id`) ON DELETE CASCADE;
Doctrine2 mapping (which generated the SQL code above):
<?php
class Thread
{
/* #ORM\OneToOne() */
private $lastMessage;
}
class Message
{
/* #ORM\ManyToOne() */
private $thread;
}
And when I try to delete either a thread or a message, I get (logically) the error:
Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails ('thread', CONSTRAINT 'FK_C023F2BBBA0E79C3' FOREIGN KEY ('last_message_id') REFERENCES 'message' ('id'))
So, is there a way to avoid this error?
Or should I forget mutual constraints?
Anything?
I want to add that I want to keep the last_message_id because I want to display the threads with infos on their last message, and making a (paginated) query without this reference to the last message was a total nightmare...
Thanks!
Circular paths in FOREIGN KEY constraints are hard to deal with and your problem is an example. If you can avoid them, do that. Here's one way to redesign your tables:
First, add a UNIQUE KEY in table message on (thread_id, message_id) (or make it the Primary Key, if Doctrine can do that. That would mean - for MySQL- that message(id) would not be auto-incremented but produced by the ORM. You may don't want that if you plan to have applications that access the database directly or through other ORMs).
Then move the last_message_id to a new table that has a 1-to-1 relationship with message though the compound (thread_id, message_id). In this table, the thread_id would be Unique so every thread has exactly one last message.
I'll write the SQL code here. This page will help you with the Doctrine code which may produce slightly different structure: Compound Primary and Foreign Keys
CREATE TABLE IF NOT EXISTS `thread` (
`id` int(11) NOT NULL AUTO_INCREMENT,
---`last_message_id` int(11) DEFAULT NULL, --- REMOVED: last_message
`subject` varchar(255) NOT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`thread_id` int(11) NOT NULL, --- why was it NULL ?
`body` longtext NOT NULL
PRIMARY KEY (`id`),
KEY `IDX_9E4E8B5FA76ED395` (`user_id`),
---KEY `IDX_9E4E8B5FE2904019` (`thread_id`), --- REMOVED, not needed any more
--- because we have a this key
UNIQUE KEY (thread_id, id) --- ADDED, needed for the FK below
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `message`
ADD CONSTRAINT `FK_9E4E8B5FE2904019`
FOREIGN KEY (`thread_id`)
REFERENCES `thread` (`id`)
ON DELETE CASCADE;
And the new table, to store the last message for each thread:
CREATE TABLE IF NOT EXISTS `thread_last_message` (
`message_id` int(11) NOT NULL,
`thread_id` int(11) NOT NULL,
PRIMARY KEY (`thread_id`),
KEY (`thread_id`, message_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `thread_last_message` --- which just means
ADD CONSTRAINT `FK_something` --- that every
FOREIGN KEY (`thread_id`, `message_id`) --- thread's last message
REFERENCES `message` (`thread_id`, `id`) --- is a message
ON DELETE CASCADE;
Another possibility is to have the thread(last_message_id) column NULL and change the FK constraints appropriately (as #Eric's proposal). This is less fussy in the design phase and you have one table less to deal with. You have to be careful with the order of inserts and deletes in this approach - as your example shows.
As a third option, have you thought if you really need a thread(last_message_id) column in your table? Couldn't this be a computed (from the two tables) value and you skip the whole issue? If it was a best_message_id I would understand this but the last message is just the last row in another table, ordered by time. You can find that with a query and you don't need to store it (again) in the database, unless there are performance reasons.
The best solution I can think of would be to add a ON DELETE CASCADE constraint to the FK on the Thread table. That way if you delete the thread, the associated messages would be automatically deleted as well.
Similarly, you would need to add a ON DELETE SET NULL constraint on the Messages table FK so that if you deleted the last message in a Thread, it would set the last_message_id to NULL on the Thread table.
Or you could just do logical (soft) deletes instead of hard deletes, which would also solve the problem.
ETA:
Now that you've posted the constraints, this is the one you would have to modify:
ALTER TABLE `thread`
ADD CONSTRAINT `FK_C023F2BBBA0E79C3` FOREIGN KEY (`last_message_id`)
REFERENCES `message` (`id`) ON DELETE SET NULL;
If you have mutual constraints (ie every message has a thread and every thread has a message) why can't you combine this into one table? Seems to make more sense that way
This solution does not require altering the schema, which by the way, you have to undo.
If you want to remove a thread, the messages on that thread do not make sense either, so:
-- break one end of the mutual constraint
update thread set last_message_id = NULL where id = <thread_id_to_delete>;
delete from message where thread_id = <thread_id_to_delete>
delete from threads where id = <thread_id_to_delete>
(Disclaimer: I did not test this exact code, but a similar one)

MySQL attempting to delete all rows which are not constrained by foreign key

Okay, this is (probably) a very simple question, but I am afraid I know almost no MySQL, so please put up with me. I'm just trying to delete every row from one table which is not constrained by a Foreign Key in another table - a specific table, there are only two tables involved here. The create statements look a bit like:
CREATE TABLE `testschema`.`job` (
`Job_Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Comment` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Job_Id`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ermieimporttest`.`jobassignment` (
`JobAssignment_Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`JobId` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`JobAssignment_Id`),
KEY `FK_jobassignment_1` (`JobId`),
CONSTRAINT `FK_jobassignment_1` FOREIGN KEY (`JobId`) REFERENCES `job` (`Job_Id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Any my SQL statement is:
DELETE FROM job USING job INNER JOIN jobAssignment WHERE job.Job_Id != jobAssignment.JobId;
I thought this was correct - it should delete every job from the job table for which there does not exist a job assignment which has that job as it's Foreign Key. However, this fails with the following error when I try and execute it:
Cannot delete or update a parent row:
a foreign key constraint fails
(testdatabase.jobassignment,
CONSTRAINT FK_jobassignment_1
FOREIGN KEY (JobId) REFERENCES job
(Job_Id))
So what silly thing am I doing wrong?
EDIT: As usual, I found an answer only seconds after posting here. I used the (completely different) query:
DELETE FROM job WHERE Job_Id NOT IN (SELECT JobId FROM jobassignment)
Out of curiosity, is this the better way to do it? Was my original idea even feasible? And if so, what was wrong with it?
DELETE FROM job USING job
LEFT JOIN jobAssignment ON(job.Job_Id = jobAssignment.JobId)
WHERE jobAssignment.JobId IS NULL;
You'll probably need a subquery, not sure if this will work in mySQL, but something similar at least:
DELETE FROM job
WHERE job.Job_Id NOT IN (
SELECT JobId FROM jobAssignment
)
Naktibalda suggests the subquery may be inefficient; if so you could try
DELETE FROM job
WHERE NOT EXISTS (SELECT *
FROM jobassignment
WHERE job.Job_Id = jobassignment.Job_Id);
I've had bad experiences with IN and NOT IN in the past; less trouble with NOT EXISTS.

database design: User will submit a howto, each howto will have one or more steps associated with, each step can have random pictures associated with

I am trying to design a database but I need some help with the relationships. Am i getting the table design right?
Here is the database idea..
User will submit a howto, each howto will have one or more steps associated with(a one to many). each step can have random pictures associated with(another one to many). so I am thinking of this:
CREATE TABLE `HowtoStepImage`
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) unsigned NOT NULL,
`howto_id` varchar(25) NOT NULL,
`step_id` varchar(25) NOT NULL,
`img_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `hsi_k_1` (`howto_id`),
CONSTRAINT `hsi_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
CONSTRAINT `hsi_ibfk_2` FOREIGN KEY (`step_id`) REFERENCES `HowtoStep` (`step_id`),
CONSTRAINT `hsi_ibfk_3` FOREIGN KEY (`img_id`) REFERENCES `StepImage` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
table HowtoStep
step_id, title, content, created
primary key (step_id)
table StepImage
img_id, filename, created
CREATE TABLE `UserHowtoComment` (
`id` int(10) unsigned NOT NULL auto_increment,
`howto_id` varchar(25) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`comment` varchar(500) NOT NULL,
`created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `UserHowtoComment_ibfk_1` (`howto_id`),
KEY `UserHowtoComment_ibfk_2` (`user_id`),
CONSTRAINT `UserHowtoComment_ibfk_1` FOREIGN KEY (`howto_id`) REFERENCES `HowtoStepImage` (`howto_id`),
CONSTRAINT `UserHowtoComment_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
however, I am getting error when creating the table, I am sure it is due to my database design. here is what mysql>SHOW ENGINE INNODB STATUS; shows:
091217 9:59:59 Error in foreign key constraint of table UserhowtoComment:
FOREIGN KEY (`howto_id`) REFERENCES `howtoStepImage` (`howto_id`),
CONSTRAINT `UserHowtoComment_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8:
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
the howto_id is a key(index) in UserHowtoComment though. I am not sure if that is the exact problem here..
Make 3 tables: one for HowTo, one for HowToStep, one for HowToStepImage.
Give each table a clearly defined key, e.g. a number or a string.
Then let the 'child' table refer to the key of the parent table.
Make sure that the columns have clear names as well.
TABLE HowTo
COLUMNS HowToId(key)
TABLE HowToStep
COLUMNS HowToStepId(key), HowToId
TABLE HowToStepImage
COLUMNS HowToStepImageId(key), HowToStepId
your query is really messy e.g. step_id varchar(25) needs to be an int.
why dont you just use a gui programm or maybe the good old phpMyAdmin, so you can learn the from the Querys they are creating, phpMyAdmin also has a advanced feature call "Designer" to create constraints.
If I read this correctly, your HowToComment id is a foreign key to HowtoStepImage. Does every comment have to have an image? Seems like a chicken and the egg issue. It seems, from your problem description, that an image links to a comment, not the other way around.
you're falling prey to the misleading terminology in MySQL. in the relational model, key is (necessarily) distinct. in the MySQL-speak, it's just an index. you need either PRIMARY KEY or UNIQUE KEY.
edit to add explicitly what is implied above: foreign keys must point to a key in the relational sense.