Rust Diesel插入冲突时执行Redis自定义存储逻辑可行吗?
解决方案:插入冲突时将文章存入Redis
可以实现这个需求,下面提供两种实用的方案,适配你当前的Diesel 2.0.4版本:
方案1:在Rust代码中判断插入结果,触发Redis存储
这是最直接且易维护的方式,利用Diesel插入操作的返回值判断是否发生冲突,再执行Redis写入逻辑。
实现步骤:
- 执行插入语句,通过
execute返回的受影响行数,或returning的结果是否为空,判断是否冲突。 - 若判定为冲突(插入未生效),将文章序列化为JSON后存入Redis。
代码示例:
use diesel::prelude::*; use redis::Commands; use serde::Serialize; // 假设你的文章结构体实现了Serialize trait #[derive(Insertable, Serialize)] #[diesel(table_name = articles)] struct NewArticle { id: i32, title: String, content: String, // 其他字段... } fn insert_article_or_save_to_redis( article: NewArticle, db_conn: &mut PgConnection, redis_conn: &mut redis::Connection, ) -> Result<(), Box<dyn std::error::Error>> { // 执行插入,返回受影响的行数 let inserted_rows = diesel::insert_into(articles::table) .values(&article) .on_conflict_do_nothing() .execute(db_conn)?; if inserted_rows == 0 { // 无行受影响,说明发生冲突,存入Redis let article_json = serde_json::to_string(&article)?; redis_conn.set(format!("article:{}", article.id), article_json)?; } Ok(()) }
注意事项:
- 高并发场景下可能存在竞态条件:比如线程A执行插入前数据不存在,线程B先完成插入,线程A执行插入时返回冲突,但此时数据已存在。若业务对强一致性要求高,可结合数据库事务或行级锁,但会牺牲部分性能。
方案2:利用PostgreSQL触发器处理冲突(数据库层面)
通过PostgreSQL的触发器机制,在插入冲突时触发自定义逻辑将数据写入Redis。这种方式无需修改太多Rust代码,但依赖数据库扩展,维护成本较高。
实现思路:
- 安装PostgreSQL的Redis扩展(如
pg_redis或redis_fdw)。 - 创建PL/pgSQL函数,将冲突数据写入Redis。
- 在
INSERT ... ON CONFLICT语句中通过间接方式触发该函数(Diesel 2.0.4暂不支持ON CONFLICT DO CALL,可通过DO UPDATE SET配合触发器实现)。
示例SQL(需适配你的表结构):
-- 假设已安装pg_redis扩展 CREATE OR REPLACE FUNCTION save_article_to_redis() RETURNS TRIGGER AS $$ BEGIN -- 将冲突的文章数据序列化为JSON存入Redis PERFORM redis_set(CONCAT('article:', NEW.id), row_to_json(NEW)); RETURN NULL; END; $$ LANGUAGE plpgsql; -- 创建触发器,当插入冲突时触发 CREATE TRIGGER trigger_article_conflict AFTER INSERT ON articles FOR EACH ROW WHEN (NOT FOUND) -- 仅在插入未成功时触发 EXECUTE FUNCTION save_article_to_redis();
优缺点:
- 优点:Rust代码无需额外逻辑,冲突处理逻辑集中在数据库。
- 缺点:依赖特定数据库扩展,调试和迁移难度大,灵活性不如Rust代码层面处理。
推荐方案
优先选择方案1,逻辑清晰、易调试,且能更好地与Rust业务代码集成。若业务对数据库层面的冲突处理有特殊需求,再考虑方案2。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

