项目中经常会用到自增id 比如uid 最简单的方法就是用直接用数据库提供的AUTO_INCREMENT 但是如果用户量非常大 几千万 几亿然后需要分表存储的时候呢 这种方案就搞不定了 所以最好有一个全局的自增ID的生成器 不管是否分表 都能从生成器中获取到全局自增的ID 实现方法应该有很多 不过所有的方案都需要解决一个问题 就是保证在高并发的情景下 数据获取依然正确 每次获取的ID都不会重复 这里我分享两种利用mysql的innodb的事务特性来实现的方案 一种是实现过了的 另一种没有试验过 不过应该也能走的通 先介绍第一种 在数据库中单独设置一张表 来存储ID 表有两个字段 一个是种类吧 一个就是ID
复制代码 代码如下:
CREATE TABLE auto_id( idname varchar( ) NOT NULL DEFAULT id bigint( ) NOT NULL DEFAULT MENT primary key(idname) )ENGINE=Innodb DEFAULT CHARSET=utf ;
接下来是一个存储过程复制代码 代码如下:
delimiter // drop procedure if exists get_increment_id; create procedure get_increment_id(in idname_in varchar( ) in all_in bigint out id_out bigint) begin declare oldid bigint; start transaction; select id into oldid from maibo_auto_id where idname=idname_in for update; if oldid is NULL then insert into maibo_auto_id(idname id) value(idname_in all_in); set id_out= all_in; else update maibo_auto_id set id=id+ where idname=idname_in; set id_out=oldid+ ; end if; mit; end; //
重点是这句 select id into oldid from maibo_auto_id where idname=idname_in for update 会给相关数据加一个独占锁定 这时候别的进程如果来读取该条记录 就会进入等待 等待这个进程mit之后 再继续 这样就保证了在并发的情况下 不同的进程不会取到相同的值 如果你的前端是用php实现的 只需执行如下两个sql 就可以获取到 这个 all参数是定义的是从多少开始自增复制代码 代码如下:
$sql = call get_increment_id( {$key} {$ all} @id) ; $ret = $db >getData( select @id );
还有另外一种方法 就是利用mysql的auto_increment 先创建一张表 表里边只有一个自增字段复制代码 代码如下:
create table test( `id` int( ) NOT NULL AUTO_INCREMENT MENT id primary key (id) )ENGINE=MyISAM AUTO_INCREMENT= DEFAULT CHARSET=utf ;
通过如下两条sql复制代码 代码如下:
也能解决问题 LAST_INSERT_ID是不用查表的 而且只针对当前连接 也就是说别的连接的更新不会影响到当前连接的取值 这样可能每个ID都得弄一张表来维护 这也是缺点 具体使用中如何处理 就看自己的选择了 lishixinzhi/Article/program/MySQL/201405/30865
id 自增,name 为字符串类型
table 依然为空,开启事务后在没有commit的情况下,是没有修改 table 的。
这里将事务 b 直接提交
由于事物 b 提交了,插入了一行数据,id 为 2,所以这里生成 id 是在插入的时候。
这个时候开始提交事物a
事务 a 和 b 都提交成功,上一个事务a的插入的数据项也出现了。
由于事物 c 回滚,事物 d 提交成功,所以 table 中确实是少了一行数据。
自增 id,是在插入的时候就已经生成了,事务并不影响 id 的自增。
如果事务回滚,table 对应的数据行就会缺失,id 也会缺失,自增 id 和事务是独立的,互不影响。
MVCC(Mutil-Version Concurrency Control),就是多版本并发控制。这种并发控制的方法,主要应用在RC和RR隔离级别的事务当中,利用执行select *** 作时,访问记录版本链,使得不同事物的读写,写读可以并发执行,提高系统性能。
Innodb 有两个隐藏字段 trx_id(事务id)和roll_pointer(回滚指针)。
transaction id :
innoDB里面每个事务有一个唯一的事务ID,叫作transaction id,它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。
roll_pointer :
指向上一事务版本的指针。
版本链 :
是一个单链表结构,对于同一行数据,每一个事务对其进行更新的时候都会产生一个新的版本,就会存储在这个链表当中。
一个存储事务id的列表。
readview的几个参数:
m_ids:表示活跃事务id列表
min_trx_id:活跃事务中的最小事务id
max_trx_id:已创建的最大事务id
creator_trx_id:当前的事务id。
readview的生成时机:
RC隔离级别:每次读取数据前,都生成一个readview;
RR隔离级别:在第一次读取数据前,生成一个readview;
使用场景:
[ 创建事务节点 ] 当我创建一个新的事务需要读取一行数据, 我会查询活跃的事务列表; 假设我当前的事务id是200, 当前活跃的事务id没有我的200, 因此需要去拷贝一个最新的不活跃事务并在版本链最后插入一个新节点200; mysql会去对比版本链和readView, 假设版本链数据为[1,50,100,150], 活跃列表为[100,150], 说明100和150都是未提交的活跃事务, 再向前一个节点50不在活跃事务列表说明事务50已经提交, 所以事务200拷贝事务50并插入版本链最后, 且将200追加到readView活跃列表的最后一个元素
[ 使用事务节点 ] 当我再次进行200号事务的查询或修改, 我需要读版本链的数据, 因为上一次 *** 作已经在版本链做了200号节点, 因此我读的数据都是200号节点的数据, 这样就隔离了其他未提交的事务; 我的全部增删查改都在200号版本链上进行
[ readView实现事务隔离级别 ]以上两点都是基于隔离级别"读已提交"来进行说明的; 当mysql设置为"可重复读"时, 不同事务仍然是保存在版本链的不同节点上, 只不过新的事务创建的时候拷贝了当下的readView列表, 只要新事物不提交就一直使用这个拷贝的活跃列表; 假设此时100号数据提交了, 我在新事务执行了select 会去查活跃列表发现100号事务还是未提交状态, 因此读取到的还是50号事务提交的记录。
原子性,一致性,隔离性,持久性。
未提交读(read uncommitted)、提交读(read committed)、可重复读(repeatable read)、序列化读(serializable)
1,分布式事务产生的背景。
分情况而定
1, 在单体的项目中,多个不同的业务逻辑都是在同一个数据源中实现事务管理,是不存在分布式事务的问题,因为同一个数据源的情况都是采用事务管理器,相当于每个事务管理器对应一个数据源。
[上传失败(image-810669-1618491127348)]
2,在单体的项目中,有多个不同的数据源,每个数据源都有自己独立的事务管理器,互不影响,那么这时候也会存在多数据源事务管理: 解决方案 jta+Atomikos
[上传失败(image-7df061-1618491220423)]
3,在分布式/微服务架构中,每个服务都有自己的本地事务,每个服务本地事务互不影响,那么这时候也会存在分布式事务的问题。
事务的定义:
对我们的业务逻辑可以实现提交或者回滚,保证数据的一致性的情况。
所以要么提交,要么回滚
原子性a 要么提交 要么回滚
一致性c
隔离性i 多个事务在一起执行的时候,互不影响;
持久性d 事务一旦提交或者回滚后,不会在对该结果有任何影响
2,传统分布式事务解决方案
3,2PC/3PC协议使用场景。
4,LCN为什么不更新了?那些思想值得学习、
5,分布式事务解决方案有哪些?
6,强一致性/最终一致性区别。
7,LCn深度源码解读。
1 CAP定律和BASE理论
11 CAP定律#
这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
(一)一致性(C)
在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
(二)可用性(A)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
(三)分区容错性(P) 形成脑裂问题
以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前 *** 作在C和A之间做出选择。
>
你好,用sql语句是不可能实现的,我们可以通过事务来实现,也就是说,SqlCommand的CmdText属性在一个方法当中可以赋多个SQL语句 SqlConnection sqlConnection = new SqlConnection(); 初始化连接 // 开启事务 SqlTransaction sqlTransaction = sqlConnectionBeginTransaction(); // 将事务应用于Command SqlCommand sqlCommand = new SqlCommand(); sqlCommandConnection = sqlConnection; sqlCommandTransaction = sqlTransaction; sqlCommandCmdText=第一个sql语句 sqlCommandExcuteNoneQuery(); sqlCommandCmdText=第二个sql语句 sqlCommandExcuteNoneQuert(); try { // 利用sqlcommand进行数据 *** 作 // 成功提交 sqlTransactionCommit(); } catch(Exception ex) { // 出错回滚 sqlTransactionRollback(); } 如果在执行第二次SQL语句是出错了,那么就会到Catch异常中,执行回滚,那么第一次执行的也同样回滚了,所以必须2个都一行成功才往数据库中提交这里是用了2次SQL语句,如果你想用1个语句同时 *** 控2个表的话,就要用到存储过程或者是触发器,存储过程和触发器
一张表怎么与一张表的一个字段的关联获取它的id值的方法。
如下参考:
1第一步是创建数百个不相关的表,但是请注意,必须对表使用与主键表相同的数据类型作为外键表。
2设置可以唯一标识为主键的行,其他表与跟踪类似。
3接下来,添加关系,如下所示。
4通过拖动添加特殊关系,如下图。
5创建表STUDENT()——SnoCHAR(10)主键,SnameCHAR(10)UNIQUE,SsexCHAR(2),SageSMALLINT,SdeptCHAR(10)。
以上就是关于利用mysql事务特性实现并发安全的自增ID示例全部的内容,包括:利用mysql事务特性实现并发安全的自增ID示例、关于 mysql 事务中的自增 id 的疑问、MYSQL的事务隔离级别,MVCC,readView和版本链小结等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)