“数据库事务(Database Transaction)

2023-10-27

《事务的使用》
  
  关于事务,我今天要把自己放在一个初学者的心态来写这篇文章。之前几篇文章大多讲的是对于Winner的应用,今天要从根本上来讲
  
  一下“事务”,以及事务在Winner中的应用。
  
  首先从基础讲起,什么是“事务”?事务能帮我们解决哪些问题? 摘录百度上的一段话教科书式的文字:
  
  “数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。”
  
  其实很好理解,比如说我们的商品购物流程中支付成功之后要做的几步操作:
  
  1,修改订单表该状态;
  
  2,修改库存表库存数量;
  
  3,添加物流表发货信息;
  
  三个操作必须一气呵成,这时候就需要串联事务,当一个操作失败之后,事务就回滚整个业务失败。当操作成功之后,所有操作才最终持久化执行。
  
  假设我们没有事务的话,会怎么样?
  
  还是上面三个流程,没有联事务就有可能出现以下情况:
  
  步骤一: 修改订单表该状态 (完成)
  
  步骤二:修改库存表库存数量 (完成)
  
  步骤三:添加物流发货信息(失败)
  
  当步骤三失败,由于没有事务回滚,程序中就必须得通过程序判断步骤三得到失败后,再操作“步骤二”中库存订单数量回到未修改前的值。
  
  同事还要还原“步骤一”中订单表的订单状态。
  
  而且,如果数据库健壮性不够,有可能导致二次修改步骤一,步骤二失败,造出数据库一片混乱。
  
  这就是为什么我们要使用事务,事务有四大特性(百度摘录):
  
  原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
  
  一致性:事务在完成时,必须使所有的数据都保持一致状态。
  
  隔离性:由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。
  
  持久性:事务完成之后,它对于系统的影响是永久性的。
  
  这些其实我也早忘了,毕竟工作多年以后也不会有这样的考试,读书那会还是记得挺清楚的,只是那会不能感受到
  
  事务的重要性,那时候的老师也不管那么多就照本宣科的讲,那些是以后工作生涯的重点,有时候老师自己都不知道
  
  造成了我们可能花很多时间去理解“游标”,“函数” 这些压根用不着几次的东西。
  
  开始事物:begin transaction
  
  提交事物:commit  transaction
  
  回滚事物:rollback transaction
  
  复制代码
  
  begin transaction
  
  declare @errorSum int      --定义局部变量
  
  set @errorSum=0  --初始化临时变量
  
  update bank set currentMoneycurrentMoney= currentMoney-1000 where customerName='张三'
  
  set @errorSum=@errorSum+@@error    --累计是否有错误
  
  update bank set currentMoneycurrentMoney= currentMoney+1000 where customerName='李四'
  
  set @errorSum=@errorSum+@@error    --累计是否有错误
  
  if @errorSum<>0     --如果有错误
  
  begin
  
  rollback transaction
  
  end
  
  else
  
  begin
  
  commit  transaction
  
  end
  
  go
  
  复制代码
  
  这里我就偷个懒,不自己去写个事务的案例,直接从网络上摘录,出处与:http://database.51cto.com/art/201108/283348.htm。
  
  每个数据库语法略又有不同,大整体差不到哪去我这里就不详细解释每个关键字上面意思了,自行百度吧! 再说这还是比较基础的知识,我就一笔带过。
  
  最后在事务基础知识再补充一点,使用事务时一定要谨慎,事务必须 “一开一关”,开启了一个事务必须要关闭这个事务,无论是提交(commit ) 还是 回滚(roolback)。
  
  必须要有关闭操作,如果没有关闭事务,则会造成事务挂起。数据库就会被锁,一旦数据数据被锁,轻则导致该表不能操作,重则导致整个数据库不能操作,致使整个
  
  程序奔溃不能运行。 这里一定要谨慎,在我工作了8年后我任然很多次看到我们系统会出现锁表的情况,都是有个别程序员对事务应用不当,导致事务挂起,数据库死锁。
  
  Winner一直使用的是Oracle数据库,这里贴一个我们常用的Sql工具:"锁表侦探"
  
  复制代码
  
  SELECT ROOT, L.SID_BLOCKED, L.TYPE, L.LMODE, SINFO.*
  
  FROM (SELECT ROWNUM ORDERNO, CONNECT_BY_ROOT SID_WAITING ROOT, T.*
  
  FROM (SELECT B.SID SID_BLOCKED, W.SID SID_WAITING, W.TYPE, W.LMODE
  
  FROM V$LOCK B, V$LOCK W
  
  WHERE B.ID1 = W.ID1
  
  AND B.ID2 = W.ID2
  
  AND B.BLOCK = 1
  
  AND W.REQUEST > 0
  
  UNION ALL
  
  SELECT NULL, SID, TYPE, LMODE
  
  FROM V$LOCK B
  
  WHERE B.BLOCK = 1
  
  AND SID NOT IN (SELECT DISTINCT W.SID SID_WAITING
  
  FROM V$LOCK B, V$LOCK W
  
  WHERE B.ID1 = W.ID1
  
  AND B.ID2 = W.ID2
  
  AND B.BLOCK = 1
  
  AND W.REQUEST > 0)) T
  
  START WITH SID_BLOCKED IS NULL
  
  CONNECT BY SID_BLOCKED = PRIOR SID_WAITING) L
  
  LEFT JOIN (SELECT S.SID,
  
  SERIAL#,
  
  O.OBJECT_NAMES,
  
  T.START_TIME,
  
  S.STATUS,
  
  ST.SQL_TEXT,
  
  S.MACHINE,
  
  S.PROGRAM,
  
  S.USERNAME,
  
  S.LOGON_TIME
  
  FROM V$SESSION S
  
  JOIN (SELECT SESSION_ID,
  
  SUBSTR(SYS_CONNECT_BY_PATH(OBJECT_NAME, ','), 2) OBJECT_NAMES
  
  FROM (SELECT ROW_NUMBER() OVER(PARTITION BY SESSION_ID ORDER BY OBJECT_NAME) AS RN,
  
  LO.*,
  
  O.*
  
  FROM V$LOCKED_OBJECT www.douniu178.com LO
  
  LEFT JOIN DBA_OBJECTS O
  
  ON LO.OBJECT_ID = O.OBJECT_ID) T
  
  WHERE CONNECT_BY_ISLEAF = 1
  
  START WITH RN = 1
  
  CONNECT BY SESSION_ID = PRIOR SESSION_ID
  
  AND RN =
  
  PRIOR RN + 1) O
  
  ON S.SID = O.SESSION_ID
  
  LEFT JOIN V$TRANSACTION T
  
  ON S.TADDR = T.ADDR
  
  LEFT JOIN V$SQLAREA ST
  
  ON ST.ADDRESS = S.SQL_ADDRESS) SINFO
  
  ON SINFO.SID = L.www.thylpt1.com  SID_WAITING
  
  ORDER BY ORDERNO;
  
  复制代码
  
  在后面的篇章中会讲到“报表系统”,我习惯把锁表侦探添加到报表系统中,每次遇到锁表情况的时候就上报表系统查看是哪个项目锁表。
  
  这里有人就会问了,锁表侦探能查出具体哪张表所了,那怎么监控数据库有没有锁表的迹象? 这里要推荐第二个工具:“Spotligth”
  
  翻译过来叫“聚光灯”, Spotligth有很多版本,有监控 服务器的,有监控数据库的(主流都支持)
  
  spotlight on Oracle
  
  spotlight on Mysql
  
  spotlight on Windows
  
  我上班的时候 是两台电脑,一台办公,另外一台则挂着Spotligth 实时监控着我们的数据库,一飘红里面上报表系统查“锁表侦探”
  
  然后通知到相应的技术员,当然有时候还免不了要对犯错误的技术员 “指点”几句。
  
  =======================华丽的分割线=======================
  
  基础知识就到这里了,下面就是Winner的干货了。 在整个Winner中,我觉得最牛逼的当属“事务”这一块,能想出这种方式并开发出来了的真的很厉害。
  
  最初我在上家公司任职时,我的老大(William )他跟我讲事务的时候我就觉得太屌了,而这个事务就是由他开发的。
  
  其他的不多说,贴一张图就知道Winner中的事务有多好用:
  
  真的超级好用,一来不用写一句Sql,二来业务流程清晰,尤其是当程序需要调试的时候,这种方式能让程序员清晰的看到业务逻辑的每一个流程。
  
  这里运用到一个“职责分离”的思想,我们设定数据库的职责就是:“持久化存储数据”  复杂的业务逻辑由程序去处理。
  
  我刚参加工作那会任职过的几家公司,就没有这种思想(可能也是因为去的都是单一的项目型公司)。 最常见的就是 一旦涉及业务流程处理的他们
  
  就习惯性的以“存储过程”去处理,这样就使得开发变得繁琐,一会要写C#代码,一会有要去写sql代码,最重要的是数据库的不同又造成程序员要熟悉
  
  各种数据库的sql语法来写存储过程、事务、函数等等。
  
  “职责分离”的思想跟设计模式六大原则中的“单一职责”有点类似,但是“单一职责”更多的是指在程序中一个类只负责一项职责。“职责分离” 相当于“单一职责”
  
  的抽象版,程序做程序的负责业务逻辑,数据库做数据库的数据存储。
  
  我曾经也见过,有的公司一开始用的sqlserver数据库,然后开发方式还是当时特牛气的 Html + Ajax + C# + 存储过程,后来因为业务关系更换到MySql,大量的存储
  
  过程写在了数据库里面,特别是有些关键字Mysql是没有或不支持的,致使他们痛苦不堪。
  
  ==============================华丽的分割线========================
  
  我们来看看Winner是如何实现的,首先Winner的业务类对象都基础了 FacadeBase 这个基类。 (关于Winner解决方案不清楚的可以《解决方案命名规范》)
  
  复制代码
  
  using System;
  
  using Winner.Framework.Core.DataAccess;
  
  using Winner.Framework.Core.Interface;
  
  using Winner.Framework.Utils;
  
  namespace Winner.Framework.Core.Facade
  
  {
  
  public class FacadeBase : IDisposable, IPromptInfo
  
  {
  
  public IChangePage ChangePage;
  
  public FacadeBase();
  
  public PromptInfo PromptInfo { get; }
  
  public Transaction Transaction { get; }
  
  public virtual void Dispose();
  
  public void ReferenceTransactionFrom(Transaction transaction);
  
  protected void Alert(ResultType restulType);
  
  protected void Alert(PromptInfo result);
  
  protected void Alert(string msg);
  
  protected void Alert(ResultType restulType, PromptInfo result);
  
  protected void Alert(ResultType restulType, string msg);
  
  protected void Alert(string msg, PromptInfo result);
  
  protected void Alert(ResultType restulType, string msg, PromptInfo result);
  
  protected void BeginTransaction(www.dfgj157.com/  );
  
  protected void Commit();
  
  protected void RealRollback();
  
  protected void Rollback();
  
  }
  
  }
  
  复制代码
  
  FacadeBase在Winner.Framework.Core 程序集中,关于 FacadeBase其他的方法后面的篇章中再详细讲,今天重点讲事务这一块。
  
  public Transaction Transaction { get; } 定义事务对象; 对象由
  
  public void ReferenceTransactionFrom(Transaction transaction); 串联事务;
  
  protected void BeginTransaction(); 开启事务;
  
  protected void Rollback(www.hengxinyl.cn);  回滚事务;
  
  protected void Commit(); 提交事务;
  
  protected void RealRollback(); 强制回滚事务;
  
  复制代码
  
  using System;
  
  using Winner.Framework.Core.DataAccess;
  
  using Winner.Framework.Core.Interface;
  
  using Winner.Framework.Utils;
  
  namespace Winner.Framework.www.senta77.com Core.Facade
  
  {
  
  /// <summary>
  
  /// 通用三层架构的业务处理层(BLL)基类
  
  /// </summary>
  
  public class FacadeBase : IDisposable, IPromptInfo
  
  {
  
  #region 事务
  
  /// <summary>
  
  /// 事物对象
  
  /// </summary>
  
  public Transaction Transaction { get; private set; }
  
  /// <summary>
  
  /// 开启事务
  
  /// </summary>
  
  protected void BeginTransaction()
  
  {
  
  if (this.Transaction == null)
  
  {
  
  this.Transaction = new Winner.Framework.Core.DataAccess.Transaction();
  
  }
  
  this.Transaction.BeginTransaction();
  
  }
  
  /// <summary>
  
  /// 提交事务
  
  /// </summary>
  
  protected void Commit()
  
  {
  
  this.Transaction.Commit();
  
  }
  
  /// <summary>
  
  /// 强制回滚事物
  
  /// </summary>
  
  protected void RealRollback()
  
  {
  
  this.Transaction.RealRollback();
  
  }
  
  /// <summary>
  
  /// 事物串联
  
  /// </summary>
  
  /// <param name="transaction">事物对象</param>
  
  public void ReferenceTransactionFrom(Transaction transaction)
  
  {
  
  this.Transaction = transaction;
  
  }
  
  /// <summary>
  
  /// 回滚事物
  
  /// </summary>
  
  protected void Rollback()
  
  {
  
  this.Transaction.Rollback(www.qinlinyu.cn/);
  
  }
  
  #endregion
  
  }
  
  }
  
  复制代码
  
  为了更清楚的单一讲清楚事务,FacadeBase我精剪掉了其他方法,只剩下事务有关的方法,会看到FacadeBase作为调用实现几个基本的操作
  
  整个Winner事务的核心在Winner.Framework.Core.DataAccess.Transaction  这个对象中。
  
  下面贴一些阿杰开发的Winner2.0 的事务对象,写的非常漂亮。
  
  复制代码
  
  using System;
  
  using System.Data;
  
  using System.Data.Common;
  
  using System.Diagnostics;
  
  using Winner.Framework.Core.CustomException;
  
  using Winner.Framework.Core.Delegate;
  
  using Winner.Framework.Utils;
  
  namespace Winner.Framework.Core.DataAccess
  
  {
  
  /// <summary>
  
  /// 数据库事务机制
  
  /// </summary>
  
  /// <remarks>
  
  /// <![CDATA[
  
  /// 四大特性:原子性、一致性、隔离性、持久性
  
  /// ]]>
  
  /// </remarks>
  
  [DebuggerDisplay("事务状态={Status},计数器={Counter}")]
  
  public class Transaction
  
  {
  
  #region Event
  
  /// <summary>
  
  /// 开启事务时触发
  
  /// </summary>
  
  public event BeginTransaction BeginTransactionEvent;
  
  /// <summary>
  
  /// 提交事务时触发
  
  /// </summary>
  
  public event CommitTransaction CommitEvent;
  
  /// <summary>
  
  /// 强制回滚时触发
  
  /// </summary>
  
  public event RealRollbackTransaction RealRollbackEvent;
  
  /// <summary>
  
  /// 回滚事务时触发
  
  /// </summary>
  
  public event RollbackTransaction RollbackEvent;
  
  #endregion
  
  #region Property
  
  /// <summary>
  
  /// 是否已开启事务
  
  /// </summary>
  
  public bool IsBegin { get; private set; }
  
  /// <summary>
  
  /// 是否提交
  
  /// </summary>
  
  public bool IsCommit { get; private set; }
  
  /// <summary>
  
  /// 是否强制回滚
  
  /// </summary>
  
  public bool IsRealRollback { get; private set; }
  
  /// <summary>
  
  /// 是否回滚
  
  /// </summary>
  
  public bool IsRollback { get; private set; }
  
  /// <summary>
  
  /// 事务计数器
  
  /// </summary>
  
  public int Counter { get; private set; }
  
  /// <summary>
  
  /// 事务状态
  
  /// </summary>
  
  public TransactionStatus Status { get; internal set; }
  
  /// <summary>
  
  /// 获取或设置连接数据库对象
  
  /// </summary>
  
  internal IDbConnection DbConnection { get; set; }
  
  /// <summary>
  
  /// 获取或设置事务机制对象
  
  /// </summary>
  
  internal IDbTransaction DBTransaction { get; set; }
  
  #endregion
  
  #region Member
  
  /// <summary>
  
  /// 开启事务(此处不真正开事务,会导致性能问题,所以在使用ADO.NET对象时才开启事务)
  
  /// </summary>
  
  public void BeginTransaction()
  
  {
  
  try
  
  {
  
  if (this.Counter == 0)
  
  {
  
  this.Status = TransactionStatus.已启动事务;
  
  this.IsBegin = true;
  
  this.IsCommit = this.IsRollback = this.IsRealRollback = false;
  
  if (this.BeginTransactionEvent != null)
  
  {
  
  this.BeginTransactionEvent(this);
  
  }
  
  }
  
  this.Counter++;
  
  OutupRunLog("BeginTransaction() Counter: " + this.Counter);
  
  }
  
  catch (Exception e)
  
  {
  
  if (!Debuger.IsDebug)
  
  {
  
  Log.Error("开启事务时出现异常", e);
  
  }
  
  throw new TransactionException(e);
  
  }
  
  }
  
  /// <summary>
  
  /// 提交事务
  
  /// </summary>
  
  public void Commit()
  
  {
  
  try
  
  {
  
  if (this.IsRealRollback)
  
  {
  
  return;
  
  }
  
  switch (this.Counter)
  
  {
  
  case 0:
  
  throw new TransactionException(this.Status.ToString());
  
  case 1:
  
  if (!this.DBTransaction.IsNull())
  
  {
  
  this.DBTransaction.Commit();
  
  }
  
  if (!this.DbConnection.IsNull())
  
  {
  
  this.DbConnection.Close();
  
  }
  
  this.Status = TransactionStatus.事务已提交;
  
  this.IsCommit = true;
  
  this.IsBegin = this.IsRollback = this.IsRealRollback = false;
  
  if (this.CommitEvent != null)
  
  {
  
  this.CommitEvent(this);
  
  }
  
  break;
  
  }
  
  OutupRunLog("Commit() Counter: " + this.Counter);
  
  this.Counter--;
  
  }
  
  catch (Exception e)
  
  {
  
  if (!Debuger.IsDebug)
  
  {
  
  Log.Error("提交事务时出现异常", e);
  
  }
  
  throw e;
  
  }
  
  }
  
  /// <summary>
  
  /// 强制回滚事务
  
  /// </summary>
  
  public void RealRollback()
  
  {
  
  try
  
  {
  
  if (this.IsRollback)
  
  {
  
  return;
  
  }
  
  if (!this.DBTransaction.IsNull())
  
  {
  
  this.DBTransaction.Rollback();
  
  }
  
  if (!this.DbConnection.IsNull())
  
  {
  
  this.DbConnection.Close();
  
  }
  
  this.Status = TransactionStatus.事务已强制回滚;
  
  this.IsRollback = this.IsRealRollback = true;
  
  this.IsBegin = this.IsCommit = false;
  
  if (this.RealRollbackEvent != null)
  
  {
  
  this.RealRollbackEvent(this);
  
  }
  
  OutupRunLog("RealRollback()");
  
  }
  
  catch (Exception ex)
  
  {
  
  if (!Debuger.IsDebug)
  
  {
  
  Log.Error("强制回滚事务出现异常", ex);
  
  }
  
  throw new TransactionException(ex);
  
  }
  
  finally
  
  {
  
  this.IsRealRollback = true;
  
  }
  
  }
  
  /// <summary>
  
  /// 回滚事务
  
  /// </summary>
  
  public void Rollback()
  
  {
  
  try
  
  {
  
  if (this.IsRealRollback)
  
  {
  
  return;
  
  }
  
  switch (this.Counter)
  
  {
  
  case 0:
  
  throw new TransactionException(this.Status.ToString());
  
  case 1:
  
  if (!this.DBTransaction.IsNull())
  
  {
  
  this.DBTransaction.Rollback();
  
  }
  
  if (!this.DbConnection.IsNull())
  
  {
  
  this.DbConnection.Close();
  
  }
  
  this.Status = TransactionStatus.事务已回滚;
  
  this.IsRollback = true;
  
  this.IsBegin = this.IsCommit = this.IsRealRollback = false;
  
  if (this.RollbackEvent != null)
  
  {
  
  this.RollbackEvent(this);
  
  }
  
  break;
  
  }
  
  OutupRunLog("Rollback() Counter: " + this.Counter);
  
  this.Counter--;
  
  }
  
  catch (Exception e)
  
  {
  
  if (!Debuger.IsDebug)
  
  {
  
  Log.Error("回滚事务时出现异常", e);
  
  }
  
  throw e;
  
  }
  
  }
  
  #endregion
  
  /// <summary>
  
  /// 输出运行日志
  
  /// </summary>
  
  /// <param name="msg"></param>
  
  private void OutupRunLog(string msg)
  
  {
  
  Debug.WriteLine(msg);
  
  Console.WriteLine(msg);
  
  }
  
  }
  
  /// <summary>
  
  /// 事务状态
  
  /// </summary>
  
  public enum TransactionStatus
  
  {
  
  /// <summary>
  
  /// 未开启事务
  
  /// </summary>
  
  未启动事务 = 0,
  
  /// <summary>
  
  /// 已开启事务,但未操作数据库
  
  /// </summary>
  
  已启动事务 = 1,
  
  /// <summary>
  
  /// 已开启事务,并有数据库事务被挂起
  
  /// </summary>
  
  事务已挂起 = 2,
  
  /// <summary>
  
  ///
  
  /// </summary>
  
  事务已提交 = 3,
  
  /// <summary>
  
  /// 事务已回滚
  
  /// </summary>
  
  事务已回滚 = 4,
  
  /// <summary>
  
  /// 事务已强制回滚
  
  /// </summary>
  
  事务已强制回滚 = 5,
  
  }
  
  }
  
  复制代码
  
  原理不复杂,都是调用System.Data 的 IDbTransaction 去完成的,经典的地方在于这个 Counter 事务计数器累加事务。
  
  而且阿杰的代码有个很有限的地方,并不是调用 BeginTransaction(),就去开启事务,这里是一个非常巧妙巧妙的设计,有效的
  
  避免了个别马虎的技术员开启了事务却有在代币流程中忘记提交or回滚造成的数据库死锁。
  
  下午阿杰过来和我聊了会,我说我在写关于事务的博客,阿杰说了很多。包括他当初为什么这样设计,以及他综合了动软基础框架的
  
  事务,还有微软的分布式事务。只是我还没办法转换成自己的语言写成博客,同时也要我经历像阿杰这样的创作过程,才能
  
  像他那样富含底蕴的讲述他的思考逻辑。
  
  今天我就写到这里,本来想跟深入的剖析一下Transaction对象,但是仔细看了一下,有些地方我也不是特别明白。哈哈,有点尴尬!
  
  关于对事务这一块的理解,如果有机会我希望阿杰也能写一篇博客,比我更详细的阐述Winner事务,尤其是他在对比多个框架的事务后,
  
  他对Winner创作过程的思考。他一定写的比我出彩,毕竟我只是一个使用者,而他是创这者。
  
  关于Winner,我新建了一个QQ群,有兴趣的可以加我们QQ群,阿杰,jason都在群中。我们可以一起探讨Winner,群号:261083244
  
  也可以扫描博客左侧二维码加群。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“数据库事务(Database Transaction) 的相关文章

  • 将图片保存成字符串,以及字符串转换为图片

    将图片保存成字符串的样子保存在excel表格 上代码 public class FileConfig 当前项目目录下的files public static String FILE ADDRESS PATH System getProper
  • C语言入门之工资计算

    include
  • Python 第一章 基础知识(3) 数字和表达式 加减乘除

    第一章 基础知识 3 数字和表达式 运行IDLE 在提示符前输入 加法 gt gt gt 2 2 4 lt 解释器会得出2 2的答案4 除法 gt gt gt 1 2 0 lt 解释器会给出截除掉小数部分的1除以2的商 gt gt gt 1
  • Mybatis-plus:条件查询的方法

    方法1 QueryWrapper
  • windows系统80端口号被System占用

    废话不多说 直入主题 windows系统80端口号被System占用 查找追踪看到是 PID 4 的一个System进程在占用 网上所说的解决方法 方法一 1 Win R 组合快捷键 快速打开运行命令框 在打开后面键入命令 Regedit
  • 码农得用专用的Code字体,推荐几款专业级别的程序员专用字体

    别怀疑 下面的这些字体是程序员专用的编码字体 尤其是带 Code 名字的字体 从名字上看就知道 专门用来Code用的 1 Source Code Pro PS那个公司知道吧 就是这个公司专门为程序员设计的等宽字体 要知道 写代码 一般的字体
  • 光束法空三的计算问题,误差方程的多余观测数,未知数个数、多余观测值的计算

    1 未知数个数 必要观测值个数 未知数个数 t u 3 未知点数目 6 相片数目 3 代表一个未知点的 x y z 6代表一张像片的6个外方位元素 都是待定值 都是未知数 2 观测值个数 1 未知数个数求法 1 观测值个数 n 2 m 的含
  • sparksql压缩小文件

    SET spark sql shuffle partitions 2 SET spark sql adaptive enabled true SET spark sql adaptive shuffle targetPostShuffleI
  • Kotlin 常见符号大全

    gt 下划线 as 1 修饰在成员变量的类型后面 表示这个变量可以为null 系统在任何情况下不会报它的空指针异常 修改在对象后面代表该对象如果为null则不处理后面逻辑 如果我们定义的变量是一个可以为空的类型 则要用 String 在变量
  • Qt、MinGW编译OpenCV 4.5.4(包含opencv_contrib)详细过程

    Qt MinGW CMake编译OpenCV 4 5 4 包含opencv contrib 详细过程 目录 Qt MinGW CMake编译OpenCV 4 5 4 包含opencv contrib 详细过程 1 工具下载准备 2 CMak
  • Linux·异步IO编程框架

    hi 大家好 今天分享一篇Linux异步IO编程框架文章 对比IO复用的epoll框架 到底性能提高多少 让我们看一看 译者序 本文组合翻译了以下两篇文章的干货部分 作为 io uring 相关的入门参考 How io uring and
  • ZGC的运行过程以及读屏障

    ZGC运作过程 ZGC的运作过程大致可划分为以下四个大的阶段 并发标记 Concurrent Mark 与G1一样 并发标记是遍历对象图做可达性分析的阶段 它的初始标记 Mark Start 和最终标记 Mark End 也会出现短暂的停顿
  • 了解前端监控和埋点(持续更新中,,,)

    基本概念 前端埋点主要是为了运营以及开发人员采集用户行为数据 以及页面性能等数进行后续的数据分析 一般目的 1 性能监控 2 数据监控 3 异常监控 埋点方案 5 代码埋点 6 无埋点 7 可视化埋点 埋点的目的 在现今用户就是上帝的年代
  • 会话保持和非会话保持

    会话保持 方式 1 session机制保持会话 缺点 1 1当前服务器处于高并发的时候 对于服务器是一个压力 占用大量的服务器资源 1 2如果一个业务系统是分布式部署在多个服务器的情况下 session不能相互间共享 解决方式 1 3高并发
  • 十个常见异常实例

    第一个 try float j 1 0 catch Exception e exceptions add e 第二个 try String s null String m s toLowerCase catch Exception e ex
  • 常见软件开发模式

    一 瀑布模型 瀑布模型的特点 1 阶段间具有顺序性和依赖性 2推迟实现 3质量保证的观点 总结 瀑布模型是文档驱动的模型 遵守这个约束可使软件维护变得比较容易一些 从而显著降低软件预算 二 快速原型 快速分析 gt 构造 gt 运行 gt
  • NOIP 1998 普及组 2的幂次方 解题报告

    虽说是普及组 还是不好处理啊 想了好久 最后决定使用一个栈来维护所有的枚举 总的来说我这里算是用了两个栈 因为BFS本身就能算一个栈 这个题目真的有挑战性啊 算法没什么 但是规律方面的东西好重要饿 不说了 代码 include
  • 逻辑运算符详细讲解(基础版)

    本文将详细讲解6个逻辑运算符的应用 总结放在最后了哦 1 与 gt 见false则为false 这里用两个关系表达式进行比较 只要其中一个运算结果为false则最后结果也为false 2 或 gt 见true则为true 这里用两个关系表达
  • openGauss学习笔记-66 openGauss 数据库管理-创建和管理schema

    文章目录 openGauss学习笔记 66 openGauss 数据库管理 创建和管理schema 66 1 背景信息 66 2 注意事项 66 3 操作步骤 66 3 1 创建管理用户及权限schema 66 3 2 使用schema 6
  • python-图形用户界面

    图形用户界面 1 python中图形界面库 界面开发 Tkinter 是 Python 官方提供的图形用户界面开发库 用于封装 TGUI 工具包 跨平台 PyQt 是非 Python 官方提供的图用户界面开发库 用于封装 Qt 工具包 跨平

随机推荐