报表数据保存
如果某客户需要实现报表数据的保存功能,应如何实现?
写进数据库里呗,难道你还自定义txt文件吗
保存是为了查询
你写进别的什么文件里,怎么查呢
你要是光想着怎么存,不想怎么读取,那是存了个寂寞
1.重复消费
消息重复消费是使用消息队列之后,必须考虑的一个问题,也是比较严重和常见的问题
就比如有这样的一个场景,用户下单成功后我需要去一个活动页面给他加GMV(销售总额),最后根据他的GMV去给他发奖励,这是电商活动很常见的玩法。
这样的活动页面肯定是异步去加的,不然你想,你一个用户下一单就给他加一下,那就意味着对那张表就要操作一下,你考虑下双十一当天多少次对这个表的操作?这数据库或者缓存都顶不住吧。
而且大家应该也有这样的体会,你下单了马上去看一些活动页面,有时候马上就有了,有时候缺延迟有很久,为啥?这个速度取决于消息队列的消费速度,消费慢堵塞了就迟点看到呗。
你下个单支付成功你就发个消息出去,我们上面那个活动的开发人员就监听你的支付成功消息,我监听到你这个订单成功支付的消息,那我就去我活动GMV表里给你加上去,听到这里大家可能觉得顺理成章。
但是我告诉大家一般消息队列的使用,我们都是有重试机制的,就是说我下游的业务发生异常了,我会抛出异常并且要求你重新发一次。
我这个活动这里发生错误,你要求重发肯定没问题。但是大家仔细想一下问题在哪里?
是的,不止你一个人监听这个消息啊,还有别的服务也在监听,他们也会失败啊,他一失败他也要求重发,但是你这里其实是成功的,重发了,你的钱不就加了两次了?
真实的情况其实重试是很正常的,服务的网络抖动,开发人员代码Bug,还有数据问题等都可能处理失败要求重发的。
其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性。
2.幂等性
幂等(
idempotent
、idempotence
)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
例如,setTrue()
函数就是一个幂等函数,无论多次执行,其结果都是一样的。更复杂的操作幂等保证是利用唯一交易号(流水号)实现。
通俗了讲就是你同样的参数调用我这个接口,调用多少次结果都是一个,你加GMV同一个订单号你加一次是多少钱,你加N次都还是多少钱。
例如这条 SQLupdate t1 set money = 150 where id = 1 and money = 100;
执行多少遍money都是150,这就叫幂等。
3.如何保证
一般幂等,我会分场景去考虑,看是强校验还是弱校验,比如跟金钱相关的场景那就很关键呀,就做强校验,别不是很重要的场景做弱校验。
强校验:
比如你监听到用户支付成功的消息,你监听到了去加GMV是不是要调用加钱的接口,那加钱接口下面再调用一个加流水的接口,两个放在一个事务,成功一起成功失败一起失败。
每次消息过来都要拿着订单号+业务场景这样的唯一标识(比是天猫双十一活动)去流水表查,看看有没有这条流水,有就直接return不要走下面的流程了,没有就执行后面的逻辑。
之所以用流水表,是因为涉及到金钱这样的活动,有啥问题后面也可以去流水表对账,还有就是帮助开发人员定位问题。
public void checkmess(String objid){
try{
//查询订单是否存在加GMV的流水
Object bill = getBill('addBill' + objid);
if(Objects.isNull(bill)){
//不存在流水,加GMV、加流水,写在同一个事务中
addBill(objid);
}else{
// 存在流水,直接返回
return;
}
}catch(Exception e){
// 发送异常 触发消息队列框架重试机制
}
}
弱校验:
这个简单,一些不重要的场景,比如给谁发短信啥的,我就把这个id+场景唯一标识作为 Redis
的key,放到缓存里面失效时间看你场景,一定时间内的这个消息就去Redis判断。
用KV就算消息丢了可能这样的场景也没关系,反正丢条无关痛痒的通知短信嘛(你敢说你没验证码短信丢失的情况?)。
结合业务,大概总结一下:
问题回答:
在应用程序中实现报表数据保存功能,可以考虑将报表数据存储在数据库中。具体步骤如下: 1.设计数据库表结构,包括报表数据表、用户表等相关表。需要注意的是,报表数据表需要与其他表建立外键关系,以确保数据的完整性和一致性。 示例SQL代码:
CREATE TABLE report_data ( id INT PRIMARY KEY AUTO_INCREMENT, -- id主键自增 user_id INT NOT NULL, -- 报表数据所属用户id report_name VARCHAR(50) NOT NULL, -- 报表名称 report_content TEXT -- 报表内容 );
CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, -- id主键自增 username VARCHAR(50) NOT NULL -- 用户名 );
2.编写代码将数据写入数据库中。通过使用SQL语句,向report_data表中插入新的数据记录。 示例Python代码:
import mysql.connector
conn = mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword", database="yourdatabase" )
cursor = conn.cursor()
user_id = 1 # 假设用户id为1 report_name = "Test Report" report_content = "This is a test report."
sql = "INSERT INTO report_data (user_id, report_name, report_content) VALUES (%s, %s, %s)" val = (user_id, report_name, report_content)
cursor.execute(sql, val) conn.commit()
print(cursor.rowcount, "record inserted.")
3.编写代码从数据库中检索数据。通过使用SQL语句,从report_data表中检索数据记录。 示例Python代码:
import mysql.connector
conn = mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword", database="yourdatabase" )
cursor = conn.cursor()
user_id = 1
sql = "SELECT * FROM report_data WHERE user_id = %s" val = (user_id,)
cursor.execute(sql, val)
result = cursor.fetchall()
for row in result: print(row)
4.数据的安全性和保密性需要采取相应的措施,如使用加密算法对数据进行加密处理,在传输和存储过程中做好权限控制等。此外,还需要定期备份和恢复数据,以确保数据的安全和完整性。
参考资料: