Mysql 锁的介绍和使用

参考

基本概念

并发

网络编程中,多个请求/查询(进程/线程) 修改同一数据时,就会产并发控制问题,即谁先修改谁后修改.锁机制就是用来解决和控制并发问题的.

Mysql根据加锁的范围(粒度)分为三种锁

全局锁

给整个数据库加锁

FLUSH TABLES WITH READ LOCK
//全局读锁,会使其它线程中的写操作 数据更新语句(删除更新) 数据定义语句(创建修改表) 和更新事物提交类语句被阻塞
//使用场景全库逻辑备份,存在无事物的存储引擎的数据库
mysqldump –single-transaction 适合全库innodb引擎(所有表都支持事物)备份

表级锁

一种是表锁,一种是元数据锁(meta data lock,MDL)

表锁 lock tables tablename… read/write 释放所unlock tables

表锁不仅仅会锁定其它线程对锁定表的操作,也会锁定当前执行lock 语句的线程接下里的操作对象.只能执行当前表的操作

当前线程锁定users表,无法操作其它表
其它线程可以正常读取users表

上图示例当前线程执行了所锁定users表,则无法查询或操作其它表,

此时其它线程中可以正常读取users表的数据,因为读锁不互斥.

锁定执行写操作
释放表锁
另一线程内的查询操作

上三图表示线程A锁定users表示,其它线程对users表的操作,在未释放表锁前,另一线程一直处于阻塞状态,直到释放表锁

另一类表级的锁是MDL(meta data lock)

不需要显示调用,数据库自动操作 Mysql5.5版本中开始引入.作用是保证读写的正确性.例如,一个线程进行遍历查询,另一个线程进行表结构变更.

对一个表进行增删改查操作时,加MDL读锁,对表执行结构变更时,加MDL写锁.

读锁之间不互斥,因此多个线程可以对同一张表做增删改查,

读写锁之间,读锁之间互斥,用户保证表结构变更操作的安全性.例如线程A进行表遍历查询,线程B进行表结构变更操作,线程B需要等线程A的读锁释放之后才能执行

MDL测试案例

测试数据20万
测试代码,事务中遍历数据表
sessionA执行代码中的数据表遍历
sessionB执行Alter语句修改users表结构

此时,sessionB会阻塞,因为sessionA中的MDL读锁还没有被释放

sessoinC中的所有查询都处于等待状态

sessionC所有查询被被阻塞,此时整个数据库不能正常查询,这就是生产环境数据库结构变更可能会踩的坑.

解决方案

1.解除长事务,变更前查询一下存在事物正在运行的事物(information_schema 库的 innodb_trx表),手动kill调.

2.alter table语句设定等待时间. MariaDB和AliSQL支持 DDL NOWWAIT?WAIT n 语法

ALTER TABLE tbl_name NOWAIT add column...

ALTER TABLE tbl_name WAIT n add column...

MDL总结

mysql 5.6之前的版本,执行alter语句时间会很长,通过建立新的临时表方案解决,导入数据,最后重命名.

mysql5.6引入online ddl,通常情况下不会锁库,但是存在长事务和慢查询时,也会出现

行锁

行锁是由存储引擎层实现的,是mysql最小锁定粒度,可以锁定一行数据.常用引擎Innodb支持行锁MyISAM不支持行锁

Innodb行锁介绍

两阶段加锁,,

Laravel 迁移文件 简单总结

参考

简介

数据库迁移就像是数据库的版本控制,可以让你的团队轻松修改并共享应用程序的数据库结构。迁移通常与 Laravel 的数据库结构生成器配合使用,让你轻松地构建数据库结构。如果你曾经试过让同事手动在数据库结构中添加字段,那么数据库迁移可以让你不再需要做这样的事情。

执行 php artisan migtate 后 数据库中会生成一个迁移文件表 migrations ,每一条记录对应一个执行过的迁移文件,怎么看每次迁移了哪些文件?在 migrations 表中有一个 batch 字段,字段值相同的为同一次迁移

创建 created方法

Schema::create('users', function (Blueprint $table) {
    //...		
});

修改 table方法

数据库因为业务需要变更时,每个表的变更创建一个单独的迁移文件方便生产执行.

需要引入composer require doctrine/dbal 扩展包

Schema::table('migration_demo', function (Blueprint $table) {

});

1 对一个字段做多种修改 例如 重命名和修改类型同时进行.原字段为type 类型int

//无效方式1
Schema::table('migration_demo', function (Blueprint $table) {
    $table->bigInteger('type')->default('0')->change();
    $table->renameColumn('type', 'demo_type');
    //经测试这两号代码颠倒顺序最后生成的语句是一样的
});

执行语句, 字段重命名时又改回了默认的int类型

ALTER TABLE migration_demo CHANGE type demo_type INT DEFAULT 0 NOT NULL
ALTER TABLE migration_demo CHANGE type type BIGINT DEFAULT 0 NOT NULL
//无效方式2
Schema::table('migration_demo', function (Blueprint $table) {
    $table->bigInteger('type')->default('0')->change()->renameColumn('type', 'demo_type');
});

执行语句 ,rename并没有生效

ALTER TABLE migration_demo CHANGE type type BIGINT DEFAULT 0 NOT NULL

同一字段执行多种变更,正确的方式

Schema::table('demo', function (Blueprint $table) {
   $table->renameColumn('name', 'demo_name');
});
Schema::table('demo', function (Blueprint $table) {
   $table->string('demo_name', 255)->default('')->change();
});

执行语句结果

ALTER TABLE demo CHANGE demo_name demo_name VARCHAR(255) DEFAULT '' NOT NULL COLLATE utf8mb4_unicode_ci	
ALTER TABLE demo CHANGE name demo_name VARCHAR(20) DEFAULT '' NOT NULL

迁移文件的另一种简介执行方式,使用DB statement 执行原生DDL语句

DB::statement("ALTER TABLE `lara`.`users` CHANGE COLUMN `remember_token` `remember_tokens` VARCHAR(255) COLLATE 'utf8mb4_unicode_ci' NULL DEFAULT NULL");

总结

个人感觉这种方式适合中小型项目和公司,在有DBA的公司应该使用专业的数据库迁移工具

Laravel Telescope

参考

简介

Larave Telescope 是 Laravel 框架的官方出品的debug工具包, 5.7.7以上版本才有.

此文用来记录一些文档中没有介绍的坑点,使用技巧以及原理

安装配置参考文档

1.扩展自带数据表迁移文件,配合其它迁移文件扩展包生成时要注意

定制数据迁移

如果您不打算使用 Telescope 的默认迁移,则应该在 AppServiceProvider 的 register 方法中调用 Telescope::ignoreMigrations 方法。您可以使用 php artisan vendor:publish --tag=telescope-migrations 命令导出默认迁移。

注意:如果使用三方扩展包生成迁移文件或手动编写迁移文件时不要生成Telescope相关迁移文件,否则执行时会造成冲突

按钮功能介绍

1暂停 2刷新 3定制Tag 可以对指定Tag数据进行监控

Tag设置规则,Auth:ID 模型:ID 可以看一下 telescope_entries_tags 表中自动记录的标签。但是在环境变量设置为production后,除了Auth其它的并不好使

生产环境通过认证访问

protected function gate()
    {
        Gate::define('viewTelescope', function ($user) {
            return in_array($user->email, [
                'jordon.kub@example.com', 'njerde@example.net'
            ]);
        });
    }

设置制定邮箱或自定以其他字段的用户,前提必须使用laravel自带的用户认证功能

测试问题

参考

启用了telescope(望远镜)debug工具之后,用phpunit 跑所有测试是会报ReflectionException: Class env does not exist 错误

解决方案

You could just add <env name="TELESCOPE_ENABLED" value="false"/> to the phpunit.xml-file.
//将TELESCOPE_ENABLED false添加到phpunit.xml配置中