当前位置: 首页 > news >正文

网站建设的项目方案模板网络事件营销案例

网站建设的项目方案模板,网络事件营销案例,深圳做网站比较好,网页的后台管理系统自增主键不能保证连续递增 Hi#xff0c;我是阿昌#xff0c;今天学习记录的是关于自增主键不能保证连续递增的内容。 MySql保证了主键是自增#xff0c;但不相对连续#xff1b;帮助开发人员快速识别每个行的唯一性#xff0c;并提高查询效率。 自增主键可以让主键索引…自增主键不能保证连续递增 Hi我是阿昌今天学习记录的是关于自增主键不能保证连续递增的内容。 MySql保证了主键是自增但不相对连续帮助开发人员快速识别每个行的唯一性并提高查询效率。 自增主键可以让主键索引尽量地保持递增顺序插入避免了页分裂因此索引更紧凑。 之前我见过有的业务设计依赖于自增主键的连续性也就是说这个设计假设自增主键是连续的。但实际上这样的假设是错的因为自增主键不能保证连续递增。 创建一个表 t其中 id 是自增主键字段、c 是唯一索引。 CREATE TABLE t (id int(11) NOT NULL AUTO_INCREMENT,c int(11) DEFAULT NULL,d int(11) DEFAULT NULL,PRIMARY KEY (id),UNIQUE KEY c (c) ) ENGINEInnoDB;一、自增值保存在哪儿 在这个空表 t 里面执行 insert into t values(null, 1, 1); 插入一行数据再执行 show create table 命令就可以看到如下图所示的结果 可以看到表定义里面出现了一个 AUTO_INCREMENT2表示下一次插入数据时如果需要自动生成自增值会生成 id2。 其实这个输出结果容易引起这样的误解自增值是保存在表结构定义里的。 实际上表的结构定义存放在后缀名为.frm 的文件中但是并不会保存自增值。 不同的引擎对于自增值的保存策略不同。 MyISAM 引擎的自增值保存在数据文件中。InnoDB 引擎的自增值其实是保存在了内存里并且到了 MySQL 8.0 版本后才有了“自增值持久化”的能力也就是才实现了“如果发生重启表的自增值可以恢复为 MySQL 重启前的值”具体情况是 在 MySQL 5.7 及之前的版本自增值保存在内存里并没有持久化。每次重启后第一次打开表的时候都会去找自增值的最大值 max(id)然后将 max(id)1 作为这个表当前的自增值。举例来说如果一个表当前数据行里最大的 id 是 10AUTO_INCREMENT11。这时候我们删除 id10 的行AUTO_INCREMENT 还是 11。但如果马上重启实例重启后这个表的 AUTO_INCREMENT 就会变成 10。也就是说MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值。在 MySQL 8.0 版本将自增值的变更记录在了 redo log 中重启的时候依靠 redo log 恢复重启之前的值。 二、自增值修改机制 在 MySQL 里面如果字段 id 被定义为 AUTO_INCREMENT在插入一行数据的时候自增值的行为如下 如果插入数据时 id 字段指定为 0、null 或未指定值那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段如果插入数据时 id 字段指定了具体的值就直接使用语句里指定的值。 根据要插入的值和当前自增值的大小关系自增值的变更结果也会有所不同。 假设某次要插入的值是 X当前的自增值是 Y。 如果 XY那么这个表的自增值不变如果 X≥Y就需要把当前自增值修改为新的自增值。 新的自增值生成算法是从 auto_increment_offset 开始以 auto_increment_increment 为步长持续叠加直到找到第一个大于 X 的值作为新的自增值。 其中auto_increment_offset 和 auto_increment_increment 是两个系统参数分别用来表示自增的初始值和步长默认值都是 1。 备注在一些场景下使用的就不全是默认值。比如双 M 的主备结构里要求双写的时候我们就可能会设置成 auto_increment_increment2让一个库的自增 id 都是奇数另一个库的自增 id 都是偶数避免两个库生成的主键发生冲突。 当 auto_increment_offset 和 auto_increment_increment 都是 1 的时候新的自增值生成逻辑很简单就是 如果准备插入的值 当前自增值新的自增值就是“准备插入的值 1”否则自增值不变。 三、自增值的修改时机 在这两个参数都设置为 1 的时候自增主键 id 却不能保证是连续的这是什么原因呢 假设表 t 里面已经有了 (1,1,1) 这条记录这时再执行一条插入数据命令 insert into t values(null, 1, 1); 这个语句的执行流程就是 执行器调用 InnoDB 引擎接口写入一行传入的这一行的值是 (0,1,1);InnoDB 发现用户没有指定自增 id 的值获取表 t 当前的自增值 2将传入的行的值改成 (2,1,1);将表的自增值改成 3继续执行插入数据操作由于已经存在 c1 的记录所以报 Duplicate key error语句返回。 对应的执行流程图如下 可以看到这个表的自增值改成 3是在真正执行插入数据的操作之前。 这个语句真正执行的时候因为碰到唯一键 c 冲突所以 id2 这一行并没有插入成功但也没有将自增值再改回去。 所以在这之后再插入新的数据行时拿到的自增 id 就是 3。 也就是说出现了自增主键不连续的情况。 如图 3 所示就是完整的演示结果。 可以看到这个操作序列复现了一个自增主键 id 不连续的现场 (没有 id2 的行。 可见唯一键冲突是导致自增主键 id 不连续的第一种原因。 同样地事务回滚也会产生类似的现象这就是第二种原因。 下面这个语句序列就可以构造不连续的自增 id可以自己验证一下。 insert into t values(null,1,1); begin; insert into t values(null,2,2); rollback; insert into t values(null,2,2); //插入的行是(3,2,2)为什么在出现唯一键冲突或者回滚的时候MySQL 没有把表 t 的自增值改回去呢 如果把表 t 的当前自增值从 3 改回 2再插入新数据的时候不就可以生成 id2 的一行数据了吗其实MySQL 这么设计是为了提升性能。 假设有两个并行执行的事务在申请自增值的时候为了避免两个事务申请到相同的自增 id肯定要加锁然后顺序申请。 假设事务 A 申请到了 id2 事务 B 申请到 id3那么这时候表 t 的自增值是 4之后继续执行。事务 B 正确提交了但事务 A 出现了唯一键冲突。如果允许事务 A 把自增 id 回退也就是把表 t 的当前自增值改回 2那么就会出现这样的情况表里面已经有 id3 的行而当前的自增 id 值是 2。接下来继续执行的其他事务就会申请到 id2然后再申请到 id3。这时就会出现插入语句报错“主键冲突”。 而为了解决这个主键冲突有两种方法 每次申请 id 之前先判断表里面是否已经存在这个 id。如果存在就跳过这个 id。但是这个方法的成本很高。因为本来申请 id 是一个很快的操作现在还要再去主键索引树上判断 id 是否存在。把自增 id 的锁范围扩大必须等到一个事务执行完成并提交下一个事务才能再申请自增 id。这个方法的问题就是锁的粒度太大系统并发能力大大下降。 可见这两个方法都会导致性能问题。造成这些麻烦的罪魁祸首就是我们假设的这个“允许自增 id 回退”的前提导致的。 因此InnoDB 放弃了这个设计语句执行失败也不回退自增 id。 也正是因为这样所以才只保证了自增 id 是递增的但不保证是连续的。 四、自增锁的优化 可以看到自增 id 锁并不是一个事务锁而是每次申请完就马上释放以便允许别的事务再申请。其实在 MySQL 5.1 版本之前并不是这样的。 在 MySQL 5.0 版本的时候自增锁的范围是语句级别。也就是说如果一个语句申请了一个表自增锁这个锁会等语句执行结束以后才释放。显然这样设计会影响并发度。 MySQL 5.1.22 版本引入了一个新策略新增参数 innodb_autoinc_lock_mode默认值是 1。 这个参数的值被设置为 0 时表示采用之前 MySQL 5.0 版本的策略即语句执行结束后才释放锁这个参数的值被设置为 1 时 普通 insert 语句自增锁在申请之后就马上释放类似 insert … select 这样的批量插入数据的语句自增锁还是要等语句结束后才被释放 这个参数的值被设置为 2 时所有的申请自增主键的动作都是申请后就释放锁。 为什么默认设置下insert … select 要使用语句级的锁为什么这个参数的默认值不是 2 答案是这么设计还是为了数据的一致性。 一起来看一下这个场景 在这个例子里我往表 t1 中插入了 4 行数据然后创建了一个相同结构的表 t2然后两个 session 同时执行向表 t2 中插入数据的操作。你可以设想一下如果 session B 是申请了自增值以后马上就释放自增锁那么就可能出现这样的情况 session B 先插入了两个记录(1,1,1)、(2,2,2)然后session A 来申请自增 id 得到 id3插入了3,5,5)之后session B 继续执行插入两条记录 (4,3,3)、 (5,4,4)。 你可能会说这也没关系吧毕竟 session B 的语义本身就没有要求表 t2 的所有行的数据都跟 session A 相同。 是的从数据逻辑上看是对的。但是如果我们现在的 binlog_formatstatement可以设想下binlog 会怎么记录呢 由于两个 session 是同时执行插入数据命令的所以 binlog 里面对表 t2 的更新日志只有两种情况要么先记 session A 的要么先记 session B 的。 但不论是哪一种这个 binlog 拿去从库执行或者用来恢复临时实例备库和临时实例里面session B 这个语句执行出来生成的结果里面id 都是连续的。这时这个库就发生了数据不一致。你可以分析一下出现这个问题的原因是什么其实这是因为原库 session B 的 insert 语句生成的 id 不连续。 这个不连续的 id用 statement 格式的 binlog 来串行执行是执行不出来的。而要解决这个问题有两种思路 一种思路是让原库的批量插入数据语句固定生成连续的 id 值。所以自增锁直到语句执行结束才释放就是为了达到这个目的。另一种思路是在 binlog 里面把插入数据的操作都如实记录进来到备库执行的时候不再依赖于自增主键去生成。这种情况其实就是 innodb_autoinc_lock_mode 设置为 2同时 binlog_format 设置为 row。 因此在生产上尤其是有 insert … select 这种批量插入数据的场景时从并发插入数据性能的角度考虑建议你这样设置innodb_autoinc_lock_mode2 并且 binlog_formatrow. 这样做既能提升并发性又不会出现数据一致性问题。 需要注意的是这里说的批量插入数据包含的语句类型是 insert … select、replace … select 和 load data 语句。但是在普通的 insert 语句里面包含多个 value 值的情况下即使 innodb_autoinc_lock_mode 设置为 1也不会等语句执行完成才释放锁。 因为这类语句在申请自增 id 的时候是可以精确计算出需要多少个 id 的然后一次性申请申请完成后锁就可以释放了。 也就是说批量插入数据的语句之所以需要这么设置是因为“不知道要预先申请多少个 id”。 既然预先不知道要申请多少个自增 id那么一种直接的想法就是需要一个时申请一个。但如果一个 select … insert 语句要插入 10 万行数据按照这个逻辑的话就要申请 10 万次。 显然这种申请自增 id 的策略在大批量插入数据的情况下不但速度慢还会影响并发插入的性能。 因此对于批量插入数据的语句MySQL 有一个批量申请自增 id 的策略 语句执行过程中第一次申请自增 id会分配 1 个1 个用完以后这个语句第二次申请自增 id会分配 2 个2 个用完以后还是这个语句第三次申请自增 id会分配 4 个依此类推同一个语句去申请自增 id每次申请到的自增 id 个数都是上一次的两倍。 举个例子这个语句序列 insert into t values(null, 1,1); insert into t values(null, 2,2); insert into t values(null, 3,3); insert into t values(null, 4,4); create table t2 like t; insert into t2(c,d) select c,d from t; insert into t2 values(null, 5,5);insert…select实际上往表 t2 中插入了 4 行数据。但是这四行数据是分三次申请的自增 id第一次申请到了 id1第二次被分配了 id2 和 id3 第三次被分配到 id4 到 id7。由于这条语句实际只用上了 4 个 id所以 id5 到 id7 就被浪费掉了。 之后再执行 insert into t2 values(null, 5,5)实际上插入的数据就是8,5,5)。这是主键 id 出现自增 id 不连续的第三种原因。 五、总结 在 MyISAM 引擎里面自增值是被写在数据文件上的。而在 InnoDB 中自增值是被记录在内存的。MySQL 直到 8.0 版本才给 InnoDB 表的自增值加上了持久化的能力确保重启前后一个表的自增值不变。 自增值改变的时机分析了为什么 MySQL 在事务回滚的时候不能回收自增 id。MySQL 5.1.22 版本开始引入的参数 innodb_autoinc_lock_mode控制了自增值申请时的锁范围。 从并发性能的角度考虑建议你将其设置为 2同时将 binlog_format 设置为 row。binlog_format 设置为 row是很有必要的。 在最后一个例子中执行 insert into t2(c,d) select c,d from t; 这个语句的时候如果隔离级别是可重复读repeatable readbinlog_formatstatement。 这个语句会对表 t 的所有记录和间隙加锁。觉得为什么需要这么做呢 假如原库不对t表所有记录和间隙加锁如果有其他事物新增数据并先与这个批量操作提交由于事物的隔离级别是可重复读t2是看不到新增的数据的。但是记录的binlog是statement格式备库或基于binlog恢复的临时库t2会看到新增的数据出现数据不一致的情况。
http://www.hkea.cn/news/14590965/

相关文章:

  • 传媒公司做网站编辑 如何设计坞官网首页
  • 商城的网站统计如何做重庆制作网站公司哪家好
  • 岳阳建设公司网站门户网站建设工作室
  • 高端网站建设内容泰安网页设计公司
  • 昆明睿腾科技网站建设swf影视网站源码
  • 做磁力搜索网站违法吗青岛网站建站团队
  • 淘宝做女鞋在哪个网站找货上海嘉定建设局网站
  • 凡科网站内容怎么做效果好网上推广的平台有哪些
  • 向祖国建设者致敬网站在线网站建设工程标准
  • 六安企业网站seo多少钱做的好的区块链网站
  • 网站设计 广州太原广告设计公司
  • 网站开发模板如何给网站配色
  • 做网站目录12306网站的建设历程
  • 网站收缩目录模板做公司的网站有哪些东西
  • 苏州专业网站建设公司网站集约化建设会议
  • 秦淮html5响应式网站企业移动端建设与网站建设
  • 如何优化企业网站平台网站很难做
  • 网站开发培训太原网站设计前期沟通单
  • 佛山网站建设 乐云践新专家酒泉网站怎么做seo
  • 简洁的企业网站手机网站建设流程
  • 如何 建公司网站wordpress侧栏小工具栏
  • 中国移动官方网站湖南省建设厅城乡建设网站
  • 英文网站网站建设最近新闻
  • 做固定背景网站长沙互联网公司在哪个区
  • 服务好的成都网站建设在线视频网站开发成本
  • 网站开发业务流程图网站建设公司哪家好 在线磐石网络
  • 域名注册和网站建设市场监督管理局官网查询
  • 软件开放和网站开发wordpress分类目录打不开
  • 做伤残鉴约号网站响应式网站建设需要注意什么
  • 台州网站制作方案什么是优化产业结构