精华内容
下载资源
问答
  • 悲观锁乐观锁是人们定义出来的概念,你可以理解为一种思想,是处理并发资源的常用手段。 不要把他们与mysql中提供的锁机制(表锁,行锁,排他锁,共享锁)混为一谈。 一、悲观锁 顾名思义,就是对于数据的处理持悲观...
  • MySql悲观锁乐观锁的使用

    万次阅读 多人点赞 2018-05-25 13:37:13
    我们分别在无锁和乐观锁悲观锁进行相应的代码演示来说明问题。 建表语句如下:CREATE TABLE `stock` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT...

        现在我有一个购买商品的需求,我们知道当我们购买商品时,后台会进行减库存和增加购买记录的操作。我们分别在无锁和乐观锁和悲观锁进行相应的代码演示来说明问题。

        建表语句如下:

    CREATE TABLE `stock` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
      `count` int(11) NOT NULL COMMENT '库存',
      `sale` int(11) NOT NULL COMMENT '已售',
      `version` int(11) NOT NULL COMMENT '乐观锁,版本号',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
    CREATE TABLE `stock_order` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `sid` int(11) NOT NULL COMMENT '库存ID',
      `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=981 DEFAULT CHARSET=utf8

    一、无锁的Mysql:

        先看代码:

    public class Test2 {
        public static void main(String[] args) throws SQLException, InterruptedException {
            Test2 tests = new Test2();
            Thread[] threads = new Thread[100];
            for (int i=0;i<100;i++){
                threads[i] = new Thread(){
                    @Override
                    public void run() {
                        try {
                            tests.service();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                };
            }
            for (int i=0;i<100;i++){
                threads[i].start();
            }
        }
        public void service() throws Exception {
            Connection connection = dbUtils.getConnection();
            String selectSql = "select count from stock where id = 1";
            PreparedStatement statement1 = connection.prepareStatement(selectSql);
            ResultSet resultSet = statement1.executeQuery();
            resultSet.next();
            String count = resultSet.getString("count");
            System.out.println(count);
            int c = Integer.parseInt(count);
            Thread.sleep(10);
            if (c<1)
                throw new Exception();
            String updateSql = "update stock set count = count - 1 where count > 0";
            PreparedStatement preparedStatement = connection.prepareStatement(updateSql);
            int update = preparedStatement.executeUpdate();
            String insertSql = "insert into stock_order(sid,name) VALUES (1,'aaa')";
            PreparedStatement statement = connection.prepareStatement(insertSql);
            int insert = statement.executeUpdate();
        }
    }

        从上述代码可以看到,有一百个线程去模拟一百个用户购买商品,数据库中只有10个商品,所以当商品卖完时,应该增加10条购买记录。为了让大家看个清楚,我在代码中加入了线程的睡眠。

        

        我们看到,增加了11条记录,也就是所谓的超卖现象,商家绝不可能允许这种情况的发生。

    MySql的乐观锁:

        我们在使用乐观锁时会假设在极大多数情况下不会形成冲突,只有在数据提交的时候,才会对数据是否产生冲突进行检验。如果数据产生冲突了,则返回错误信息,进行相应的处理。

        实现:MySql最经常使用的乐观锁时进行版本控制,也就是在数据库表中增加一列,记为version,当我们将数据读出时,将版本号一并读出,当数据进行更新时,会对这个版本号进行加1,当我们提交数据时,会判断数据库表中当前的version列值和当时读出的version是否相同,若相同说明没有进行更新的操作,不然,则取消这次的操作。

    public class Test {
        public static void main(String[] args) {
            Test test = new Test();
            Thread[] threads = new Thread[200];
            for (int i=0;i<200;i++){
                int finalI = i;
                threads[i] = new Thread(){
                    @Override
                    public void run() {
                       test.service();
                    }
                };
            }
            for (int i=0;i<200;i++){
                threads[i].start();
            }
        }
        public void service(){
            try {
                Connection connection = dbUtils.getConnection();
                Stock stock1 = checkStock(connection);
                updateCountByOpti(connection,stock1);
                createOrder(connection);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    
        private void createOrder(Connection connection) throws SQLException {
            String insertSql = "insert into stock_order(sid,name) VALUES (1,'aaa')";
            PreparedStatement statement = connection.prepareStatement(insertSql);
            int insert = statement.executeUpdate();
        }
        private void updateCountByOpti(Connection connection,Stock stock) throws SQLException {
            String sql = "update stock set count = count -1,version = version + 1 where version = " + stock.getVersion();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            int update = preparedStatement.executeUpdate();
            if (update==0)
                throw new RuntimeException("没抢到");
        }
        public Stock checkStock(Connection connection) throws SQLException {
            String sql = "select * from stock where id = 1";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet resultSet = preparedStatement.executeQuery();
            Stock stock = null;
            if (resultSet.next()){
                stock = new Stock();
                stock.setId(resultSet.getInt("id"));
                stock.setName(resultSet.getString("name"));
                stock.setCount(resultSet.getInt("count"));
                stock.setSale(resultSet.getInt("sale"));
                stock.setVersion(resultSet.getInt("version"));
            }
            if (stock.getCount()<1)
                throw new RuntimeException("没有库存了");
            return stock;
        }
    }
    

        上述在提交时,对version字段进行了比较,当数据库中的version和之前读取的version一样才会进行提交,否则提交失败,接下来进行测试。

                

        可以看到,只有10条记录,乐观锁保证了数据的一致性。

    三、悲观锁

        MySql的悲观锁就是打开事务,当启动事务时,如果事务中的sql语句涉及到索引并用索引进行了条件判断,那么会使用行级锁锁定所要修改的行,否则使用表锁锁住整张表。

    public class Test {
        public static void main(String[] args) {
            Test test = new Test();
            Thread[] threads = new Thread[200];
            for (int i=0;i<200;i++){
                threads[i] = new Thread(){
                    @Override
                    public void run() {
                        try {
                            test.service();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                };
            }
            for (int i=0;i<200;i++){
                threads[i].start();
            }
        }
            public void service() throws SQLException {
                Connection connection = null;
                try {
                    connection = dbUtils.getConnection();
                    connection.setAutoCommit(false);
                    Stock stock1 = checkStock(connection);
                    updateCountByOpti(connection,stock1);
                    createOrder(connection);
                    connection.commit();
                }catch (Exception e){
                    System.out.println(e.getMessage());
                    connection.rollback();
                }
            }
    
            private void createOrder(Connection connection) throws SQLException {
                String insertSql = "insert into stock_order(sid,name) VALUES (1,'aaa')";
                PreparedStatement statement = connection.prepareStatement(insertSql);
                int insert = statement.executeUpdate();
            }
            private void updateCountByOpti(Connection connection,Stock stock) throws SQLException {
                String sql = "update stock set count = count -1,version = version + 1 where version = " + stock.getVersion();
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
                int update = preparedStatement.executeUpdate();
                if (update==0)
                    throw new RuntimeException("没抢到");
            }
    
            public Stock checkStock(Connection connection) throws SQLException, InterruptedException {
                String sql = "select * from stock where id = 1";
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
                ResultSet resultSet = preparedStatement.executeQuery();
                Stock stock = null;
                if (resultSet.next()){
                    stock = new Stock();
                    stock.setId(resultSet.getInt("id"));
                    stock.setName(resultSet.getString("name"));
                    stock.setCount(resultSet.getInt("count"));
                    stock.setSale(resultSet.getInt("sale"));
                    stock.setVersion(resultSet.getInt("version"));
                }
                if (stock.getCount()<1)
                    throw new RuntimeException("没有库存了");
                return stock;
            }
    }

        开启事务并不难,所以使用悲观锁很简单,让我们看一下结果


        结果还是10条记录

    我们可以在不同的场合使用不同的处理方法,乐观锁并发高并且性能也很好,而悲观锁虽然并发不是很高,但是它不允许脏读,所以各有各的优点。

    展开全文
  • 悲观锁,假设丢失更新一定存在;sql后面加上**for update;**这是数据库的一种机制。 2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。 一、乐观锁 乐观锁 不是数据库自带 的,...

    mysql的并发操作时而引起的数据的不一致性(数据冲突):

    丢失更新:两个用户(或以上)对同一个数据对象操作引起的数据丢失。

    解决方案:1.悲观锁,假设丢失更新一定存在;sql后面加上**for update;**这是数据库的一种机制。

    2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。

    一、乐观锁

    乐观锁 不是数据库自带 的,需要我们自己去实现。 乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

    通常实现是这样的:在表中的数据进行操作时(更新), 先给数据表加一个版本(version)字段 ,每操作一次,将那条记录的版本号加1。也就是 先查询出那条记录 ,获取 出version字段 ,如果要对那条记录进行操作(更新),则 先判断此刻version的值是否与刚刚查询出来时的version的值相等 ,如果相等,则说明这段期间,没有其他程序对其进行操作, 则可以执行更新,将version字段的值加1 ;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

    下单操作包括3步骤:

    1.查询出商品信息

    select (status,version) from t_goods where id=#{id}

    2.根据商品信息生成订单

    3.修改商品status为2

    update t_goods 
    set status=2,version=version+1
    where id=#{id} and version=#{version};
    
    二、悲观锁

    与乐观锁相对应的就是悲观锁了。 悲观锁就是在 操作数据时,认为此操作会出现数据冲突 ,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作 , 这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

    说到这里,由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

    共享锁

    共享锁指的就是**对于多个不同的事务,对同一个资源共享同一个锁。**相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,进去啪啪啪啥的,一下理解了哈,没错,这个就是所谓的共享锁。

    共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。通过查询资料,了解到通过在执行语句后面加上 lock in share mode 就代表对某些资源加上共享锁了。

    比如,我这里通过mysql打开两个查询编辑器,在其中开启一个事务, 并不执行commit语句

    city表DDL如下:

    CREATE TABLE `city` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) DEFAULT NULL,
      `state` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
    
    img
    begin;
    SELECT * from city where id = "1"  lock in share mode;(不提交,commit)
    

    然后在另一个查询窗口中,对id为1的数据进行更新

    img

    update city set name=“666” where id =“1”; (未commit)

    执行数秒,报错

    此时,操作界面进入了卡顿状态,过几秒后,也提示错误信息

    [SQL]update  city set name="666" where id ="1";
    [Err] 1205 - Lock wait timeout exceeded; try restarting transaction
    

    那么证明,对于id=1的记录加锁成功了,在 上一条记录还没有commit之前 ,这条id=1的记录被锁住了,只有在上一个事务释放掉锁后才能进行操作,或用共享锁才能对此数据进行操作。

    再实验一下:

    img
    update city set name="666" where id ="1" lock in share mode; 
    [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1
    

    加上共享锁后 ,也提示错误信息了,通过查询资料才知道,对于update,insert,delete语句会自动加排它锁的原因

    于是,我又试了试SELECT * from city where id = “1” lock in share mode;(没问题)

    排它锁

    排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。

    与共享锁类型,在需要执行的语句后面加上for update就可以了

    (对于 Innodb引擎 语句后面加上for update表示 把此行数据锁定 MyISAM则是锁定整个表 。)

    InnoDB中的select … for update语句:

    1)select .. for update语句仅适用于InnoDB
    2)select .. for update语句必须在事务中才能生效。
    3)在执行事务中的select .. for update语句时,MySQL会对查询结果集中的每行数据都添加排他锁(行锁、表锁),其它线程对锁定行的 更新、删除、select .. for update查询 这3种操作都会被阻塞,一般的select语句不会被阻塞。
    4)查看自动提交是否开启(1表示开启,0表示关闭,默认开启): select @@autocommit
    5)InnoDB行级锁的实现:InnoDB的行级锁是通过在索引上加锁来实现的,所以只有通过明确的索引来查找数据时才会使用行级锁。
    

    MySQL InnoDB为例

    商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。

    1如果不采用锁,那么操作方法如下:

    //1.查询出商品信息
    select status from t_goods where id=1;
    //2.根据商品信息生成订单
    insert into t_orders (id,goods_id) values (null,1);
    //3.修改商品status为2
    update t_goods set status=2;
    

    上面这种场景在高并发访问的情况下很可能会出现问题。

    前面已经提到,只有当goods status为1时才能对该商品下单,上面第一步操作中,查询出来的商品status为1。但是当我们执行第三步Update操作的时候, 有可能出现其他人先一步对商品下单把goods status修改为2了 ,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致。所以说这种方式是不安全的。

    2使用悲观锁来实现:

    在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。

    注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

    我们可以使用命令设置MySQL为非autocommit模式:

    set autocommit=0;

    设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

    //0.开始事务
    begin;/begin work;/start transaction; (三者选一就可以)
    //1.查询出商品信息
    select status from t_goods where id=1 for update;
    //2.根据商品信息生成订单
    insert into t_orders (id,goods_id) values (null,1);
    //3.修改商品status为2
    update t_goods set status=2;
    //4.提交事务
    commit;/commit work;
    

    :上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。

    上面的第一步我们执行了一次查询操作:

    select status from t_goods where id=1 for update; 
    

    与普通查询不一样的是,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改

    :需要注意的是,在事务中,只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一笔数据时会等待其它事务结束后才执行,一般SELECT … 则不受此影响 。拿上面的实例来说,当我执行select status from t_goods where id=1 for update;后。我在另外的事务中如果再次执行select status from t_goods where id=1 for update;则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态,但是如果我是在第二个事务中执行select status from t_goods where id=1;则能正常查询出数据,不会受第一个事务的影响。

    补充:MySQL select…for update的Row Lock与Table Lock

    MyISAM只支持表锁。

    上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

    例:

    假設有個表單 products ,裡面有 id 跟 name 二個欄位,id 是主鍵。

    例1: (明確指定主鍵,並且有此筆資料,row lock)

    SELECT * FROM products WHERE id='3' FOR UPDATE;
    

    例2: (明確指定主鍵,若查無此筆資料,無 lock)

    SELECT * FROM products WHERE id='-1' FOR UPDATE;
    

    例3: (無主鍵,table lock)

    SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
    

    例4: (主鍵不明確,table lock)

    SELECT * FROM products WHERE id<>'3' FOR UPDATE;  !=与<> ,意思一样
    

    例5: (主鍵不明確,table lock)

    SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;
    

    另外的:

    排它锁的选择:
    	若where条件中明确指定了主键,且该行数据存在,则只锁定该行,故排它锁为行锁(row lock)。
    	若where条件中明确指定了主键,但是该行数据不存在,则不会加锁。
    	
    	若where条件中明确指定了索引,且该行数据存在,则只锁定该行,故排它锁为行锁(row lock)。
    	若where条件中明确指定了索引,但是该行数据不存在,则不会加锁。
    	
    	若where条件中未明确指定主键或索引,则会锁定全表,故排它锁为表锁(table lock)。
    	注:未明确指定 即 未指定(主键/索引) 或 指定的是(主键/索引)的范围
    
    eg:
    	# 只锁定message_id为1的行
    	set autocommit=0;
    	begin;
    	select * from t_message where message_id=1 for update; # message_id为主键
    	commit;
    
    	# 锁定全表
    	set autocommit=0;
    	begin;
    	select * from t_message where message_id>1 for update; # message_id为主键
    	commit;
    	
    	# 锁定全表
    	set autocommit=0;
    	begin;
    	select * from t_message where type='good' for update; # good非索引列
    	commit;
    
    	其它线程因为等待(排它锁)超时而报错:
    	update t_message set title='asdf' where message_id=1;
    	[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
    
    释放锁

    1、暴力解决方式

    重启MYSQL(重启解决问题利器,手动滑稽)

    2、查看表情况:

     show processlist; 
    

    State状态为Locked即被其他查询锁住

    3、kill掉锁表的进程ID

    KILL  10866;//后面的数字即时进程的ID`
    

    寄语:在这里插入图片描述
    谢谢支持!在这里插入图片描述

    展开全文
  • mysql悲观锁乐观锁

    万次阅读 多人点赞 2018-09-04 12:16:52
    悲观锁,假设丢失更新一定存在;sql后面加上for update;这是数据库的一种机制。  2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。 一、乐观锁 乐观锁不是数据库自带的,...

    mysql的并发操作时而引起的数据的不一致性(数据冲突):

    丢失更新:两个用户(或以上)对同一个数据对象操作引起的数据丢失。

        解决方案:1.悲观锁,假设丢失更新一定存在;sql后面加上for update;这是数据库的一种机制。

             2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。

    一、乐观锁

    乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

    通常实现是这样的:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

    eg:

    下单操作包括3步骤:

    1.查询出商品信息

    select (status,status,version) from t_goods where id=#{id}

    2.根据商品信息生成订单

    3.修改商品status为2

    update t_goods 

    set status=2,version=version+1

    where id=#{id} and version=#{version};

     

    除了自己手动实现乐观锁之外,现在网上许多框架已经封装好了乐观锁的实现,如hibernate,需要时,可能自行搜索"hiberate 乐观锁"试试看。

    二、悲观锁

    与乐观锁相对应的就是悲观锁了。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

    说到这里,由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

    共享锁

    共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,进去啪啪啪啥的,一下理解了哈,没错,这个就是所谓的共享锁。

    刚刚说了,对于悲观锁,一般数据库已经实现了,共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。通过查询资料,了解到通过在执行语句后面加上lock in share mode就代表对某些资源加上共享锁了。

    比如,我这里通过mysql打开两个查询编辑器,在其中开启一个事务,并不执行commit语句

    city表DDL如下:

    CREATE TABLE `city` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) DEFAULT NULL,
      `state` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
    

    begin;
    SELECT * from city where id = "1"  lock in share mode;

    然后在另一个查询窗口中,对id为1的数据进行更新

    update  city set name="666" where id ="1";

    此时,操作界面进入了卡顿状态,过几秒后,也提示错误信息

    [SQL]update  city set name="666" where id ="1";
    [Err] 1205 - Lock wait timeout exceeded; try restarting transaction

     

    那么证明,对于id=1的记录加锁成功了,在上一条记录还没有commit之前,这条id=1的记录被锁住了,只有在上一个事务释放掉锁后才能进行操作,或用共享锁才能对此数据进行操作。

    再实验一下:

    update city set name="666" where id ="1" lock in share mode;

    [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1

     

    加上共享锁后,也提示错误信息了,通过查询资料才知道,对于update,insert,delete语句会自动加排它锁的原因

    于是,我又试了试SELECT * from city where id = "1" lock in share mode;

    这下成功了。

    排它锁

    排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。

    与共享锁类型,在需要执行的语句后面加上for update就可以了(对于Innodb引擎语句后面加上for update表示把此行数据锁定,MyISAM则是锁定整个表。)

    行锁

    行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

    比如之前演示的共享锁语句

    SELECT * from city where id = "1"  lock in share mode; 

    由于对于city表中,id字段为主键,就也相当于索引。执行加锁时,会将id这个索引为1的记录加上锁,那么这个锁就是行锁。

     

    表锁

    表锁,和行锁相对应,给这个表加上锁。

     

    MyISAM引擎里有的,暂时研究了

    展开全文
  • 主要介绍了thinkPHP框架乐观锁悲观锁,结合实例形式分析了框架乐观锁悲观锁的原理及thinkPHP相关实现技巧,需要的朋友可以参考下
  • 锁的定义:   数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。 表级锁:开销小,加锁快;...一、悲观锁   顾名思义,就是对于数
  • Mysql悲观锁乐观锁区别与使用场景

    俗话说金三银四,不管是为了升职加薪还是打算换个环境,现在正是广大程序员们的跳槽季,IT行业正暗流涌动腥风血雨,互联网寒冬似乎并没有过去,很多企业依然在裁员,还有部分企业光明正大的实行着"996",这一行可能正面临着最大的竞争压力和生存压力。我们无法改变环境,也无法左右市场,但是我们能改变和左右的是自己的知识深度和广度,只有武装到牙齿,不断的充实自己,才能在面试这场江湖纷争里游刃有余。

    你是不是经常被面试官突如其来的问题问的语无伦次或者毫无底气?进而在一整场面试里都失去了信心。最近有个群员就遇到这个问你,面试官让他自我介绍以后,就突然问他mysql的悲观锁和乐观锁区别,而这正好是他的盲区,第一个问题就被堵住了,这场面试的结果也可想而知。下面就从面试角度来浅谈一下,该怎样回答这个问题。

    一、概念上区别

    乐观锁(** Optimistic Locking**):顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,"乐观"的认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。

    悲观锁(Pessimistic Lock):正如其名字一样,悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

    二、实现方式:

    乐观锁:

    version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
    sql实现代码:
    update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

    CAS(定义见后)操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。(这种方式作者也是最近刚知道,惭愧惭愧)
    悲观锁:是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了(原理:共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程),如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。

    三、使用场景

    乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

    悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

    四、特点

    乐观锁:乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

    悲观锁:悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。

    五、sql实现代码和案例

    因为本文主要是讲解的是面试的时候怎样回答,所以就不将具体sql代码和案例贴出来了,感兴趣的同学可以自行去看一下,如果以后有时间,我也会单独写一篇详细的实现案例出来。

    下面是一张网络图,作者觉得画的蛮好的,给大家参考一下

    在这里插入图片描述
    总结:

    以上,基本回答完前四点后,这道题已经能在面试官心里达到八九十分了。但是我们的人生不仅仅是为了面试,希望同学们一定要弄懂其中的原理和区别,悲观锁和乐观锁都有自己的优缺点,简而言之记得一句话:读取频繁使用乐观锁,写入频繁使用悲观锁。乐观锁不能解决脏读的问题。

    如果觉得本文有用,请推荐给更多有需要的人,谢谢!如果发现问题,欢迎留言,请随时批评改正,谢谢!

    展开全文
  • Mysql悲观锁乐观锁区别及使用场景

    千次阅读 2019-04-03 22:32:00
    最近有个群员就遇到这个问你,面试官让他自我介绍以后,就突然问他mysql悲观锁乐观锁区别,而这正好是他的盲区,第一个问题就被堵住了,这场面试的结果也可想而知。下面就从面试角度来浅谈一下,该怎样回答这个...
  • 说是写乐观锁的概念,但是通常乐观锁悲观锁的概念都要一块写。对比着来才更有意义。 1、悲观锁概念 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样...
  • 文章目录一、并发控制二、悲观锁(Pessimistic Lock)三、乐观锁(Optimistic Locking)四、实现方式1、悲观锁实现方式2、乐观锁实现方式五、如何选择 一、并发控制 当程序中可能出现并发的情况时,就需要通过一定的手段...
  • 正确的理解MySQL乐观锁悲观锁与MVCC !首先声明,MySQL的测试环境是5.7 前提概念 数据库并发的三种场景 乐观锁悲观锁的澄清 悲观锁 乐观锁 MVCC多版本并发控制 总结 乐观锁悲观锁的...
  • MySql乐观锁悲观锁 先上图: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8fFZxcim-1587634926298)(https://i.loli.net/2020/04/23/V6p9kBeuo5Z3Ybx.jpg)] 乐观锁乐观锁,...
  • 在关系型数据库中,悲观锁乐观锁是解决资源并发场景的解决方案,接下来将详细讲解:magnifying_glass_tilted_right:一下这两个并发解决方案的实际使用及优缺点。 首先定义一下数据库,做一个最简单的库存表,如下...
  • mysql悲观锁乐观锁

    2020-06-14 15:28:04
    无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。 悲观锁 https://www.hollischuang.com/archives/934 当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好...
  • 悲观锁乐观锁的实现(详情图解)

    万次阅读 多人点赞 2020-05-27 10:26:59
    在了解悲观锁乐观锁之前,我们先了解一下什么是锁,为什么要用到锁? 技术来源于生活,锁不仅在程序中存在,在现实中我们也随处可见,例如我们上下班打卡的指纹锁,保险柜上的密码锁,以及我们我们登录的用户名...
  • MySQL -乐观锁悲观锁

    2021-01-23 15:15:43
    乐观锁悲观锁是数据库的一种思想,和其他的排它锁,共享锁之类的不是一类含义。在并发的情况下,采用乐观锁或者悲观锁可以防止数据问题。 悲观锁 定义 悲观锁乐观锁
  • 悲观锁乐观锁是人们定义出来的概念,你可以理解为一种思想,是处理并发资源的常用手段。 不要把他们与mysql中提供的锁机制(表锁,行锁,排他锁,共享锁)混为一谈。 一.悲观锁 就是对于数据的处理持悲观态度,总...
  • mysql 悲观锁乐观锁

    2017-08-29 14:56:57
    在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是...无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁悲观锁的概念,像memca
  • 乐观锁(Optimistic Locking):顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,"乐观"的认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。 悲观锁(Pessimistic Lock...
  • MySQL乐观锁悲观锁

    2020-09-11 15:06:34
    悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。 悲观锁:假定会发生并发冲突,...
  • 一 引言--为什么mysql提供了  最近看到了mysql有行锁和表锁两个概念,越想越疑惑。为什么mysql要提供机制,而且这种机制不是一个摆设,还有很多人在用。在现代数据库里几乎有事务机制,acid的机制应该能解决...
  • 补充mysql select ... for update 的行锁定 和表锁定 明确指定主键,并且有此数据,row lock select * from t_goods where id=1 for update; 明确指定主键,若查无此数据,无lock select * from t_goods where id=...
  • 文章目录前言一、悲观锁乐观锁定义二、悲观锁的实现1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习...
  • 无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁悲观锁的概念,像memcache、hibernate、tair等都有类似的概念。 针对于不同的业务场景,应该选用...
  • 悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。 悲观锁:假定会发生并发冲突,...
  • ,在现实生活中是为我们想要隐藏于外界所使用的一种工具。在计算机中,是协调多个进程或线程并发访问某一资源的一种机制。在数据库当中,除了传统的计算资源(CPU、RAM、I/O等等)的争用之外,数据也是一种供许多...
  • mysql乐观锁悲观锁

    2017-11-28 08:24:46
    乐观锁认为一般情况下对记录的修改不会发生冲突,所以在数据最后提交更新的时候,才会正式对数据的冲突与否进行检测.如果发现修改冲突,业务逻辑什么都不做,将错误信息告诉客户端,由客户端决定如何处理
  • MySQL乐观锁&悲观锁

    2019-06-24 23:09:49
    二、乐观锁(Optimistic Lock) 1、定义 2、实现: 3、使用场景举例: 建议阅读文献: 一、悲观锁(Pessimistic Lock) 1、定义 悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有...
  • 无论是悲观锁还是乐观锁, 都是人们定义出来的概念, 可以认为是一种思想; 其实不仅仅是关系型数据库系统中有乐观锁悲观锁的概念, 像memcache、hibernate、tair等都有类似的概念 针对于不同的业务场景, 应该选用不同...
  • 数据库乐观锁悲观锁的理解和实现(转载&总结)

    万次阅读 热门讨论 2018-05-11 10:36:08
    理解:1. 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。...这种是数据库锁乐观锁优点程序实现,不会存在死锁等问题。他的适用场景也相对乐观。阻止不了除了程序之外...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,390
精华内容 4,956
关键字:

mysql悲观锁乐观锁定义

mysql 订阅