Re: UPSERT with non-unique index
The Natural Philosopher <[email protected]> Mon, 3 Aug 2020 10:05:05 +0100
| Newsgroups | comp.databases.mysql |
|---|---|
| Organization | A little, after lunch |
| Message-ID | <[email protected]> |
On 02/08/2020 22:46, Lew Pitcher wrote: > On August 2, 2020 17:28, The Natural Philosopher wrote: > >> On 02/08/2020 19:15, J.O. Aho wrote: >>> On 02/08/2020 18.55, Stanimir Stamenkov wrote: >>>> [I've posted this on Stack Overflow: >>>> https://stackoverflow.com/questions/63194116/upsert-with-non-unique-index] >>>> >>>> >>>> I need to implement concurrent-safe UPSERT using a non-unique key and >>>> avoid unnecessary auto-increment of ID. >>>> >>>> Traditional INSERT ... ON DUPLICATE KEY [1] doesn't work for me, so >>>> I'm performing: >>>> >>>> INSERT INTO table (col1, col2, col3, col4, col5) >>>> SELECT 1, 2, 'value3', 'value4', 'value5' >>>> WHERE NOT EXISTS (SELECT 1 >>>> FROM table >>>> WHERE col3 = 'value3' >>>> AND col4 = 'value4' >>>> AND col5 = 'value5') >>>> >>>> then if it results in no row inserted, I'm performing: >>>> >>>> UPDATE table >>>> SET col1 = col1 + 1, >>>> col2 = MAX(col2, 2) >>>> WHERE col3 = 'value3' >>>> AND col4 = 'value4' >>>> AND col5 = 'value5' >>>> >>>> There's an index: >>>> >>>> CREATE INDEX ON table (col3, col4, col5) >>>> >>>> It is non-unique as there are legacy data that does not allow me to >>>> declare it unique. Newer records, however, should not have duplicated >>>> (col3, col4, col5) rows. >>>> >>>> Unsurprisingly, using the given INSERT statement I'm getting mixed >>>> results trying to execute it concurrently from two sessions. I can >>>> see the second session blocking until the first one commits its >>>> transaction, but then the second transaction is also able to insert a >>>> new row sometimes (or sometimes it achieves the expected of avoiding >>>> to insert a duplicate (col3, col4, col5) row). >>> >>> Maybe the blocking is more then way you are testing things than what in >>> reality happens. >>> >>> I would suggest you use >>> >>> LOCK TABLES table WRITE; >>> >>> <do what you did before> >>> >>> UNLOCK TABLES; >>> >>> >>> LOCK TABLES table WRITE; >>> >>> UNLOCK TABLES; >>> >>> https://www.mysqltutorial.org/mysql-table-locking/ >>> https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html >>> >> I don't know how SQL manages it, but if you have two asynchronous >> instances of read-modify-write, where what is written depends on what is >> read, then you MUST make the read-modify-write cycle ATOMIC - that is >> uninterruptible, by another process doing the same. >> >> You need locking, and the attendant danger that if aprocess crashes with >> locks set... > > I'd roll the whole thing in a transaction > > START TRANSACTION; > > -- your sql goes here > > COMMIT; > > I don't use SQL often enough to remember that it has transactions these days :-( Note that you appear to need to be using innoDB or another advanced engine and not MyISAM if you want to use transactions -- "In our post-modern world, climate science is not powerful because it is true: it is true because it is powerful." Lucas Bergkamp