您当前的位置: 首页 >  ar

知其黑、受其白

暂无认证

  • 0浏览

    0关注

    1250博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

laravel执行过程

知其黑、受其白 发布时间:2021-06-21 12:12:10 ,浏览量:0

参考文献

Laravel速查表:https://learnku.com/docs/laravel-cheatsheet/6.0

Laravel API文档:https://laravel.com/api/6.x/index.html

Laravel内助辅助函数文档:https://learnku.com/docs/laravel/6.x/helpers/5164

laravel执行过程

1、载入Composer的自动加载文件,自动加载的真正实现是通过/vendor/autoload.php实现的,代码如下

2、加载/bootstrap/app.php文件,实例化服务容器,存在$app

3、向服务容器里绑定了三个服务:HTTP、Console、Excepiton

4、make方法取出http,存到$request变量中($request变量贯穿始终)

5、按照app配置文件顺序register所有的服务提供者

6、按照注册顺序执行所有服务提供者的boot方法

7、将请求发送到route

8、执行中间件

9、发送请求

10、返回响应

技术关键字

sharedLock:共享锁

lockForUpdate:排他锁

// 消极锁
DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景

|–表级锁(锁定整个表)

|–页级锁(锁定一页)

|–行级锁(锁定一行)

|–共享锁(S锁,MyISAM 叫做读锁)

|–排他锁(X锁,MyISAM 叫做写锁)

|–悲观锁(抽象性,不真实存在这个锁)

|–乐观锁(抽象性,不真实存在这个锁)

1、InnoDB与MyISAM

Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB。查看当前存储引擎:

show variables like '%storage_engine%';

MyISAM 操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高。当然同时它也不会存在死锁问题。

而 InnoDB 与 MyISAM 的最大不同有两点:

一是 InnoDB 支持事务; 二是 InnoDB 采用了行级锁。也就是你需要修改哪行,就可以只锁定哪行。

在 Mysql 中,行级锁并不是直接锁记录,而是锁索引。

索引分为主键索引和非主键索引两种:

如果一条sql 语句操作了主键索引,Mysql 就会锁定这条主键索引; 如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。

InnoDB 行锁是通过给索引项加锁实现的,如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。

也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。

2、共享锁与排他锁

1.首先说明:数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。

共享锁:

对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。

要想修改就必须等所有共享锁都释放完之后。语法为:

select * from table lock in share mode

排他锁:

对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。语法为:

select * from table for update
数据库锁机制
1 前言
数据库大并发操作要考虑死锁和锁的性能问题。看到网上大多语焉不详(尤其更新锁),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一个线程。T3,T4以此类推。下面以SQL Server(2005)为例。

2 锁的种类
共享锁(Shared lock)。
例1:
----------------------------------------
T1:    select * from table (请想象它需要执行1个小时之久,后面的sql语句请都这么想象)
T2:    update table set column1='hello'

过程:

T1运行 (加共享锁)
T2运行
If T1 还没执行完
    T2等......
else
    锁被释放
    T2执行
endif

T2之所以要等,是因为T2在执行update前,试图对table表加一个排他锁,
而数据库规定同一资源上不能同时共存共享锁和排他锁。所以T2必须等T1
执行完,释放了共享锁,才能加上排他锁,然后才能开始执行update语句。

例2:
----------------------------------------
T1:    select * from table
T2:    select * from table

这里T2不用等待T1执行完,而是可以马上执行。

分析:
T1运行,则table被加锁,比如叫lockA
T2运行,再对table加一个共享锁,比如叫lockB。

两个锁是可以同时存在于同一资源上的(比如同一个表上)。这被称为共
享锁与共享锁兼容。这意味着共享锁不阻止其它session同时读资源,但阻
止其它session update

例3:
----------------------------------------
T1:    select * from table
T2:    select * from table
T3:    update table set column1='hello'

这次,T2不用等T1运行完就能运行,T3却要等T1和T2都运行完才能运行。
因为T3必须等T1和T2的共享锁全部释放才能进行加排他锁然后执行update
操作。

例4:(死锁的发生)
----------------------------------------
T1:
begin tran
select * from table (holdlock) (holdlock意思是加共享锁,直到事物结束才释放)
update table set column1='hello'

T2:
begin tran
select * from table(holdlock)
update table set column1='world'

假设T1和T2同时达到select,T1对table加共享锁,T2也对加共享锁,当
T1的select执行完,准备执行update时,根据锁机制,T1的共享锁需要升
级到排他锁才能执行接下来的update.在升级排他锁前,必须等table上的
其它共享锁释放,但因为holdlock这样的共享锁只有等事务结束后才释放,
所以因为T2的共享锁不释放而导致T1等(等T2释放共享锁,自己好升级成排
他锁),同理,也因为T1的共享锁不释放而导致T2等。死锁产生了。

例5:
----------------------------------------
T1:
begin tran
update table set column1='hello' where id=10

T2:
begin tran
update table set column1='world' where id=20

这种语句虽然最为常见,很多人觉得它有机会产生死锁,但实际上要看情
况,如果id是主键上面有索引,那么T1会一下子找到该条记录(id=10的记
录),然后对该条记录加排他锁,T2,同样,一下子通过索引定位到记录,
然后对id=20的记录加排他锁,这样T1和T2各更新各的,互不影响。T2也不
需要等。

但如果id是普通的一列,没有索引。那么当T1对id=10这一行加排他锁后,
T2为了找到id=20,需要对全表扫描,那么就会预先对表加上共享锁或更新
锁或排他锁(依赖于数据库执行策略和方式,比如第一次执行和第二次执行
数据库执行策略就会不同)。但因为T1已经为一条记录加了排他锁,导致
T2的全表扫描进行不下去,就导致T2等待。

死锁怎么解决呢?一种办法是,如下:
例6:
----------------------------------------
T1:
begin tran
select * from table(xlock) (xlock意思是直接对表加排他锁)
update table set column1='hello'

T2:
begin tran
select * from table(xlock)
update table set column1='world'

这样,当T1的select 执行时,直接对表加上了排他锁,T2在执行select时,就需要等T1事物完全执行完才能执行。排除了死锁发生。
但当第三个user过来想执行一个查询语句时,也因为排他锁的存在而不得不等待,第四个、第五个user也会因此而等待。在大并发
情况下,让大家等待显得性能就太友好了,所以,这里引入了更新锁。
更新锁(Update lock)
为解决死锁,引入更新锁。

例7:
----------------------------------------
T1:
begin tran
select * from table(updlock) (加更新锁)
update table set column1='hello'
T2:
begin tran
select * from table(updlock)
update table set column1='world'

更新锁的意思是:“我现在只想读,你们别人也可以读,但我将来可能会做更新操作,我已经获取了从共享锁(用来读)到排他锁
(用来更新)的资格”。一个事物只能有一个更新锁获此资格。

T1执行select,加更新锁。
T2运行,准备加更新锁,但发现已经有一个更新锁在那儿了,只好等。

当后来有user3、user4...需要查询table表中的数据时,并不会因为T1的select在执行就被阻塞,照样能查询,相比起例6,这提高
了效率。

例8:
----------------------------------------
T1:    select * from table(updlock)    (加更新锁)
T2:    select * from table(updlock)    (等待,直到T1释放更新锁,因为同一时间不能在同一资源上有两个更新锁)
T3:    select * from table (加共享锁,但不用等updlock释放,就可以读)

这个例子是说明:共享锁和更新锁可以同时在同一个资源上。这被称为共享锁和更新锁是兼容的。

例9:
----------------------------------------
T1:
begin
select * from table(updlock)      (加更新锁)
update table set column1='hello'  (重点:这里T1做update时,不需要等T2释放什么,而是直接把更新锁升级为排他锁,然后执行update)
T2:
begin
select * from table               (T1加的更新锁不影响T2读取)
update table set column1='world'  (T2的update需要等T1的update做完才能执行)

我们以这个例子来加深更新锁的理解,

第一种情况:T1先达,T2紧接到达;在这种情况中,T1先对表加更新锁,T2对表加共享锁,假设T2的select先执行完,准备执行update,
发现已有更新锁存在,T2等。T1执行这时才执行完select,准备执行update,更新锁升级为排他锁,然后执行update,执行完成,事务
结束,释放锁,T2才轮到执行update。

第二种情况:T2先达,T1紧接达;在这种情况,T2先对表加共享锁,T1达后,T1对表加更新锁,假设T2 select先结束,准备
update,发现已有更新锁,则等待,后面步骤就跟第一种情况一样了。

这个例子是说明:排他锁与更新锁是不兼容的,它们不能同时加在同一子资源上。

排他锁(独占锁,Exclusive Locks)
这个简单,即其它事务既不能读,又不能改排他锁锁定的资源。
例10
T1:    update table set column1='hello' where id1000

假设T1先达,T2随后至,这个过程中T1会对id10

T1执行时,会先对第一页加锁,读完第一页后,释放锁,再对第二页加锁,依此类推。假设前10行记录恰好是一页(当然,一般不可能
一页只有10行记录),那么T1执行到第一页查询时,并不会阻塞T2的更新。

例18:
----------------------------------------
T1:    select * from table (rowlock)
T2:    update table set column1='hello' where id=10

T1执行时,对每行加共享锁,读取,然后释放,再对下一行加锁;T2执行时,会对id=10的那一行试图加锁,只要该行没有被T1加上行锁,
T2就可以顺利执行update操作。

例19:
----------------------------------------
T1:    select * from table (tablock)
T2:    update table set column1='hello' where id = 10

T1执行,对整个表加共享锁. T1必须完全查询完,T2才可以允许加锁,并开始更新。

以上3例是手工指定锁的粒度,也可以通过设定事物隔离级别,让数据库自动设置锁的粒度。不同的事物隔离级别,数据库会有不同的
加锁策略(比如加什么类型的锁,加什么粒度的锁)。具体请查联机手册。
5 锁与事物隔离级别的优先级
手工指定的锁优先,
例20:
----------------------------------------
T1:    GO
       SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
       GO
       BEGIN TRANSACTION
       SELECT * FROM table (NOLOCK)
       GO
T2:    update table set column1='hello' where id=10

T1是事物隔离级别为最高级,串行锁,数据库系统本应对后面的select语句自动加表级锁,但因为手工指定了NOLOCK,所以该select
语句不会加任何锁,所以T2也就不会有任何阻塞。
6 数据库的其它重要Hint以及它们的区别
1) holdlock 对表加共享锁,且事物不完成,共享锁不释放。
2) tablock  对表加共享锁,只要statement不完成,共享锁不释放。
   与holdlock区别,见下例:
   例21
   ----------------------------------------
   T1:
   begin tran
   select * from table (tablock)
   T2:
   begin tran
   update table set column1='hello' where id = 10

   T1执行完select,就会释放共享锁,然后T2就可以执行update. 此之谓tablock. 下面我们看holdlock
   例22
   ----------------------------------------
   T1:
   begin tran
   select * from table (holdlock)
   T2:
   begin tran
   update table set column1='hello' where id = 10
   
   T1执行完select,共享锁仍然不会释放,仍然会被hold(持有),T2也因此必须等待而不能update. 当T1最后执行了commit或
   rollback说明这一个事物结束了,T2才取得执行权。
  
3) TABLOCKX 对表加排他锁
  
   例23:
   ----------------------------------------
   T1:    select * from table(tablockx) (强行加排他锁)
   其它session就无法对这个表进行读和更新了,除非T1执行完了,就会自动释放排他锁。
   例24:
   ----------------------------------------
   T1:    begin tran
          select * from table(tablockx)
   这次,单单select执行完还不行,必须整个事物完成(执行了commit或rollback后)才会释放排他锁。
  
4) xlock 加排他锁
   那它跟tablockx有何区别呢?

   它可以这样用,
   例25:
   ----------------------------------------
   select * from table(xlock paglock) 对page加排他锁
   而TABLELOCX不能这么用。

   xlock还可这么用:select * from table(xlock tablock) 效果等同于select * from table(tablockx)
7 锁的超时等待
例26

SET LOCK_TIMEOUT 4000 用来设置锁等待时间,单位是毫秒,4000意味着等待
4秒可以用select @@LOCK_TIMEOUT查看当前session的锁超时设置。-1 意味着
永远等待。

T1: begin tran
    udpate table set column1='hello' where id = 10
T2: set lock_timeout 4000
    select * from table wehre id = 10
T2执行时,会等待T1释放排他锁,等了4秒钟,如果T1还没有释放排他锁,T2就会抛出异常: Lock request time out period exceeded.

8 附:各种锁的兼容关系表
| Requested mode                     | IS  | S   | U   | IX  | SIX | X  |
| Intent shared (IS)                 | Yes | Yes | Yes | Yes | Yes | No |
| Shared (S)                         | Yes | Yes | Yes | No  | No  | No |
| Update (U)                         | Yes | Yes | No  | No  | No  | No |
| Intent exclusive (IX)              | Yes | No  | No  | Yes | No  | No |
| Shared with intent exclusive (SIX) | Yes | No  | No  | No  | No  | No |
| Exclusive (X)                      | No  | No  | No  | No  | No  | No |

9 如何提高并发效率
悲观锁:利用数据库本身的锁机制实现。通过上面对数据库锁的了解,可以根据具体业务情况综合使用事务隔离级别与合理的手工指定锁的方式比如降低锁的粒度等减少并发等待。
乐观锁:利用程序处理并发。原理都比较好理解,基本一看即懂。方式大概有以下3种
对记录加版本号.
对记录加时间戳.
对将要更新的数据进行提前读取、事后对比。
不论是数据库系统本身的锁机制,还是乐观锁这种业务数据级别上的锁机制,本质上都是对状态位的读、写、判断。
关注
打赏
1665558895
查看更多评论
立即登录/注册

微信扫码登录

0.0436s