SQLite什么都好,就怕“database is locked”这些年来想尽办法去规避它。

测试代码:

  1. static void Test2()
  2. {
  3. XCode.Setting.Current.TransactionDebug = true;
  4.  
  5. XTrace.WriteLine(Role.Meta.Count + "");
  6. XTrace.WriteLine(Log.Meta.Count + "");
  7. Console.Clear();
  8.  
  9. Task.Run(() => TestTask());
  10. Thread.Sleep();
  11. Task.Run(() => TestTask());
  12. }
  13.  
  14. static void TestTask(Int32 tid)
  15. {
  16. try
  17. {
  18. XTrace.WriteLine("TestTask {0} Start", tid);
  19. using (var tran = Role.Meta.CreateTrans())
  20. {
  21. var role = new Role();
  22. role.Name = "R" + DateTime.Now.Millisecond;
  23. role.Save();
  24. XTrace.WriteLine("role.ID={0}", role.ID);
  25.  
  26. Thread.Sleep();
  27.  
  28. role = new Role();
  29. role.Name = "R" + DateTime.Now.Millisecond;
  30. role.Save();
  31. XTrace.WriteLine("role.ID={0}", role.ID);
  32.  
  33. Thread.Sleep();
  34.  
  35. if (tid == ) tran.Commit();
  36. }
  37. }
  38. catch (Exception ex)
  39. {
  40. XTrace.WriteException(ex);
  41. }
  42. finally
  43. {
  44. XTrace.WriteLine("TestTask {0} End", tid);
  45. }
  46. }

预热环境以后,我们开了两个任务去执行测试函数,间隔1秒。
测试函数负责插入两行数据,间隔3秒。
第一个任务最后会回滚,第二个任务提交。
显然,两个任务会重叠。

比较好奇,任务1申请得到自增1后,任务2申请得到的自增会是多少?
任务1回滚以后,它所申请得到的自增数字如何处理?

结果:

  1. 02:45:03.470 6 Y 5 TestTask 1 Start
  2. 02:45:03.470 6 Y 5 Transaction.Begin ReadCommitted
  3. 02:45:03.486 6 Y 5 Select Count(*) From Role Where Name='R470'
  4. 02:45:03.501 6 Y 5 Insert Into Role(Name, IsSystem, Permission) Values('R470', 0, '');Select last_insert_rowid() newid
  5. 02:45:03.517 6 Y 5 开始初始化实体类UserX
  6. 02:45:03.517 6 Y 5 完成初始化实体类UserX
  7. 02:45:03.533 6 Y 5 role.ID=11
  8. 02:45:04.486 14 Y 6 TestTask 2 Start
  9. 02:45:04.486 14 Y 6 Transaction.Begin ReadCommitted
  10. 02:45:04.486 14 Y 6 Select Count(*) From Role Where Name='R486'
  11. 02:45:04.486 14 Y 6 Insert Into Role(Name, IsSystem, Permission) Values('R486', 0, '');Select last_insert_rowid() newid
  12. 02:45:05.251 15 Y 7 Transaction.Begin ReadCommitted
  13. 02:45:05.251 15 Y 7 Insert Into Log(Category, [Action], LinkID, CreateUserID, CreateTime, Remark) Values('角色', '添加', 11, 0, '2017-01-27 02:45:03', 'ID=11,Name=R470');Select last_insert_rowid() newid
  14. 02:45:06.548 6 Y 5 Select Count(*) From Role Where Name='R548'
  15. 02:45:06.548 6 Y 5 Insert Into Role(Name, IsSystem, Permission) Values('R548', 0, '');Select last_insert_rowid() newid
  16. 02:45:06.548 6 Y 5 role.ID=12
  17. 02:45:09.555 6 Y 5 Transaction.Rollback ReadCommitted
  18. 02:45:09.555 6 Y 5 TestTask 1 End
  19. 02:45:09.618 14 Y 6 SQL耗时较长,建议优化 5,120毫秒 Insert Into Role(Name, IsSystem, Permission) Values('R486', 0, '');Select last_insert_rowid() newid
  20. 02:45:09.618 14 Y 6 role.ID=11
  21. 02:45:12.633 14 Y 6 Select Count(*) From Role Where Name='R633'
  22. 02:45:12.633 14 Y 6 Insert Into Role(Name, IsSystem, Permission) Values('R633', 0, '');Select last_insert_rowid() newid
  23. 02:45:12.633 14 Y 6 role.ID=12
  24. 02:45:15.649 14 Y 6 Transaction.Commit ReadCommitted
  25. 02:45:15.649 14 Y 6 TestTask 2 End
  26. 02:45:15.774 15 Y 7 SQL耗时较长,建议优化 10,519毫秒 Insert Into Log(Category, [Action], LinkID, CreateUserID, CreateTime, Remark) Values('角色', '添加', 11, 0, '2017-01-27 02:45:03', 'ID=11,Name=R470');Select last_insert_rowid() newid
  27. 02:45:15.774 15 Y 7 Transaction.Commit ReadCommitted
  28. 02:45:16.622 16 Y 9 Transaction.Begin ReadCommitted
  29. 02:45:16.622 16 Y 9 Insert Into Log(Category, [Action], LinkID, CreateUserID, CreateTime, Remark) Values('角色', '添加', 12, 0, '2017-01-27 02:45:06', 'ID=12,Name=R548');Select last_insert_rowid() newid
  30. 02:45:16.622 16 Y 9 Insert Into Log(Category, [Action], LinkID, CreateUserID, CreateTime, Remark) Values('角色', '添加', 11, 0, '2017-01-27 02:45:09', 'ID=11,Name=R486');Select last_insert_rowid() newid
  31. 02:45:16.622 16 Y 9 Insert Into Log(Category, [Action], LinkID, CreateUserID, CreateTime, Remark) Values('角色', '添加', 12, 0, '2017-01-27 02:45:12', 'ID=12,Name=R633');Select last_insert_rowid() newid
  32. 02:45:16.637 16 Y 9 Transaction.Commit ReadCommitted

从测试结果来看:
1,任务1申请得到11和12,任务2也是
2,任务1申请得到11后,任务2启动,执行到Insert时阻塞了5.12秒,直到任务1回滚了事务
3,线程15和16是异步写日志,显然它们也被阻塞,线程15阻塞10.519秒,知道任务2提交事务

结论:SQLite执行更新事务操作时使用排它锁,强制自增数字同步分配!

参考:
http://sqlite.1065341.n5.nabble.com/Transactions-and-sqlite3-last-insert-rowid-td8905.html

> If I understand it correctly, connection C1 can do an INSERT, get
> ROWID 4, C2 does an INSERT, gets 5, and commits, and then C1 commits,
> with its 4; if C1 rolled back, there's no 4 in the database, just 5
> and whatever else, correct?
>
No, this can't happen. As soon as C1 does its insert, it acquires an
exclusive lock on the database. C2 can't do an insert until C1 either
commits or rolls back and releases the lock. If C1 committed, then C2
will get 5, if C1 rolled back, then C2 will get 4.

SQLite事务与自增深度分析的更多相关文章

  1. C# SQLite事务操作方法分析

    本文实例讲述了C# SQLite事务操作方法.分享给大家供大家参考,具体如下: 在 C#中执行Sqlite数据库事务有两种方式:SQL代码和C#代码 1. SQL代码: BEGIN… COMMIT / ...

  2. const与readonly深度分析(.NET)

    前言 很多.NET的初学者对const和readonly的使用很模糊,本文就const和readonly做一下深度分析,包括: 1. const数据类型的优势 2. const数据类型的劣势 3. r ...

  3. 转:[gevent源码分析] 深度分析gevent运行流程

    [gevent源码分析] 深度分析gevent运行流程 http://blog.csdn.net/yueguanghaidao/article/details/24281751 一直对gevent运行 ...

  4. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  5. AndroidService 深度分析(2)

    AndroidService 深度分析(2) 上一篇文章我们Service的生命周期进行了測试及总结. 这篇文章我们介绍下绑定执行的Service的实现. 绑定执行的Service可能是仅为本应用提供 ...

  6. 深度分析如何在Hadoop中控制Map的数量

    深度分析如何在Hadoop中控制Map的数量 guibin.beijing@gmail.com 很多文档中描述,Mapper的数量在默认情况下不可直接控制干预,因为Mapper的数量由输入的大小和个数 ...

  7. MapReduce深度分析(二)

    MapReduce深度分析(二) 五.JobTracker分析 JobTracker是hadoop的重要的后台守护进程之一,主要的功能是管理任务调度.管理TaskTracker.监控作业执行.运行作业 ...

  8. MapReduce深度分析(一)

    MapReduce深度分析(一) 一.数据流向分析 图为MapReduce数据流向示意图 步骤1.输入文件从HDFS流向到Mapper节点.在一般情况下,存储数据的节点就是Mapper运行的节点,不需 ...

  9. 【JVM】深度分析Java的ClassLoader机制(源码级别)

    原文:深度分析Java的ClassLoader机制(源码级别) 为了更好的理解类的加载机制,我们来深入研究一下ClassLoader和他的loadClass()方法. 源码分析 public abst ...

随机推荐

  1. HTTP响应状态码含义参考

    1xx:信息 100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求.101 Switching Protocols服务器转换协议:服务器将遵从客 ...

  2. 「mysql优化专题」90%程序员面试都用得上的索引优化手册(5)

    目录(技术文) 多关于索引,分为以下几点来讲解: 一.索引的概述(什么是索引,索引的优缺点) 二.索引的基本使用(创建索引) 三.索引的基本原理(面试重点) 四.索引的数据结构(B树,hash) 五. ...

  3. web.xml文件--编码注意事项

    写在前面: 最近发布项目的时候,要修改web.xml文件的内容,然后我在本机的web.xml文件中是有注释的,但是到了服务器上面,就说编码不同.我也没有怎么注意.就继续启动服务器,但是访问网站,一直报 ...

  4. JavaScript数字例子,二分法,冒泡排序

    先看一下两个例子: 十个成绩,求总分,最高分,最低分 //输入10个成绩,求总分,最高,最低 var arr=new Array(67,45,56,12,90,98,23,43,56,99,97); ...

  5. ES6之Set方法与Map方法

    ES6提供了新的数据结构--Set与Map,Set本身是一个构造函数且成员的值是唯一的,没有重复的值!!!Set()是一个存储已排序的无重复元素的数据而Map()是一对数据Map()使用关键值Key来 ...

  6. .NET开发一个微信跳一跳辅助程序

    昨天微信更新了,出现了一个小游戏"跳一跳",玩了一下 赶紧还蛮有意思的 但纯粹是拼手感的,玩了好久,终于搞了个135分拿了个第一名,没想到过一会就被朋友刷下去了,最高的也就200来 ...

  7. 【ASP.NET系列】详解Views

    描述 本片文章内容属于ASP.NET MVC系列视图篇,主要讲解View,大致内容如下: 1.Views文件夹讲解 2.View种类 3.Razor语法 4.对视图的基本操作 一   Views文件夹 ...

  8. Who's in the Middle

    FJ is surveying his herd to find the most average cow. He wants to know how much milk this 'median' ...

  9. 51nod 1203 jzplcm

    长度为N的正整数序列S,有Q次询问,每次询问一段区间内所有数的lcm(即最小公倍数).由于答案可能很大,输出答案Mod 10^9 + 7.   例如:2 3 4 5,询问[1,3]区间的最小公倍数为2 ...

  10. 闲来无事做了一个批处理的win10账号管理

    @echo off %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe&q ...