MySQL 5.6新特性MRR
**一、什么是MRR **
MMR全称是Multi-Range Read,是MYSQL5.6优化器的一个新特性,在MariaDB5.5也有这个特性。优化的功能在使用二级索引做范围扫描的过程中减少磁盘随机IO和减少主键索引的访问次数。是优化器将随机 IO 转化为顺序 IO 以降低查询过程中 IO 开销的一种手段。(参考: https://blog.csdn.net/caomiao2006/article/details/52205177)
**二、MRR和没有MRR的区别 **
给出一个简单的例子,在innodb表执行下面的查询:
SELECT non_key_column FROM tbl WHERE key_column=x
在没有MRR的情况下,它是这样得到结果的:
1. select key_column, pk_column from tb where key_column=x order by key_column —> 假设这个结果集是t
2. for each row in t ; select non_key_column from tb where pk_column = pk_column_value。(回表)
在有MRR的情况下,它是这样执行的:
1. select key_column, pk_column from tb where key_column
= x order by key_column
—> 假设这个结果集是t
2. 将结果集t
放在buffer
里面(直到read_rnd_buffer_size
这个buffer满了),然后对结果集t按照 pk_column
排序 —> 假设排序好的结果集是t_sort
3. select non_key_column
from tb where pk_column
in (select pk_column
from t_sort
)
By admin
read moreMySQL中select中的for update 的用法
注意: FOR UPDATE 只能用在事务区块(BEGIN/COMMIT)中才有效。
有时候我们会看到一些select语句后面紧跟一句for update,表示手动加锁的意思,这里我们就介绍一下对for update的理解。相对另一种手动加锁方法lock in share mode 的区别见: https://blog.csdn.net/liangzhonglin/article/details/65438777。
for update:IX锁(意向排它锁),即在符合条件的rows上都加了排它锁,其他session也就无法在这些记录上添加任何的S锁或X锁。如果不存在一致性非锁定读的话,那么其他session是无法读取和修改这些记录的,但是innodb有非锁定读(快照读并不需要加锁),for update之后并不会阻塞其他session的快照读取操作,除了select …lock in share mode和select … for update这种显示加锁的查询操作。
lock in share mode:是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁,这样的话,其他session可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的session执行完成(否则直接锁等待超时)。
for update是一个意向排它锁,也就是说对于select … for update 这条语句所在的事务中可以进行任何操作(锁定的是记录,这里指主键id=1的这条记录),但其它事务中只能读取(对这条id=1的记录),不能进行update更新操作。
一般用在并发场景下,如双11的时候商品数量的更新,如果不添加for update的话,则会出现商品数量被多减的bug。
为了更加方便理解,我们举例说明(事务隔离级别为 RR,这里表tb的id为主键)
事务A:
start transaction;
select * from tb where id=1 for update;
update tb set product_num=product_num-1 where id=1;
此时另一个事务B执行同样的程序语句:
start transaction;
// 下面此时会被阻塞,直到事务A提交或者回滚
select * from tb where id=1 for update;
update tb set product_num=product_num-1 where id=1;
对于其它主键值非1的不存在这种情况,只要两个事务操作的不是同一条记录就可以执行成功。
推荐阅读: https://blog.csdn.net/u011957758/article/details/75212222
By admin
read moremysql explain 中key_len的计算方法
建议先阅读这篇文章: http://hidba.org/?p=404
下面我们只对其中提到的做一个验证。
(1).索引字段的附加信息:可以分为变长和定长数据类型讨论,当索引字段为定长数据类型,比如char,int,datetime,需要有是否为空的标记,这个标记需要占用1个字节;对于变长数据类型,比如:varchar,除了是否为空的标记外,还需要有长度信息,需要占用2个字节;
(备注:当字段定义为非空的时候,是否为空的标记将不占用字节)
(2).同时还需要考虑表所使用的字符集,不同的字符集,gbk编码的为一个字符2个字节,utf8编码的一个字符3个字节, utf8mb4 编码则是4个字节;
每种MySQL数据类型的定义参考:
下面我们以定长数据类型准,变长数据类型请自行测试。
一、数据索引类型允许为null的情况:
表结构:
CREATE TABLE `tb` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`sid` smallint(5) DEFAULT NULL,
`gid` smallint(5) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_common` (`sid`,`gid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
执行分析语句:
mysql> EXPLAIN select * from tb where sid=1 and gid=5;
+----+-------------+-------+------------+------+---------------+------------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------------+---------+-------------+------+----------+-------------+
| 1 | SIMPLE | tb | NULL | ref | idx_common | idx_common | 6 | const,const | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
发现用到了复合索引idx_common,这时复合索引的两个字段全部用到了,而由于 smallint 数据类型占用字节为两个字节, 属于定长类型,且允许为null,所以key_len长度计算公式为 (2 + 1) + (2 + 1) = 6 下面我们将两个字段全部禁止null看一下计算值
By admin
read moreRedis单线程架构
1 单线程模型
Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型。
2 单线程模型每秒万级别处理能力的原因
(1)纯内存访问。数据存放在内存中,内存的响应时间大约是100纳秒,这是Redis每秒万亿级别访问的重要基础。
(2)非阻塞I/O,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了时间,不在I/O上浪费过多的时间。
(3)单线程避免了线程切换和竞态产生的消耗。
(4)Redis采用单线程模型,每条命令执行如果占用大量时间,会造成其他线程阻塞,对于Redis这种高性能服务是致命的,所以Redis是面向高速执行的数据库。
redis为什么要设计成单线程: https://hacpai.com/article/1470967571415
By admin
read moreRedis中的锁
单Redis实例锁: http://www.redis.cn/commands/setnx.html
分布式锁: http://redis.cn/topics/distlock.html(提供各种开发语言提供的库)
By admin
read moreMYSQL之ICP、MRR、BKA
Index Condition Pushdown(ICP)
Index Condition Pushdown (ICP)是mysql使用索引从表中检索行数据的一种优化方式。
ICP原理
禁用ICP,存储引擎会通过遍历索引定位基表中的行,然后返回给MySQL Server层,再去为这些数据行进行WHERE后的条件的过滤。
开启ICP,如果部分WHERE条件能使用索引中的字段,MySQL Server 会把这部分下推到存储引擎层,存储引擎通过索引过滤,把满足的行从表中读取出。ICP能减少引擎层访问基表的次数和MySQL Server 访问存储引擎的次数。
ICP的目标是减少从基表中全纪录读取操作的数量,从而降低IO操作
对于InnoDB表,ICP只适用于辅助索引。
ICP标识
当使用ICP优化时,执行计划的Extra列显示Using indexcondition提示
相关参数
optimizer_switch="index_condition_pushdown=on”;
适用场景
#辅助索引INDEX (zipcode
, lastname
, firstname
).
SELECT * FROM peopleWHERE zipcode='95054'AND lastname LIKE '%etrunia%'AND address LIKE '%Main Street%';
People表有个二级索引 INDEX (zipcode, lastname, firstname)
,用户只知道某用户的zipcode
,和大概的 lastname
、address
,此时想查询相关信息。
**若不使用ICP:**则是通过 二级索引
中 zipcode
的值去基表取出所有 zipcode='95054'
的数据,然后 server
层再对 lastname LIKE '%etrunia%'AND address LIKE '%Main Street%';
进行过滤
**若使用ICP:**则 lastname LIKE '%etrunia%'AND address LIKE '%Main Street%'
的过滤操作在二级索引
中完成,然后再去基表取相关数据
使用限制
l 只支持 select 语句
By admin
read morePHP连接mysql8.0出错“SQLSTATE[HY000] [2054] The server requested authentication method unknown to”的解决办法
错误信息
SQLSTATE[HY000] [2054] The server requested authentication method unknown to…
这个错可能是mysql默认使用 caching_sha2_password
作为默认的身份验证插件,而不再是 mysql_native_password
,但是客户端暂时不支持这个插件导致的。 官方文档说明
In MySQL 8.0, caching_sha2_password is the default authentication plugin rather than mysql_native_password. For information about the implications of this change for server operation and compatibility of the server with clients and connectors, see caching_sha2_password as the Preferred Authentication Plugin.
在MySQL 8.0中,caching_sha2_password是默认的身份验证插件,而不是mysql_native_password。有关此更改对服务器操作的影响以及服务器与客户端和连接器的兼容性的信息,请参阅caching_sha2_password作为首选身份验证插件。
解决方法一:修改MySQL全局配置文件
编辑 my.cnf
文件,更改默认的身份认证插件。
$ vi /etc/my.cnf
在 [mysqld]
中添加下边的代码
default_authentication_plugin=mysql_native_password
然后重启mysql
$ service mysqld restart
解决方法二:修改密码认证方式
By admin
read more使用Dockerfile构建Swoole+php7环境
FROM php:7.2.7-cli
RUN apt-get update
&& apt-get install -y libmemcached-dev zlib1g-dev
RUN pecl install redis-4.0.1
&& pecl install swoole-4.0.1
&& pecl install memcached-3.0.4
&& pecl install xdebug-2.6.0
&& docker-php-ext- enable redis swoole memcached xdebug
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
CMD [ "php", "-m" ]
构建完环境后,使用方法见: https://blog.haohtml.com/archives/17925
这里推荐另一种更简单的方法 https://github.com/mlocati/docker-php-extension-installer,同时支持多个PHP版本,唯一的不足可能是安装时没有办法指定扩展的版本号或者手动修改脚本文件来完成。
推荐文章: Dockerfile 最佳实践
By admin
read moreMySQL中的查询开销查看方法
MySQL使用基于 成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。在MySQL可以通过查询当前会话的 last_query_cost
的值来得到其计算当前查询的成本。
mysql> select * from t_message limit 10;
...省略结果集
mysql> show status like 'last_query_cost'
+-----------------+-------------+
| Variable_name | Value |
+-----------------+-------------+
| Last_query_cost| 6391.799000 |
+-----------------+-------------+
示例中的结果表示优化器认为大概需要做6391个数据页的随机查找才能完成上面的查询。这个结果是根据一些列的统计信息计算得来的,这些统计信息包括: 每张表或者索引的页面个数、 索引的基数、 索引 和 数据行的长度、 索引的分布 情况等等。
有非常多的原因会导致MySQL选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)、MySQL认为的最优跟我们想的不一样(我们希望执行时间尽可能短,但MySQL值选择它认为成本小的,但成本小并不意味着执行时间短)等等。
这里last_query_cost的值是 io_cost 和 cpu_cost 的开销总和,它通常也是我们评价一个查询的执行效率的一个常用指标。
(1)它是作为比较各个查询之间的开销的一个依据。
(2)它只能检测比较简单的查询开销,对于包含子查询和union的查询是测试不出来的。
(3)当我们执行查询的时候,MySQL会自动生成一个执行计划,也就是query plan,而且通常有很多种不同的实现方式,它会选择最低的那一个,而这个cost值就是开销最低的那一个。
(4)它对于比较我们的开销是非常有用的,特别是我们有好几种查询方式可选的时候。
By admin
read more一个docker-compose微服务脚本,自用
容器为swoole+php7
docker-compose.yml
version: '3.6'
services:
redis:
image: redis
web:
image: cfanbo/swoole4_php7:v1
depends_on:
- redis
links:
- redis
volumes:
- /Users/sxf/sites/msgserve:/usr/src/myapp
command: "php /usr/src/myapp/src/wx_push_server.php start"
对于 wx_push_server.php文件里redis的主机地址应该写成docker-compose配置文件里的容器服务名(redis)
By admin
read more