Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SQL Anywhere 12.0.0 (中文) » SQL Anywhere 服务器 - SQL 的用法 » 使用事务和隔离级别 » 隔离级别教程

 

教程:非可重复读取

教程:脏读中的示例演示了第一类不一致(即脏读)。在该示例中,会计对零售额进行了计算,而销售经理正在更新一个价格。会计的计算使用了销售经理已经输入但正在进行改正的错误信息。

以下示例演示了另一类不一致:非可重复读取。在本示例中,假设两个相同的雇员角色,且他们都并发使用 SQL Anywhere 示例数据库。销售经理想要给塑料太阳帽定一个新的销售价格。会计想要核实某些出现在最近一份订单上的商品的价格。

本例开始时两个连接都处于隔离级别 1,而不是隔离级别 0(随 SQL Anywhere 提供的 SQL Anywhere 示例数据库的缺省隔离级别)。将隔离级别设置为 1 可以消除上一教程中演示的不一致类型(即脏读)。

注意

要按照此教程正常工作,则一定不要选择 Interactive SQL 中的 [自动释放数据库锁] 选项([工具] » [选项] » [SQL Anywhere])。

  1. 启动 Interactive SQL。

  2. 在 [连接] 窗口中,以销售经理的身份连接到 SQL Anywhere 示例数据库:

    1. 从 [操作] 下拉列表中选择 [使用 ODBC 数据源连接]。

    2. 在 [ODBC 数据源名称] 字段中,选择 [SQL Anywhere 12 Demo]。

    3. 如有必要,可单击 [高级] 以显示 [高级选项] 选项卡。

    4. 单击 [高级选项] 选项卡,在 [ConnectionName] 字段中键入 Sales Manager

    5. 单击 [连接]。

  3. 再启动一个 Interactive SQL 的实例。

  4. 在 [连接] 窗口中,以会计的身份连接到 SQL Anywhere 示例数据库:

    1. 从 [操作] 下拉列表中选择 [使用 ODBC 数据源连接]。

    2. 在 [ODBC 数据源名称] 字段中,选择 [SQL Anywhere 12 Demo]。

    3. 如有必要,可单击 [高级] 以显示 [高级选项] 选项卡。

    4. 单击 [高级选项] 选项卡,在 [ConnectionName] 字段中键入 Accountant

    5. 单击 [连接]。

  5. 执行以下命令,将会计的连接的隔离级别设置为 1。

    SET TEMPORARY OPTION isolation_level = 1;
  6. 在销售经理的窗口中执行以下命令,将隔离级别设置为 1:

    SET TEMPORARY OPTION isolation_level = 1;
  7. 会计决定列出太阳帽的价格。作为会计,执行以下命令:

    SELECT ID, Name, UnitPrice FROM Products;
    ID Name UnitPrice
    300 Tee Shirt 9.00
    301 Tee Shirt 14.00
    302 Tee Shirt 14.00
    400 Baseball Cap 9.00
    401 Baseball Cap 10.00
    500 Visor 7.00
    501 Visor 7.00
    ... ... ...
  8. 销售经理决定为塑料太阳帽定一个新的销售价格。作为销售经理,执行以下命令:

    SELECT ID, Name, UnitPrice FROM Products
    WHERE Name = 'Visor';
    UPDATE Products
    SET UnitPrice = 5.95 WHERE ID = 501;
    COMMIT;
    SELECT ID, Name, UnitPrice FROM Products
    WHERE Name = 'Visor';
    ID Name UnitPrice
    500 Visor 7.00
    501 Visor 5.95
  9. 比较 [Sales Manager] 窗口中太阳帽的价格和 [Accountant] 窗口中相同太阳帽的价格。会计再次执行 SELECT 语句,并将看到销售经理的新销售价格。

    SELECT ID, Name, UnitPrice
    FROM Products;
    ID Name UnitPrice
    300 Tee Shirt 9.00
    301 Tee Shirt 14.00
    302 Tee Shirt 14.00
    400 Baseball Cap 9.00
    401 Baseball Cap 10.00
    500 Visor 7.00
    501 Visor 5.95
    ... ... ...

    这种不一致被称作非可重复读取,因为如果会计在同一事务 中再次执行相同的 SELECT 命令,则不会得到相同的结果。

    当然,如果会计已经完成了他的事务,例如,在再次使用 SELECT 之前发出了 COMMIT 或 ROLLBACK 命令,情况则有所不同。该数据库可供多个用户同时使用,而且完全允许某人在会计执行事务之前或之后更改值。结果中的变化仅仅因为发生在执行其事务的过程中才不一致。这种情况将导致调度不可序列化。

  10. 会计注意到了这种情况,并决定从现在开始不希望看到价格发生更改。隔离级别 2 消除了非可重复读取。作为会计,执行以下语句:

    SET TEMPORARY OPTION isolation_level = 2;
    SELECT ID, Name, UnitPrice
    FROM Products;
  11. 销售经理决定最好将塑料太阳帽的销售推迟到下个星期,这样,她就不必为预计明天会收到的一个大订单报那个较低的价格。在她的窗口中,尝试执行以下语句。该命令将开始执行,然后她的窗口将呈现冻结状态。

    UPDATE Products
    SET UnitPrice = 7.00
    WHERE ID = 501;

    数据库服务器在隔离级别 2 上必须确保可重复读取。由于会计正在使用隔离级别 2,因此数据库服务器在会计读取的 Products 表的每一行上都放置一个读锁定。当销售经理尝试将价格更改回原来的值时,她的事务必须在 Products 表中包含塑料太阳帽的那一行上获取一个写锁定。由于写锁定是独占的,因此她的事务必须等到会计的事务释放其读锁定后才能继续执行。

  12. 会计查看完价格后,由于他不希望无意中更改了数据库,因此使用 ROLLBACK 语句完成他的事务。

    ROLLBACK;

    数据库服务器执行该语句后,销售经理的事务完成。

    ID Name UnitPrice
    500 Visor 7.00
    501 Visor 7.00
  13. 销售经理现在就可以完成她的工作了。她希望提交她所做的更改,恢复到原来的价格。

    COMMIT;
 锁的类型和不同的隔离级别
 事务阻塞
 使用快照隔离避免非可重复的读取