这让人绝望的大事务提交-麻了 (这让人绝望的英语)

教程大全 2025-07-15 18:28:50 浏览

一、背景

继上次的if else优化也有段时间了,最近小猫又又又着道了,接手的那个项目又遇到了坑爹的地方,经常性的报死锁异常,经常性的主从延迟……通过报错信息按图索骥,发现代码是这样的。

这是一段商品发布的逻辑,我们可以看到参数校验、查询、最终的insert以及update全部揉在一个事务中。遇到批量发布商品的时候就经常出现问题了,数据库主从延迟是肯定少不了的。

二、开启优化

其实像上述小猫遇到的这种状况我们就称其为大事务,那么我们就大概有这么一个定义。我们将执行时间长,并且操作数据比较多的事务叫做大事务。

1.大事务产生的原因

在我们日常开发过程中,其实经常会遇到大事务,老猫总结了一下,往往原因其实总结下来有这么几点(当然存在纰漏的地方,也欢迎大家评论区留言补充):

2.造成的影响

那么大事务造成的影响又是什么呢?

从开发者的角度来看的话,部分大事务必定对应的复杂的业务逻辑,代码封装事务拆解不合理,研发侧维护困难,维护成本高。

从最终系统以及运维角度来看:

3.优化方案

大事务既然有这么多坑,那么我们来看一下我们日常开发过程中,应该如何做到尽量规避呢?老猫整理了以下几种优化方法。

a.降低事务颗粒度,大事务拆解小事务:

b.大数据量一次性提交尽可能拆解分批处理。

c.拆解原始事务,异步化处理。

(1) 降低事务颗粒度

1、我们对@Transactional的事务粒度把控不好,有时候如果使用不当的话事务功能可能会失效,如果经验不足,很难排查,那么我们不如直接使用粗细粒度更好把控的编程式事务。TransactionTemplate。这样的话咱们的优化代码就可以写好才能如下方式。

@Autowiredprivate TransactionTemplate transactionTemplate;public boolean publishProduct(PublishProductRequest request) {externalSellerAuthorizeService.checkAuthorizeValid(request.getSellerId(),request.getThirdCategoryId(),request.getBrandId());......transactionTemplate.execute((status) -> {try{//执行insertproductDao.insert(productDO);productDescDao.insert(productDescDO);....//其他insert以及update操作}catch (Exception e) {//回滚status.setRollbackOnly();return true;}return false;});return true;}

非update以及insert动作外移。

原始代码:

@Transactional(rollbackFor=Exception.class)public void save(Req req) {checkParam(req);saveData1(req);updateData2(req);}private void checkParam(Req req){Data1>(2) 大数据量一次性提交尽可能拆解分批处理

我们再来看大数量批量请求的场景,咱们具体来分析一下,假设上游系统存在一个批量导入2w的数据操作。如果我们读取到上游导入的数据,并且直接执行DB一次性执行肯定是不合适的。这种情况就需要我们对其请求的数据量做一个拆解。我们可以采用lists.partition等等方式将数据拆成多个小的批量然后再进行入库操作处理。

@Servciepublic class ServiceA {@Autowiredprivate ServiceB serviceB;private void batchAdd(List inventorySkuIdList){List> partition = Lists.partition(inventorySkuIdList, 1000);for (List idList : partition) {List inventorySkuDOList = inventorySkuDao.selectByIdList(idList, null);if (CollectionUtils.isNotEmpty(inventorySkuDOList)) {serviceB.doInsertUpdate(inventorySkuDOList);}}}}@Servciepublic class ServiceB {@Transactional(rollbackFor=Exception.class)private void doInsertUpdate(List inventorySkuDOList){for (InventorySkuDO inventorySkuDO : inventorySkuDOList) {doInsert(inventorySkuDO);doUpdate(inventorySkuDO)}}}

(3) 拆解原始事务,异步化处理

这种异步化处理的方案其实有两种方式进行异步化操作。尤其是涉及到第三方RPC调用或者HTTP调用的时候,这种方案就更加适合。

方案一,采用CompletableFuture异步编排特性,当业务流程比较长的时候,我们可以将一个大业务拆解成多个小的任务进行异步化执行。比如咱们有个批量支付的业务逻辑,因为整个流程是同步的,所以大概有了下面这样的流程。(关于CompletableFeature老猫觉得挺有意思的,后续老猫会出专门的文章来理透该特性,欢迎大家持续关注)。

completeFeature

对应转换成代码逻辑的话,大概是这样的:

void doBatchPay() {CompletableFuture task1 = CompletableFuture.supplyAsync(() -> {return "订单信息";});CompletableFuture task2 = CompletableFuture.supplyAsync(() -> {try {return doPay();} catch (InterruptedException e) {//log add}});//task1、task2 执行完执行task3 ,需要感知task1和task2的执行结果CompletableFuture future = task1.thenCombineAsync(task2, (t1, t2) -> {return "邮件发送成功";});}

方案二,Mq异步化处理,还是针对上述业务逻辑,我们是否可以将最终的发送邮件的动作剥离出来,最终再去统一执行发送邮件。

关于伪代码这里不展开了,有兴趣的小伙伴可以自行实现一下。

这让人绝望的英语

成立子公司需要什么条件??

子公司是与母公司相对应的法律概念。 母公司是指拥有另一公司一定比例以上的股份或通过协议方式能够对另一公司实行实际控制的公司。 子公司是指一定比例以上的股份被另一公司所拥有或通过协议方式受到另一公司实际控制的公司。 子公司具有法人资格,可以独立承担民事责任,这是子公司与分公司的重要区别。 (1)子公司受母公司的实际控制。 所谓实际控制是指母公司对子公司的一切重大事项拥有实际上的决定权,其中尤为重要的是能够决定子公司董事会的组成。 在未经他人同意的情况下,母公司自己就可以通过行使权力而任命董事会的多名董事。 某些信托机构虽然拥有公司的大量股份,但并不参与对公司事务的实际控制,因而不属于母公司。 (2)母公司与子公司之间的控制关系是基于股权的占有或控制协议。 根据股东会多数表决原则,拥有股份越多,越能够取得对公司事务的决定权。 因此,一个公司如果拥有了另一公司50%以上的股份,就必然能够对该公司实行控制。 但实际上由于股份的分散,只要拥有一定比例以上的股份,就能够获股东会表决权的多数,即可取得控制的地位。 除股份控制方式之外,通过订立某些特殊契约或协议而使某一公司处于另一公司的支配之下,也可以形成母公司、子公司的关系。 (3)母公司、子公司各为独立的法人。 虽然子公司处于受母公司实际控制的地位,许多方面都要受到母公司的管理,有的甚至类似母公司的分支机构,但法律上,子公司仍是具有法人地位的独立公司企业,它有自己的公司名称和公司章程,并以自己的名义进行经营活动,其财产与母公司的财产彼此独立,各有自己的资产负债表。 在财产责任上,子公司和母公司也各以自己所有财产为限承担各自的财产责任,互不连带。 通过持有其他公司一定比例以上的股份而对其实行控制的公司,又称控股公司。 母公司与控股公司是可以通用的两个概念,子公司也可以通过控制其他公司一定比例以上的股份而成为控股公司,被控股的公司成为孙公司。 母公司通过控制众多的子公司、孙公司而成为庞大的公司集团。 母公司只要通过较少的资本就可以利用子公司的资本购买别的公司,组建起金字塔型的公司集团模式设立分公司需提交以下材料:1、你的分公司的法定代表人签署的分支机构设立登记申请书;2、你的分公司的企业章程复印件;3、你的分公司的企业法人营业执照复印件;4、你的分公司的股东会要求设立分支机构的决议;5、办事处的住所证明文件,如果是租用的,提供房租合同,及出租方的房产证复印件;6、你的分公司负责人的任职证明文件;7、经营范围涉及前置审批的还需提交相应的许可证明。 以上复印件都需盖章确认。

天王表gs3618s价格

2500元到4000元之间

如何在python中进行数据库的添加

你可以访问Python数据库接口及API查看详细的支持数据库列表。 不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。 DB-API 是一个规范. 它定义了一系列必须的对象和数据库存取方式, 以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口 。 Python的DB-API,为大多数的数据库实现了接口,使用它连接各数据库后,就可以用相同的方式操作各数据库。 Python DB-API使用流程:引入 API 模块。 获取与数据库的连接。 执行SQL语句和存储过程。 关闭数据库连接。 什么是MySQLdb?MySQLdb 是用于Python链接Mysql数据库的接口,它实现了 Python 数据库 API 规范 V2.0,基于 MySQL C API 上建立的。 如何安装MySQLdb?为了用DB-API编写MySQL脚本,必须确保已经安装了MySQL。 复制以下代码,并执行:#!/usr/bin/python# -*- coding: UTF-8 -*-import MySQLdb如果执行后的输出结果如下所示,意味着你没有安装 MySQLdb 模块:Traceback (most recent call last):File , line 3, in import MySQLdb ImportError: No module named MySQLdb 安装MySQLdb,请访问 ,(Linux平台可以访问:)从这里可选择适合您的平台的安装包,分为预编译的二进制文件和源代码安装包。 如果您选择二进制文件发行版本的话,安装过程基本安装提示即可完成。 如果从源代码进行安装的话,则需要切换到MySQLdb发行版本的顶级目录,并键入下列命令: $ gunzip $ tar -xvf $ cd MySQL-python-1.2.2 $ python build $ python install 注意:请确保您有root权限来安装上述模块。 数据库连接 连接数据库前,请先确认以下事项: 您已经创建了数据库 TESTDB. 在TESTDB数据库中您已经创建了表 EMPLOYEE EMPLOYEE表字段为 FIRST_NAME, LAST_NAME, AGE, SEX 和 INCOME。 连接数据库TESTDB使用的用户名为 testuser ,密码为 test123,你可以可以自己设定或者直接使用root用户名及其密码,Mysql数据库用户授权请使用Grant命令。 在你的机子上已经安装了 Python MySQLdb 模块。 如果您对sql语句不熟悉,可以访问我们的 SQL基础教程 实例: 以下实例链接Mysql的TESTDB数据库: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # 使用execute方法执行SQL语句 (SELECT VERSION()) # 使用 fetchone() 方法获取一条数据库。 data = () print Database version : %s % data # 关闭数据库连接 () 执行以上脚本输出结果如下: Database version : 5.0.45 创建数据库表 如果数据库连接存在我们可以使用execute()方法来为数据库创建表,如下所示创建表EMPLOYEE: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # 如果数据表已经存在使用 execute() 方法删除表。 (DROP TABLE IF EXISTS EMPLOYEE) # 创建数据表SQL语句 sql = CREATE TABLE EMPLOYEE ( FIRST_NAME CHAR(20) NOT NULL, LAST_NAME CHAR(20), AGE INT, SEX CHAR(1), INCOME FLOAT ) (sql) # 关闭数据库连接 () 数据库插入操作 以下实例使用执行 SQL INSERT 语句向表 EMPLOYEE 插入记录: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # SQL 插入语句 sql = INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES (Mac, Mohan, 20, M, 2000) try: # 执行sql语句 (sql) # 提交到数据库执行 () except: # Rollback in case there is any error () # 关闭数据库连接 () 以上例子也可以写成如下形式: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # SQL 插入语句 sql = INSERT INTO EMPLOYEE(FIRST_NAME, \ LAST_NAME, AGE, SEX, INCOME) \ VALUES (%s, %s, %d, %c, %d ) % \ (Mac, Mohan, 20, M, 2000) try: # 执行sql语句 (sql) # 提交到数据库执行 () except: # 发生错误时回滚 () # 关闭数据库连接 () 实例: 以下代码使用变量向SQL语句中传递参数: .................................. user_id = test123 password = password (insert into Login values(%s, %s) % \ (user_id, password)) .................................. 数据库查询操作 Python查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据。 fetchone(): 该方法获取下一个查询结果集。 结果集是一个对象 fetchall():接收全部的返回结果行. rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。 实例: 查询EMPLOYEE表中salary(工资)字段大于1000的所有数据: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # SQL 查询语句 sql = SELECT * FROM EMPLOYEE \ WHERE INCOME > %d % (1000) try: # 执行SQL语句 (sql) # 获取所有记录列表 results = () for row in results: fname = row[0] lname = row[1] age = row[2] sex = row[3] income = row[4] # 打印结果 print fname=%s,lname=%s,age=%d,sex=%s,income=%d % \ (fname, lname, age, sex, income ) except: print Error: unable to fecth data # 关闭数据库连接 () 以上脚本执行结果如下: fname=Mac, lname=Mohan, age=20, sex=M, income=2000 数据库更新操作 更新操作用于更新数据表的的数据,以下实例将 TESTDB表中的 SEX 字段全部修改为 M,AGE 字段递增1: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # SQL 更新语句 sql = UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = %c % (M) try: # 执行SQL语句 (sql) # 提交到数据库执行 () except: # 发生错误时回滚 () # 关闭数据库连接 () 删除操作 删除操作用于删除数据表中的数据,以下实例演示了删除数据表 EMPLOYEE 中 AGE 大于 20 的所有数据: #!/usr/bin/python # -*- coding: UTF-8 -*- import MySQLdb # 打开数据库连接 db = (localhost,testuser,test123,TESTDB ) # 使用cursor()方法获取操作游标 cursor = () # SQL 删除语句 sql = DELETE FROM EMPLOYEE WHERE AGE > %d % (20) try: # 执行SQL语句 (sql) # 提交修改 () except: # 发生错误时回滚 () # 关闭连接 () 执行事务 事务机制可以确保数据一致性。 事务应该具有4个属性:原子性、一致性、隔离性、持久性。 这四个属性通常称为ACID特性。 原子性(ATOMICity)。 一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。 一致性(consistency)。 事务必须是使数据库从一个一致性状态变到另一个一致性状态。 一致性与原子性是密切相关的。 隔离性(isolation)。 一个事务的执行不能被其他事务干扰。 即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 持久性(durability)。 持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。 接下来的其他操作或故障不应该对其有任何影响。 Python DB API 2.0 的事务提供了两个方法 commit 或 rollback。

本文版权声明本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请联系本站客服,一经查实,本站将立刻删除。

发表评论

热门推荐