番禺网站制作价格,线上运营怎么做,免费发布软文广告推广平台,如果想看网站的收费电影应该怎么做目录
前言
一、全局锁
二、表级锁
三、行锁 前言
为什么要设计锁#xff0c;锁设计初衷是为了解决多线程下并发问题。出现并发的时候用锁进行数据同步#xff0c;避免因并发造成了数据错误(数据覆盖)。可见锁的重要性#xff0c;并不是所有的数据库都有锁。比如Redis锁设计初衷是为了解决多线程下并发问题。出现并发的时候用锁进行数据同步避免因并发造成了数据错误(数据覆盖)。可见锁的重要性并不是所有的数据库都有锁。比如Redis单个操作是原子性的并且是单线程的并发请求会在队列排列请求是按顺序执行的就不需要锁。Mysql 需要锁mysql 是多线程的并发操作要保证数据的一致性需要通过锁进行数据同步。根据锁的范围来讲mysql 的锁分为全局锁、表级锁和行锁
一、全局锁
全局锁就是对整个数据库实例加锁。Msql 提供的加全局锁的方法命令是 Flush tables with read lock (FTWRL)以及set global readonlytrue那么整库是只读状态那么对数据库的DDL 以及 DML 将被阻塞数据库将报下列错误。 全局锁主要是用在全库逻辑备份。这个命令产生以下风险如果你在主库上备份那么备份期间不能执行更新如果在从库上备份备份期间从库不能执行主库同步过来的binlog会导致主从延迟。
我们在想想备份为什么要加锁一定要加锁吗。答案是肯定的必须的。加锁是为了保证数据的一致性以及业务的完整性。
我们现在有一个订单表有一个余额表。有用户购买一个产品业务逻辑是先扣除余额然后往订单插一条数据。业务期间我们开始备份在扣除余额前开始备份。恢复数据时候我们发现用户的余额没有减少但是用户多了一个订单。是不是不可以呀所以备份前需要加锁的。加全局锁会影响业务也是不可取的有没有其他方法呢。
可以用官方自带的逻辑备份工具mysqldump当在mysqldump使用参数–single-transaction 的时候导数据前会启动一个事物这个事物的隔离级别必须是RR来确保拿到了一致性视图。由于MVCC 支持数据库的DML 正常的处理。这种方法需要使用事务引擎的库MyISAM就不可以。
MyISAM备份还是需要使用FTWRL的。有人说用set global readonlytrue也可以让全库只读可以不当然也是可以的。他们之间有什么区别呢。一是修改global变量的方式影响面非常大readonly会被用来做其他逻辑比如用来判断一个库是主库还是备库 二是在异常处理机制的差异。如果客户端异常断开时使用FTWRL会自动释放全局锁整个库可以正常更新。使用readonly整个库依旧处于只读状态不能更新。
二、表级锁
在mysql 提供了两种表级别的锁一种是表锁二种是元数据锁meta data lockMDL)。表锁的语法是 lock tables … read/write用这种方法的话可以用unlock 去释放锁也可以客户端断开时自动释放。lock 的影响面很大不仅影响其他线程的write/read,write,本线程接下来的write/也会影响。这种方法毕竟对业务的影响很大并不常用。
MDL锁不需要用户显示使用是mysql 内部的一种机制。当我们对数据表DML时系统会自动加上MDL读锁防止用户DDL操作产生不一致性。当我们对数据表DDL时系统会自动加上MDL写锁防止用户DML操作以及其他线程的MDL操作避免数据不一致。
多说一句MDL是一个两阶段锁在事务开始时加锁事物结束时释放锁。在这个地方可以给大家解释下session A , session B , session C, session D 是几个客户端
Session A
此时表加了一个MDL 读锁事务还没有结束一直保留着MDL 读锁直到commit 或者 rollback Session B
此时表加了一个MDL 读锁,读锁之间可以共享 Session C
此时表加了一个MDL 写锁读锁和写锁之间是互斥的所以阻塞 Session D
此时表加了一个MDL 读锁由于Session C 被阻塞了所以 Session D也不阻塞了 由此我们可以确定MDL 锁的生命周期是从事务开始到事务提交。上面在解释一下Session D也是MDL读锁为啥也被阻塞了Session C被阻塞后后面客户端对表的MDL锁都会放到队列里当事务提交后依次从队列取出执行
如何安全的给表修改字段呢
事务不提交就会一直占着MDL锁。在 MySQL 的 information_schema 库的 innodb_trx 表中你可以查到当前执行中的事务。如果你要做 DDL 变更的表刚好有长事务在执行要考虑先暂停 DDL或者 kill 掉这个长事务。
下面是如何kil 一个事物
一、在 information_schema 库的 innodb_trx 中找到一个事物id 然后执行kill 就可以了 如果业务很重要或者表一直有数据更新那么这种方法不可以了。需要用
ALTER TABLE tbl_name NOWAIT add column ...ALTER TABLE tbl_name WAIT N add column ...
这种方法失败了重试知道成功 。MariaDB 已经合并了 AliSQL 的这个功能所以这两个开源分支目前都支持 DDL NOWAIT/WAIT n 这个语法。
有人问我mysql 5.6不是支持online ddl了吗还会有阻塞吗其实会的。online ddl 执行过程是这样的分为以下几步 拿MDL写锁 降级成MDL读锁 真正做DDL 升级成MDL写锁 释放MDL锁
1、2、4、5如果没有锁冲突执行时间非常短。第3步占用了DDL绝大部分时间这期间这个表可以正常读写数据是因此称为“online ”
我们的例子第一步就锁住了
三、行锁
行锁住要讨论的是innodb引擎下机制myisam 不支持行锁也就不支持并发也就意味着myisam的更新是表级锁。
在 InnoDB 事务中行锁是在需要的时候才加上的但并不是不需要了就立刻释放而是要等到事务结束时才释放。这个就是两阶段锁协议。
这个机制非常重要为我们在业务设计中减少锁的冲突提供了理论的支持。那就是说如果你的事务中需要锁多个行要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
既然是有锁对于我们业务开发会不会造成死锁呢答案是肯定的什么事死锁呢
当并发系统中不同线程出现循环资源依赖涉及的线程都在等待别的线程释放资源时就会导致这几个线程都进入无限等待的状态称为死锁。
当出现死锁后有两种策略解决死锁 一种策略是直接进入等待直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。 另一种策略是发起死锁检测发现死锁后主动回滚死锁链条中的某一个事务让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on表示开启这个逻辑。
在 InnoDB 中innodb_lock_wait_timeout 的默认值是 50s意味着如果采用第一个策略当出现死锁以后第一个被锁住的线程要过 50s 才会超时退出然后其他线程才有可能继续执行。对于在线服务来说这个等待时间往往是无法接受的。如果值设置的太小那么正常的逻辑也有可能受到影响。
innodb_deadlock_detect 这个是有负担的试想100个线程更新一条记录那么就有100*100 个量级死锁检测表的行数越多会消耗大量的cpu 资源。如果关掉呢又会一直死锁直到超时会影响业务。
那么怎么解决这样的问题呢
以上两种方案减少innodb_lock_wait_timeout时间以及 innodb_deadlock_detectoff都是不可取的那么我们通过减少连接线程去减少并发从而达到减少死锁后来发现这种方案也是不可靠的第一、客户端的数量没法控制 第二、减少线程意味着系统的性能得不到完全利用。也只能从业务上去考虑呢根据不同业务把更新一行放到多行上锁的粒度变小了死锁就减少了。通过这些我们总结一个结论提高系统性能往往需要确定系统的瓶颈在哪锁造成的瓶颈可以考虑减少锁的粒度比如用分片锁等等
重要大家有没有想过行锁是加在哪呢是在索引上意味着我们在update 时候wehere 没有设置索引那么只要扫描的记录都会加上索引事务提交的时候统一释放。所以update 更新语句一定要记住加上索引由于update 是当前读的没有加上索引一定是灾难性的。