------------数据库的加锁操作(上)

从事一个项目,需要考虑数据的安全性,之前对于数据库这部分的数据操作学习的比较零散,由于手头的项目,于是系统的

学习了下数据库操作加锁的知识:

--------------------------------------------------华丽丽的分割线-----------------------------------------------------------

 学习一个知识,我们大致都会经历这么几个过程(what this ? why  to use ?  how to use?),首先,我们需要搞懂,下面几个知识点:

   一:  什么是数据库加锁 ?

             数据库加锁: 简单的意思就是对于在执行一个操作(比如修改)时,对这个操作的对象加锁,放置其他操作读取到脏数据或者幽灵数据。

    或者术语来说就是一种排他锁,当写的时候不允许其他程序写,这样就可以保证数据一致性了

 二:为什么要给数据加锁?

      对于这点,我们需要简单的了解几个概念:

 (1).什么是事务?

       事务: 是用户定义的数据库操作系列,这些操作作为一个完整的工作单元执行。一个事务内的所有语句作为一个整体。要么全部执行,要么全部不执行。

      事务的几个特点:  原子性,一致性,隔离性,持久性, 简称ACID特征

      一般来讲:  事务的这几个特点被遭到破坏的有下列几个情况:

     (1) 多事务并行运行时,不同事务的操作有交叉情况。(->_->  如果你想不到列子:就想想多线程问题)

     (2) 事务在运行过程中被强迫停止。

(2)什么是脏读:

     脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

(3)什么是不可重复读?

    不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

(4)什么是幻读?

      幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

 因为在上述的情况下,数据会出现脏数据。对于一个考虑安全性的系统而言,加锁自然是十分必要. 

 (三)如何对数据加锁:

      对于数据加锁: 一般分为如下两种,第一类,就是数据库自己加锁,第二类,就是线程锁。

     第一种:  数据库自己加锁

         对于锁的级别: 库级锁,表级锁,页级锁,行级锁。(这篇文章提供了较多的说明)

          http://blog.csdn.net/cspyb/article/details/1865538

     举几个列子:对于数据表的加锁:

     方法一: 使用SQL语句进行加锁

public void test() {  

    String sql = "lock tables Gxjun write";
    // 或String sql = "lock tables Gxjun read";
    // 如果想锁多个表 lock tables Gxjun read stu write , .....
    String sql1 = "select * from Gxjun ";  

    String sql2 = "unlock tables";
    try {
        this.pstmt = conn.prepareStatement(sql);
        this.pstmt1 = conn.prepareStatement(sql1);
        this.pstmt2 = conn.prepareStatement(sql2);
        pstmt.executeQuery();
        pstmt1.executeQuery();
        pstmt2.executeQuery();  

    } catch (Exception e) {
        System.out.println("异常" + e.getMessage());
    }  

}  

  方法二 , 采用记录锁加锁:

  public void test() {  

    String sql = "select * from Gxjun for update";   

    try {
        conn.setAutoCommit(false);
        this.pstmt = conn.prepareStatement(sql);
        pstmt.executeQuery();  

    } catch (Exception e) {
        System.out.println("异常" + e.getMessage());
    }  

}  

需要标注的几点就是:(摘录自思绪飞帆,鸣谢----思绪飞帆)

/*
  1.for update 与 lock in share mode 属于行级锁和页级锁

                2.for update 排它锁,lock in share mode 共享锁

                3.对于记录锁.必须开启事务.

                4.行级锁定事实上是索引记录的锁定.只要是用索引扫描的行(或没索引全表扫描的行),都将被锁住.

                5.在不同的隔离级别下还会使用next-key locking算法.即所扫描的行之间的“间隙”也会也锁住(在Repeatable read和Serializable隔离级别下有间隙锁).

               6.在mysql中共享锁的含义是:在被共享锁锁住的行,即使内容被修改且并没有提交.在另一个会话中依然看到最新修改的信息.

   在同一会话中加上了共享锁.可以对这个表以及这个表以外的所有表进行增、删、改、查的操作.

   在不同的会话中.可以查到共享锁锁住行的最新消息.但是在Read Uncommitted隔离级别下不能对锁住的表进行删,

    改操作.(需要等待锁释放才能操作...)
                     在Read Committed隔离级别下不能对锁住的表进行删,改操作.(需要等待锁释放才能操作...)
                     在Repeatable read隔离级别下不能对锁住行进行增、删、改操作.(需要等待锁释放才能操作...)
                     在Serializable隔离级别下不能对锁住行进行增、删、改操作.  (需要等待锁释放才能操作...)

7.在mysql中排他锁的含义是:在被排它锁锁住的行,内容修改并没提交,在另一个会话中不会看到最新修改的信息。

   在不同的会话中.可以查到共享锁锁住行的最新消息.但是Read Uncommitted隔离级别下不能对锁住的表进行删,

    改操作.(需要等待锁释放才能操作...)
                     在Read Committed隔离级别下不能对锁住的表进行删,改操作.(需要等待锁释放才能操作...)
                     在Repeatable read隔离级别下不能对锁住行进行增、删、改操作.(需要等待锁释放才能操作...)
                     在Serializable隔离级别下不能对锁住行进行增、删、改操作. (需要等待锁释放才能操作...)   

8.在同一个会话中的可以叠加多个共享锁和排他锁.在多个会话中,需要等待锁的释放.

9.SQL中的update 与 for update是一样的原理.

10.等待超时的参数设置:innodb_lock_wait_timeout=50 (单位秒).

11.任何可以触发事务提交的命令,都可以关闭共享锁和排它锁.
*/

     第二种: 就是线程锁,这是我重点学习的地方,(额,需呀注解的是,是我重点学习的地方,然后每个人注重点不同)

           引用到的材料: 

           1.  http://lavasoft.blog.51cto.com/62575/99155

           2.http://www.cnblogs.com/hoojo/archive/2011/05/05/2038101.html

           3. http://www.blogjava.net/zhangwei217245/archive/2010/04/08/315526.html

  -------------------------------------------------------------------------------------------------------------------------------------- 鸣谢上述作者

0-----------------------------------------------------------------华丽丽的分割线----------------------------------------------------------------------0

举列子:

在不采用同步锁的情况下

 1 package Day_2;
 2
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 /**
 6  * @author Gxjun
 7  * 功能:同步锁
 8  */
 9 public class Demo_tongbusuo {
10 public static void main(String args []){
11      MyThread r = new MyThread();
12      //ExecutorService  MyThreadPool = Executors.newCachedThreadPool();
13      Thread ta = new Thread(r, "王小二");
14      Thread tb = new Thread(r, "王小三");
15      //加载线程池中去
16     // MyThreadPool.execute(ta);
17      //MyThreadPool.execute(tb);
18      ta.start();
19      tb.start();
20  }
21 }
22
23    class Foo {
24     private int x = 100;
25
26     public int getX() {
27         return x;
28     }
29
30     public int fix(int y) {
31         x = x - y;
32         return x;
33     }
34 }
35
36 class MyThread implements Runnable {
37     private Foo foo = new Foo();
38
39     public void run() {
40         for (int i = 0; i < 3; i++) {
41             this.fix(30);
42             try {
43                 Thread.sleep(1);
44             } catch (InterruptedException e) {
45                 e.printStackTrace();
46             }
47             System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());
48         }
49     }
50
51     public int fix(int y) {
52         return foo.fix(y);
53     }
54 }

结果为:

王小二 : 当前foo对象的x值= 40
     王小三 : 当前foo对象的x值= 40 
     王小二 : 当前foo对象的x值= -20
     王小三 : 当前foo对象的x值= -50
     王小二 : 当前foo对象的x值= -80
     王小三 : 当前foo对象的x值= -80

但是对于上述问题,采用线程池,却能得到完满的解决。

  将其改成同步锁之后:

 1 package Day_2;
 2
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 /**
 6  * @author Gxjun
 7  * 功能:同步锁
 8  */
 9 public class Demo_tongbusuo {
10 public static void main(String args []){
11      MyThread r = new MyThread();
12      //ExecutorService  MyThreadPool = Executors.newCachedThreadPool();
13      Thread ta = new Thread(r, "王小二");
14      Thread tb = new Thread(r, "王小三");
15      //加载线程池中去
16     // MyThreadPool.execute(ta);
17      //MyThreadPool.execute(tb);
18      ta.start();
19      tb.start();
20  }
21 }
22
23    class Foo {
24     private int x = 100;
25
26     public int getX() {
27      //改为同步锁
28      synchronized (this) {
29         return x;
30     }
31     }
32
33     public int fix(int y) {
34         x = x - y;
35         return x;
36     }
37 }
38
39 class MyThread implements Runnable {
40     private Foo foo = new Foo();
41
42     public void run() {
43         for (int i = 0; i < 3; i++) {
44             this.fix(30);
45             try {
46                 Thread.sleep(1);
47             } catch (InterruptedException e) {
48                 e.printStackTrace();
49             }
50             System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());
51         }
52     }
53
54     public int fix(int y) {
55         return foo.fix(y);
56     }
57 }

结果:

王小二 : 当前foo对象的x值= 40
王小三 : 当前foo对象的x值= 40
王小三 : 当前foo对象的x值= -20
王小二 : 当前foo对象的x值= -20
王小三 : 当前foo对象的x值= -80
王小二 : 当前foo对象的x值= -80

     这个结果和采用线程池得到结果是一样的........

采用非同步锁结果也是一样的.....

 1 package Day_2;
 2
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 /**
 6  * @author Gxjun
 7  * 功能:同步锁
 8  */
 9 public class Demo_tongbusuo {
10 public static void main(String args []){
11      MyThread r = new MyThread();
12      //ExecutorService  MyThreadPool = Executors.newCachedThreadPool();
13      Thread ta = new Thread(r, "王小二");
14      Thread tb = new Thread(r, "王小三");
15      //加载线程池中去
16     // MyThreadPool.execute(ta);
17      //MyThreadPool.execute(tb);
18      ta.start();
19      tb.start();
20  }
21 }
22
23    class Foo {
24     private int x = 100;
25
26     public synchronized int   getX() {
27         return  x;
28     }
29
30     public int fix(int y) {
31         x = x - y;
32         return x;
33     }
34 }
35
36 class MyThread implements Runnable {
37     private Foo foo = new Foo();
38
39     public void run() {
40         for (int i = 0; i < 3; i++) {
41             this.fix(30);
42             try {
43                 Thread.sleep(1);
44             } catch (InterruptedException e) {
45                 e.printStackTrace();
46             }
47             System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());
48         }
49     }
50
51     public int fix(int y) {
52         return foo.fix(y);
53     }
54 }

时间: 2024-10-15 09:13:18

------------数据库的加锁操作(上)的相关文章

linux系统上Mysql数据库导入导出操作

需求:把MySQL数据库目录中的dz数据库备份到/home/dz_bak.sql ,然后再新建一个数据库dzbak,最后把/home/dz_bak.sql 导入到数据库dzbak中.操作如下:以下操作均在终端命令行下进行 1.mysqldump -u root -p dz > /home/dz_bak.sql        #导出数据库     123456     #输入数据库密码     扩展:     mysqldump -u root -p dz pre_portal_comment >

【转载】数据库大并发操作要考虑死锁和锁的性能问题

本文转载自:http://blog.csdn.net/yuanyuanispeak/article/details/52756167 1 前言 数据库大并发操作要考虑死锁和锁的性能问题.看到网上大多语焉不详(尤其更新锁),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一个线程.T3,T4以此类推.下面以SQL Server(2005)为例. 2 锁的种类 共享锁(Shared lock). 例1: -------

数据库的并发操作

数据库的并发操作 事务 事务(Transaction)是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位. 事务是恢复和并发控制的基本单位 事务的ACID特性: 原子性(Atomicity):事务是数据库的逻辑工作单位 一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变 到另一个一致性状态 隔离性(Isolation):一个事务的执行不能被其他事务干扰 持续性(Durability ):一个事务一旦提交,它对数据库中数据的改变就应

mysql数据库和mongodb数据库的相关操作以及两个数据库的区别

在docs命令中执行数据操作 MySQL数据库 先启动MySQL服务器  net start mysql 进入MySQL服务器MySQL -uroot -p(这里写你的数据库密码) (-P是从哪个端口进) 我第一次操作是就是因为电脑上 有 MySQL  MySQL57 MySQLla 三个服务 引起端口冲突 导致 我 找不到相应的数据 数据库操作 create database   创建数据库 show databases   显示MySQL服务上的数据库 use  数据库          使

MySQL数据库(3)_MySQL数据库表记录操作语句

附: MYSQL5.7版本sql_mode=only_full_group_by问题 1.查询当前sql_mode: select @@sql_mode 2.查询出来的值为: set @@sql_mode ='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; 3.修改s

CS数据库的相关操作

备注:我们对于CS数据库的操作多数情况下只是查询操作,尽量不要在数据库中直接修改记录的值 第一.首先备份数据库 1.进入mysql数据库 [[email protected] ~]# mysql -u root -p Enter password: Welcome to the MySQL monitor.  Commands end with ; or \g. Your MySQL connection id is 188 Server version: 5.1.61-log Source d

前端学数据库之基础操作

前面的话 SQL是一门用于访问和处理数据库的ANSI的标准计算机语言.但是,存在着很多不同版本的SQL语言,为了与ANSI(美国国家标准学会)标准相兼容,它们必须以相似的方式共同地来支持一些主要的关键词(如 SELECT.UPDATE.DELETE.INSERT.WHERE等) RDBMS指的是关系型数据库管理系统,RDBMS是SQL的基础,同样也是所有现代数据库系统的基础,比如Oracle.MySQL和Access.RDBMS中的数据存储在被称为表(tables)的数据库对象中.表是相关的数据

怎样加快master数据库的写操作?分表原则!将表水平划分!

1.怎样加快master数据库的写操作?分表原则!将表水平划分!减少表的锁定时间!!! 2.既然分表了,就一定要注意分表的规则!要在代码层,代码层知道将数据insert/select到哪张表上. 3.要在代码层知道将数据操作到哪张表上!分表规则, 所以分表规则要用代码设计出来,与数据库中的分表规则对应!!!一样!!!一样的分表规则!! 4.那么按什么规则分表呢?有不同的策略,常见的有按照01234556789,账号尾数0对应0表,1对应1表. 还可以根据业务来分表.如按照用户或者商品. 具体分表

(05)odoo数据库和业务操作

以一个例子开头* To-do 向导   # 配置文件       __openerp_.py:         { 'name': 'To-do Tasks Management Assistant',    'description': 'Mass edit your To-Do backlog.',    'author': 'Daniel Reis',    'depends': ['todo_user'],    'data': ['todo_wizard_view.xml'], }