Прямо в точку
Недавно было много дождей, и случайно я придумал другое решение проблемы предотвращения повторной вставки (Вариант 3).
сцена
В проекте электронной коммерции продукт может быть привязан к нескольким этикеткам, а этикетка может быть привязана к нескольким продуктам, поэтому должна быть промежуточная таблица для связывания продукта и этикетки, предполагая, что таблица ассоциацииgoods_label
Структура выглядит следующим образом:
поле | тип | Примечания |
---|---|---|
id | bigint | первичный ключ |
goods_id | bigint | идантификационный номер продукта |
label_id | bigint | идентификатор тега |
is_delete | tinyint | Идентификатор надгробия, 1 удаляется, 2 не удаляется |
A и B одновременно выполняют операции связывания этикеток на продуктах:
A -> 绑定商品1和标签1
B -> 绑定商品1和标签1
Тогда наше ожидание состоит в том, что только одна из операций А и В увенчается успехом, а другой напомнит ему, что операция не удалась.
Далее будет несколько вариантов достижения желаемых ожиданий~
Вариант 1: распределенная блокировка
Поддельный код:
var goods_id = 1
var label_id = 1
var id = getId()
var count = select count(0) from goods_label where goods_id = $goods_id and label_id = $label_id and is_delete = 2
if count > 0{
return "已存在关联"
}
var l = lock.try(GOODS_LABEL_LOCK_KEY + goods_id)
if l != nil{
try{
var row = insert into goods_label(id, goods_id, label_id, is_delete) values ($id, $goods_id, $label_id, 2)
if row > 0{
return "成功"
}
return "失败"
} finally{
l.release()
}
}else{
return "操作超时"
}
Это очень часто и очень часто... без следа души
Вариант 2: Совместный уникальный индекс
будетgoods_id
иlabel_id
а такжеis_delete
Задайте как совместный уникальный индекс, тогда псевдокод можно записать следующим образом:
var goods_id = 1
var label_id = 1
var id = getId()
var count = select count(0) from goods_label where goods_id = $goods_id and label_id = $label_id and is_delete = 2
if count > 0{
return "已存在关联"
}
var row = insert into goods_label(id, goods_id, label_id, is_delete) values ($id, $goods_id, $label_id, 2)
if row > 0{
return "成功"
}
return "失败"
Код намного проще, но структура индекса таблицы будет сложной.tps
отклонить,is_delete
Поле также ограничено только 1 и 2 результатами в одной и той же записи.
Вариант 3: Предварительно удалить (самоназвание)
var goods_id = 1
var label_id = 1
var id = getId()
var count = select count(0) from goods_label where goods_id = $goods_id and label_id = $label_id and is_delete = 2
if count > 0{
return "已存在关联"
}
var trans = beginTrans()
// 1
var row = insert into goods_label(id, goods_id, label_id, is_delete) values ($id, $goods_id, $label_id, 1)
if row > 0{
// 2
row = update goods_label set is_delete = 2 where id = $id and (select t.count from (select count(0) as count from goods_label where goods_id = $goods_id and label_id = $label_id and is_delete = 2) t) = 0
if row > 0{
trans.commit()
return "成功"
}
trans.rollback()
return "失败"
}
return "失败"
Эта схема будет выполнять две операции транзакции sql:
- тег 1:Сначала вставьте данные, сначала установите состояние на удаление.
- тег 2:Затем обновите состояние ранее вставленных данных до восстановленных, ноПри условии, что текущая ассоциация не существует.
Можно обнаружить, что в случае сбоя выполнения тега 2 вся транзакция будет отброшена, а операция тега 1 также будет отменена, поэтому грязные данные генерироваться не будут.
Сравнение схем
- Вариант первый:Традиционный, но функциональный, без души.
- Вариант 2:Ограничение слишком велико. Если уникальных условий слишком много, индекс будет сложнее. Кроме того, в некоторых сценариях is_delete один и тот же ресурс может удаляться несколько раз, поэтому эффект уникального индекса не может быть достигнут. Преимущество в том, что бизнес-код очень лаконичен!
- третье решение:Я иногда капризничаю, не знаю хорошо это или плохо, но для достижения цели xD