一文读懂 MySQL 的隔离级别级锁的关系

MySQL 中的隔离四种隔离级别与锁的关系一直挺模糊的,读了好多文章感觉着都不是好理解,今天在“爱可生开源社区”看到一篇文章,感觉着挺容易理解的。

READ UNCOMMITTED 未提交读,可以读取未提交的数据。

READ COMMITTED 已提交读,对于锁定读(select with for update 或者 for share)、update 和 delete 语句, InnoDB 仅锁定索引记录,而不锁定它们之间的间隙,因此允许在锁定的记录旁边自由插入新记录。Gap locking 仅用于外键约束检查和重复键检查。

REPEATABLE READ 可重复读,事务中的一致性读取读取的是事务第一次读取所建立的快照。

SERIALIZABLE 序列化

文中主要对 RR 和 RC 两种常用的隔离级别做了不同情况的说明,对于 SERIALIZABLE 序列化 和 READ UNCOMMITTED 未提交读,可以读取未提交的数据这个几乎不怎么用的,也好理解所以未在文中体现。对于 RR 和 RC 主要区别是 RR 存在 Gap Lock间隙锁,而RC则没有Gap Lock间隙锁,所以在互联网中绝大部分是采用了RC 隔离级别,而未使用MySQL中默认的RR级别。对于锁的介绍请参考:https://blog.haohtml.com/archives/17758

查看原文:https://mp.weixin.qq.com/s/DhMy6fsdlFj3dGqRE_0JMg

MySQL5.7中Undo回收收缩相关参数

在MySQL5.7以前,ibdata1文件会逐渐增大(ibdata1文件包含哪些信息?),非常占用系统空间,特别是一些云数据来说,磁盘非常的贵,想要回收空间,只能进行一次导出和导入操作,来重新生成undo 表空间,从MySQL5.7开始,有了在线回收undo表空间的功能,主要由以下几个参数设置。

innodb_undo_directory = .
为undo文件存储路径。如果没有指定默认值(NULL),则undo表空间则存放到mysql的data目录里(datadir选项)。配置此项可以用undo从ibdata文件里分离出来。单独存储。

innodb_undo_logs = 128
(默认值 128)undo rollback segment 回滚段个数,为 innodb_rollback_segments 参数选项的别名,最大值为128,其中32个为使用临时表空间 ibtmp1 保留,1个为系统表空间使用,剩余的95个为 undo tablespaces 使用。
当 innodb_rollback_segments<=32的时候,系统将自动分配1个rollback segment给系统表空间,32个分给临时表空间。
此选项以后版本将移除!

innodb_undo_tablespaces = 0
(MySQL5.7默认值为0,MySQL8默认值为2)undo文件个数,此值需要在MySQL Server 初始化的时候指定,一经设定,以后将无法修改,否则重启后会提示部分Undo 文件找不到。默认值为0, 此时无法进行Undo回收操作,回收undo表空间至少需要为2个才可以, 需保证其中一个进行回收收缩时,另一个为可用状态。保存路径为 innodb_undo_directory选项设置,undo文件名规则为 undoN。文件大小受innodb_page_size选项影响。
此选项以后版本将移除!

innodb_undo_log_truncate = OFF
(默认值NO)参数设置为ON,即开启在线回收undo日志文件,支持动态设置,当超过 innodb_max_undo_log_size 时被进行收缩,至少需要两个undo文件,即innodb_undo_tablespace>=2

innodb_max_undo_log_size = 1073741824
(默认1GB)当超过阈值时,会触发truncate回收动作,truncate后空间缩小到10MB

innodb_purge_rseg_truncate_frequency = 128
(默认值128), 控制回收undo log的频率。 指定purge操作被唤起多少次之后才释放rollback segments。当undo表空间里面的rollback segments被释放时,undo表空间才会被truncate。由此可见,该参数越小,undo表空间被尝试truncate的频率越高。 。

联想思考:
ibdata文件包含哪些信息?
undo logs、change buffer、doublewrite buffer、表数据、索引数据,如果启用了 innodb_file_per_table 选项的话,则表数据和索引数据则存储到相应表的.ibd 文件里),参考:
https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_ibdata_file

ibdata文件爆增原因有哪些?
事务未提交、大事务、启用了共享表空间、磁盘io过慢导致check point远远落后。等等

如何避免ibdata文件一直爆增的问题?
尽量短事务、增加 purge 线程、加速purge频率(innodb_purge_truncate_frequency)、监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill

参考:
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_max_undo_log_size

undo表空间回收https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-tablespaces.html

goroutine和线程区别

从调度上看,goroutine的调度开销远远小于线程调度开销。

OS的线程由OS内核调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个调度器内核函数。这个函数暂停当前正在运行的线程,把他的寄存器信息保存到内存中(暂时保存线程状态),查看线程列表并决定接下来运行哪一个线程,再从内存中恢复线程的注册表信息,最后继续执行选中的线程。这种线程切换需要一个完整的上下文切换:即保存一个线程的状态到内存,再恢复另外一个线程的状态,最后更新调度器的数据结构。某种意义上,这种操作还是很慢的。

OS 线程调度器

Go运行的时候包涵一个自己的调度器,这个调度器使用一个称为一个M:N调度技术,m个goroutine到n个os线程(可以用GOMAXPROCS来控制n的数量),Go的调度器不是由硬件时钟来定期触发的,而是由特定的go语言结构来触发的,他不需要切换到内核语境,所以调度一个goroutine比调度一个线程的成本低很多。

从栈空间上,goroutine的栈空间更加动态灵活。

每个OS的线程都有一个固定大小的栈内存,通常是2MB,栈内存用于保存在其他函数调用期间哪些正在执行或者临时暂停的函数的局部变量。这个固定的栈大小,如果对于goroutine来说,可能是一种巨大的浪费。作为对比,goroutine在生命周期开始只有一个很小的栈,典型情况是2KB, 在go程序中,一次创建十万左右的goroutine也不罕见(2KB*100,000=200MB)。而且goroutine的栈不是固定大小,它可以按需增大和缩小,最大限制可以到1GB。

参考:https://time.geekbang.org/course/detail/160-86799

goroutine没有一个特定的标识。

在大部分支持多线程的操作系统和编程语言中,线程有一个独特的标识,通常是一个整数或者指针,这个特性可以让我们构建一个线程的局部存储,本质是一个全局的map,以线程的标识作为键,这样每个线程可以独立使用这个map存储和获取值,不受其他线程干扰。

goroutine中没有可供程序员访问的标识,原因是一种纯函数的理念,不希望滥用线程局部存储导致一个不健康的超距作用,即函数的行为不仅取决于它的参数,还取决于运行它的线程标识。

reference: 《Go程序设计语言》E-mail: huahuiyang@gmail.com https://www.linkedin.com/in/huahuiyang/

转自:https://www.cnblogs.com/yanghuahui/p/9043631.html

MySQL利用 INFORMATION_SCHEMA.PROFILING 分析SQL性能

MySQL5.7中有一个系统默认库 information_schema , 里面有些表如 PROFILING,、OPTIMIZER_TRACE、 PROCESSLIST、INNODB_TRX等,其中 PROFILE 对于我们分析sql有很大的帮助,在此以前我们需要使用 SHOW PROFILE 命令,不过此命令以后将被废弃。下面我们就介绍一下如何使用此表。

从 MySQL8.0开始, 这个表也开始被废弃了,以后分析性能问题直接使用另一个系统库 performance_schema 里的相关表(setup_actors)就可以了。到时候 show profiles 和show profile两个命令也不能用了。

1.在使用此表前,我们需要开户性能检测功能。

mysql> SELECT @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set (0.00 sec)

mysql> SET profiling = 1;
Query OK, 0 rows affected (0.00 sec)

默认情况下是 OFF/0 状态。在我们分析完,最好关闭以减少服务器压力。

相关查询命令

show VARIABLES like 'profil%'
-------------------------------
profiling	ON
profiling_history_size	15

2. 了解 information_schema.profiling 表的常用字段

官方文档:https://dev.mysql.com/doc/refman/8.0/en/profiling-table.html

1QUERY_ID 查询ID, 用于标记不同的查询
2SEQ 一个查询内部执行的步骤 , 从2开始
3STATE 步骤的状态
4DURATION 持续时间
5CPU_USER 用户空间的cpu 使用量
6CPU_SYSTEM 内核空间的cpu 使用量
7CONTEXT_VOLUNTARY上下文主动切换
8CONTEXT_INVOLUNTARY上下文被动切换
9BLOCK_OPS_IN阻塞输入操作
10BLOCK_OPS_OUT阻塞输出操作
11MESSAGES_SENT消息发送
12MESSAGES_RECEIVED消息接受
13PAGE_FAULTS_MAJOR主分页错误
14PAGE_FAULTS_MINOR次分页错误
15SWAPS swap 发生的次数
16SOURCE_FUNCTIONMySQL源码执行函数
17SOURCE_FILE源码文件
18SOURCE_LINE源码行数

以下是我执行了一个join语句的输出,从结果中我们可以分析出哪个步骤执行的时间最长,进行相应的优化即可。

我们可以根据DURATION 列的值来分析哪一个模块消耗的时间多来进行相应的优化。

推荐使用 OPTIMIZER_TRACER 来分析 SQL 执行过程
https://dev.mysql.com/doc/internals/en/optimizer-tracing.html ,每种参数用法可参考:
https://www.cnblogs.com/DataArt/p/10232831.html

docker中将MySQL运行在容器中失败提示“ InnoDB : Error 22 with aio_write”的解决办法

今天利用docker容器创建mysql8.0的时候(window系统),指定了本地宿主机器的一个目录为容器mysql的datadir目录,发现创建失败了。

创建命令:

$ docker run -d --name mysql81 -v /e/container/mysql/mysql81/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 33081:3306 mysql

错误提示:

$ docker logs mysql81
2019-01-26T03:05:42.567230Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
2019-01-26T03:05:42.567618Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.13) starting as process 1
2019-01-26T03:05:42.572006Z 0 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
2019-01-26T03:05:42.832344Z 1 [ERROR] [MY-012592] [InnoDB] Operating system error number 22 in a file operation.
2019-01-26T03:05:42.832473Z 1 [ERROR] [MY-012596] [InnoDB] Error number 22 means 'Invalid argument'
2019-01-26T03:05:42.832556Z 1 [ERROR] [MY-012646] [InnoDB] File ./#innodb_temp/temp_1.ibt: 'aio write' returned OS error 122. Cannot continue operation
2019-01-26T03:05:42.832609Z 1 [ERROR] [MY-012981] [InnoDB] Cannot continue operation.

解决办法:

docker run -u 1000:50 -d --name mysql81 -v /e/container/mysql/mysql81/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 33081:3306 mysql --innodb-use-native-aio=0

参数:

-u 表示运行实例的用户,这里的 1000:50 表示的是docker这个用户
–innodb-use-native-aio=0 为MySQL的参数,作用是启用异步操作功能,提高MySQL性能

注意:
上面使用了docker运行了mysql实例,则如果进入到容器内部进行一些其它需要高级权限的操作的话,如apt update则会提示权限不足的情况。

参考:https://github.com/docker-library/mysql/issues/371

docker build . 命令后面的.是什么意思

今天来公司自己构建了一个Dockerfile,放在一个经常用到的项目目录里,内容如下:

# This is a comment
FROM ubuntu:14.04
MAINTAINER Docker Newbee <newbee@docker.com>
RUN apt-get -qq update
RUN apt-get -qqy install ruby ruby-dev
RUN gem install sinatra

然后执行

sudo docker build -t "cfanbo/test:v2" .

发现在构建的时候发送给 docker daemon 竟然有4G多,超极大。首先的第一反映出问题了。一个ubuntu镜像也没有这么大呀,况且现在还没有开始从远程pull 镜像呢。

那到底什么情况了呢?经过一翻搜索,发现在docker build . 的时候,会将当前目录里的内容发送给 docker daemon。只需要加一个 .dockerignore 文件,将其它内容排除掉就可以了,类似于git中的.gitignore文件的作用。

后面就想通过docker build -h 命令查看一下相关的参数,发现对于Dockerfile 文件位置需要 -f 参数指定,并非上面命令后面的.符号。那就有些疑惑了,原来学习的时候一直把.当作当前目录指定的。难道.有其它的作用,后面查看了一些文档,发现一直对 docker build 命令的过程不太了解。通过查找了一些资料才发现它的真正作用。

Docker 在运行时分为 Docker引擎(服务端守护进程) 以及 客户端工具,我们日常使用各种 docker 命令,其实就是在使用客户端工具与 Docker 引擎 进行交互。

那么当我们使用 docker build 命令来构建镜像时,这个构建过程其实是在 Docker引擎 中完成的,而不是在本机环境。

那么如果在 Dockerfile 中使用了一些 COPY 等指令来操作文件,如何让 Docker引擎 获取到这些文件呢?

这里就有了一个镜像构建上下文的概念,当构建的时候,由用户指定构建镜像的上下文路径,而 docker build 会将这个路径下所有的文件都打包上传给 Docker 引擎,引擎内将这些内容展开后,就能获取到所有指定上下文中的文件了(参考下方docker架构图)。

比如说 dockerfile 中的 COPY ./package.json /project,其实拷贝的并不是本机目录下的 package.json 文件,而是 docker引擎中展开的构建上下文中的文件,所以如果拷贝的文件超出了构建上下文的范围,Docker引擎是找不到那些文件的。


Docker构架

所以 docker build . 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录。

理解了上面的这些概念,就更方便的去理解 .dockerignore 文件的作用了。

为验证.的作用,我们可以创建一个空的目录,在目录里执行

docker build -f /home/tom/Dockerfile . 

会发现命令完全正常执行。

python中的@staticmethod和@classmethod区别(整理)

问题:Python中 @staticmethod@classmethod 两种装饰器装饰的函数有什么不同?
原地址http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python


Python其实有3类方法:

  • 静态方法(staticmethod)
  • 类方法(classmethod)
  • 实例方法(instance method)

看一下下面的示例代码:

def foo(x):
    print "executing foo(%s)" %(x)

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)" %(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)" %(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" %x

a = A()

在示例代码中,先理解下函数里面的 self 和 cls。这个 self 和 cls 是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作和任何东西(类、实例)无关。对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self,x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a,x))。类方法一样,只不过它传递的是类而不是实例, 如A.class_foo(x)。注意这里的 self 和 cls 可以替换别的参数,但是python的约定是这两个,尽量不要更改。

对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用时候需要使用a.static_foo(x)A.static_foo()来调用。

\实例方法类方法静态方法
a = A()a.foo(x)a.class_foo(x)a.static_foo(x)
A不可用A.class_foo(x)A.static_foo(x)

那么对于类方法和静态方法都可以不实例对象来调用,那两者的区别又有哪些呢?

从它们的使用上来看

  • @staticmethod 不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
  • @classmethod 也不需要self参数,但第一个参数需要是表示自身类的cls参数。

如果在@staticmethod中要调用到这个类的一些属性方法,只能直接 类名.属性名类名.方法名

而@classmethod因为持有 cls 参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

代码:

class A(object):
    bar = 1
    def foo(self):
        print 'foo'
 
    @staticmethod
    def static_foo():
        print 'static_foo'
        print A.bar
 
    @classmethod
    def class_foo(cls):
        print 'class_foo'
        print cls.bar
        cls().foo()
 
A.static_foo()
A.class_foo()
输出
static_foo
1

class_foo
1
foo

摘自:
https://www.cnblogs.com/taceywong/p/5813166.html
https://blog.csdn.net/handsomekang/article/details/9615239

windows下更新docker源(aliyun)

每个aliyun账号都有一个专属的镜像源 https://cr.console.aliyun.com/cn-hangzhou/mirrors

我这里安装的是 Docker Toolbox 软件,更新docker源有两种情况,一种是你还没有创建过Docker Machine,另一种是你已经创建过了Docker Machine。

一、未安装过

创建一台安装有Docker环境的Linux虚拟机,指定机器名称为default,同时配置Docker加速器地址。

$ docker-machine create –engine-registry-mirror=https://xxxx.mirror.aliyuncs.com -d virtualbox default

查看机器的环境配置,并配置到本地。然后通过Docker客户端访问Docker服务。

$ docker-machine env default
$ eval “$(docker-machine env default)”
$ docker info

这里 xxxx 是您的专有加速器地址


二、已安装过

登录已创建的Docker VM
$ docker-machine ssh default
$ sudo vi /var/lib/boot2docker/profile

在EXTRA_ARGS中添加

–registry-mirror=https://xxxx.mirror.aliyuncs.com

EXTRA_ARGS='
--label provider=virtualbox
--registry-mirror=https://fgxrbz510.mirror.aliyuncs.com
'
CACERT=/var/lib/boot2docker/ca.pem
DOCKER_HOST='-H tcp://0.0.0.0:2376'
DOCKER_STORAGE=aufs
DOCKER_TLS=auto
SERVERKEY=/var/lib/boot2docker/server-key.pem
SERVERCERT=/var/lib/boot2docker/server.pem

export "NO_PROXY=192.168.99.100"

重启Docker服务

$ sudo /etc/init.d/docker restart

有时候会提示以下错误,可以忽略。如果在连接终端执行 docker info 提示服务未启动的话,可以尝试将虚拟机手动重启一下基本可以解决,我这里用的是virtualbox

Stopping dockerd (3590)
warning: ‘aufs’ is not a supported storage driver for this boot2docker install — ignoring request!

Stopping dockerd (3590)
warning: ‘aufs’ is not a supported storage driver for this boot2docker install — ignoring request!
see https://github.com/boot2docker/boot2docker/issues/1326 for more details
Starting dockerd

另外对于windows上使用Docker Toolbox (VirtualBox)的用户需要注意,当创建一个容器的时候,并指定了端口映射的时候,在宿主本机没有办法通过端口访问容器实例的。因为参数 -p 33065:3306 中的33065指的是VirtualBox中的端口,而非当前宿主机器的端口,此时如果用netstat查看的话,是看不到33065端口的。想访问容器,需要在宿主机器与VirtualBox之间再映射一个端口 33065:33065

至于原因吗,很好理解的,docker运行需要使用Linux内核,于是windows使用VirtualBox 搞了一个linux 虚拟机器,所以对于docer run 命令指定端口的时候,其实指定的是 linux服务器与容器的端口映射关系。

MySQL中group_concat函数详解

函数语法:

group_concat( [DISTINCT]  要连接的字段   [Order BY 排序字段 ASC/DESC]   [Separator ‘分隔符’] )


下面举例说明:

select * from goods;  

+——+——+
| id| price|
+——+——+
|1 | 10|
|1 | 20|
|1 | 20|
|2 | 20|
|3 | 200 |
|3 | 500 |
+——+——+
6 rows in set (0.00 sec)


  1. 以id分组,把price字段的值在同一行打印出来,逗号分隔(默认)

select id, group_concat(price) from goods group by id;  

+——+——————–+
| id| group_concat(price) |
+——+——————–+
|1 | 10,20,20|
|2 | 20 |
|3 | 200,500|
+——+——————–+
3 rows in set (0.00 sec)


2. 以id分组,把price字段的值在一行打印出来,分号分隔 
select id,group_concat(price separator ‘;’) from goods group by id;  

+——+———————————-+
| id| group_concat(price separator ‘;’) |
+——+———————————-+
|1 | 10;20;20 |
|2 | 20|
|3 | 200;500 |
+——+———————————-+
3 rows in set (0.00 sec)


3. 以id分组,把去除重复冗余的price字段的值打印在一行,逗号分隔
select id,group_concat(distinct price) from goods group by id;  

+——+—————————–+
| id| group_concat(distinct price) |
+——+—————————–+
|1 | 10,20|
|2 | 20 |
|3 | 200,500 |
+——+—————————–+
3 rows in set (0.00 sec)


4. 以id分组,把price字段的值打印在一行,逗号分隔,按照price倒序排列
select id,group_concat(price order by price desc) from goods group by id;  

+——+—————————————+
| id| group_concat(price order by price desc) |
+——+—————————————+
|1 | 20,20,10 |
|2 | 20|
|3 | 500,200|
+——+—————————————+
3 rows in set (0.00 sec)