从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]在sqlserver中,几年之前就注意到一个现象:sqlserver中对一个大表创建索引或者rebuild索引的过程中,会引起内存剧烈的动荡,究其原因为何,这种现象到底正不正常,是不是sqlserve

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法

在sqlserver中,几年之前就注意到一个现象:sqlserver中对一个大表创建索引或者rebuild索引的过程中,会引起内存剧烈的动荡,究其原因为何,这种现象到底正不正常,是不是sqlserver内存管理存在缺陷?
另外,最近刚好想到跟MySQL对比一下类似操作引起的内存变化,测试MySQL会不会有类似问题,这里就简单写个代码验证一下这个问题。

 

数据库是一个非常依赖内存资源的软件系统,通过缓存数据(索引)到内存中,来改善数据物理访问的性能问题,

但是内存往往又不是无限大,或者足以容纳所有相关数据的容量,因此就存在内存页面的淘汰问题。
内存页的淘汰算法,多数是遵循LRU算法,LRU是Least Recently Used的缩写,也即遵循“最近做少使用”的原则,选择最近最久未使用的页面予以淘汰。
这个算法表面上看起来没什么问题,如果有注意观察过在一台相对稳定的服务器上,给大表创建索引的过程,就会发现,整个过程中,buffer pool会发生剧烈的动荡,创建索引的表会迅速侵入内存,挤走内存中原本的缓存。

由于SQLServer作为商业数据库,有关于它的页面淘汰算法的研究较少,仅仅是指导一个大概是遵循LRU的原则的,但是有没有在LRU的基础上进行改进或者优化,就不得而知,
但是SQLServer究竟有没有对该问题做改进或者优化?这里从一个索引的创建来管中窥豹,从侧面验证一下这个算法。

这里需要借助SQLServer中的一个变量值:Page life expectancy,
相信稍微熟悉SQLServer一点的人应该都知道这个参数代表的意义:内存页面的平均滞留时间,如果内存页面不断地被置换出去,这个值将会维持不变或者变得更小,因为新载入内存的页面在内存中停留的时间是较短的。
不知道有没有人注意过,在一台内存相对稳定的服务器上,对大表(1000W+)创建索引的时候,Page life expectancy这个变量值会急转直下,这说明了什么?
大表创建索引粗略讲是读数据,然后写数据(索引树)的过程,这个过程中必然将相关的表读入内存,那么读入内存之后,他有没有淘汰内存中已有的数据?|
如果有,这明显是不合理的,创建索引只是创建索引,目的不是把内存中已有的热数据挤走,但是它还真的给内存中已有的热数据给挤走了。

反观MySQL(Innodb引擎),Redis等数据库,都是基于优化的LRU或者LFU的原则淘汰页面。
MySQL甚至可以人为地去调整这个LFU算法的一些参数值(innodb_old_blocks_pct,innodb_old_blocks_time),来达到优化内存淘汰的目的。
MySQL中虽然没有类似于PLE的参数,但是可以从其他参数来间接推断,如果发生同样的操作,相关的表会不会挤走内存中的热数据.
这里基于MySQL information_schema.innodb_buffer_pool_stats这张表来作分析,其中这个表有两个字段,pages_made_young, pages_not_made_young ,这两个的变化代表这个新进入内存中的页面冷热变化情况。
同样的道理,如果内存中充斥着大量的热点数据,在对一个大表创建索引的过程中,并不希望因为创建索引而把热点数据挤出内存,究竟是不是这样的,同样在创建索引的过程中,观察一下这两个值的变化情况就可以了。

 
测试方法

这里通过循环,以5秒为间隔,连续输出sqlserver中的Page life expectancy这个变量的值,以及MySQL中的pages_made_young和pages_not_made_young。

#coding=utf-8
import threading
import pymssql
import pymysql
from time import ctime,sleep
import datetime
import time

mssql_conn_conf = {"host": "***.***.***.***", "port": 1433,  "db": "master"}
mysql_conn_conf = {"host": "***.***.***.***", "port": 3306, "user": "root", "password": "***", "db": "information_schema"}

def mssql_ple():
    conn = pymssql.connect(host=mssql_conn_conf["host"], port=mssql_conn_conf["port"], database=mssql_conn_conf["db"])
    cursor = conn.cursor()
    try:
        cursor.execute(" select cntr_value from sys.dm_os_performance_counters where object_name = "MSSQL$SQL2014:Buffer Manager" and counter_name = "Page life expectancy" ")
        row = cursor.fetchone()
        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")+ "------>"+str(row[0]))
    except pymssql.Error as e:
        print("mysql execute error:", e)
    cursor.close()
    conn.close()

def mysql_memory():
    conn = pymysql.connect(host=mysql_conn_conf["host"], port=mysql_conn_conf["port"], database=mysql_conn_conf["db"],user=mysql_conn_conf["user"],password = mysql_conn_conf["password"])
    cursor = conn.cursor()
    try:
        cursor.execute("""
                            SELECT 
                                SUM(pages_made_young) AS total_pages_made_young,
                                SUM(pages_not_made_young) AS total_pages_not_made_young
                            FROM
                            (
                                SELECT  pages_made_young, pages_not_made_young 
                                FROM information_schema.innodb_buffer_pool_stats
                            )t;
                        """)
        row = cursor.fetchone()
        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")+ "------>"+"made_young:"+str(row[0])+" not_made_young:"+str(row[1]))
    except pymssql.Error as e:
        print("mysql execute error:", e)
    cursor.close()
    conn.close()


if __name__ == "__main__":
    while 1>0:
        mysql_memory()
        time.sleep(5)

代码100分

SQLServer中的PLE变化测试

其实很容易观察,对于一台没有负载的服务器,因为没有新的内存页面载入内存,它的Page life expectancy值是递增的的,这个变量的单位是秒,间隔一秒,这个值会自动加1。
一旦有新的页面载入内存,如果内存已经被用完,随着内存中已有的页面淘汰出去,这个值是会自动递减的,或者出现断崖式的下降。
这里运行上述脚本,打印出来当前服务器的Page life expectancy值,稍等一段时间后,在某个大表上创建出一个索引,再观察这个值的变化情况,

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]

step1,对DB01库上的表进行反复的查询,使其载入内存(最近较多使用),左图是DB01库占用的内存情况,
step2,在DB02库上对一张大表创建索引,此过程中中会发现创建索引的表会迅速将已换成的数据挤出内存

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]

 

MySQL中的pages_made_young和page_not_made_young测试

因笔者事前重启过实例,因此made_young的值很小,关键要看,在某个大表上创建索引的过程中是不是会大量的made_young就行了。
这里可以看到,在创建索引开始之后,会出现大量的not_made_young,实际上这种效果是预期的,仅仅是创建索引,而不是顺带让当前这个大表的数据挤走热点数据(并没有大批量的made_young)
从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]

这里也给出在db02上创建索引前后两个库占用的内存情况,虽然db02在其某个大表上创建索引之后占用了一定量的内存,但是这部分内存并非热数据,是随时可以被挤出buffer pool的,因为他们没有page_made_young
step1,对db01库上的表进行的多次查询,使其载入内存,左图是db01库占用的内存情况,
step2,在db02库上对一张大表创建索引,此过程中中会发现不断地有大量的page_not_made_young,另外原本的db01库的内存并没有被大量的挤出。

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法[亲测有效]

 

总结
以个人浅薄的经历以及测试过程,发现sqlserver的内存管理,与MySQL相比,一直停留在小学二年级的水平,其buffer pool管理本身的算法就存在问题,又是一个黑盒,也没有人为可以调整的可能性。

 

sqlserver再不加油,真的就没人用了……

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/8887.html

(0)
上一篇 2023-02-20
下一篇 2023-02-20

相关推荐

  • 数据库自学笔记(2)-[通俗易懂]

    数据库自学笔记(2)-[通俗易懂]1.HAVING和WHERE: WHERE 和 HAVING 的作用对象不一样。WHERE作用于基本表或视图,挑出满足条件的元组。HAVING作用于组(group),一般配合GROUP BY 使用。

    2023-01-23
    149
  • 优化计算:用Python编写arccos函数与Matlab对比

    优化计算:用Python编写arccos函数与Matlab对比在数学运算中,反三角函数是常见的运算之一。Python内置了arccos函数用于计算反余弦值。然而,Python自带的arccos函数速度比较慢,使用一些优化技巧可以大幅提升计算速度。

    2024-03-28
    68
  • 利用jsarray.filter进行数组过滤

    利用jsarray.filter进行数组过滤jsarray.filter是JavaScript中一个非常有用的函数,它可以根据指定的条件从数组中筛选出符合条件的元素,返回一个新的数组。今天我们就来详细介绍一下如何使用jsarray.filter进行数组过滤。

    2024-06-02
    58
  • PostgreSQL 15 正式发布!工作负载、开发体验等方面有显著提升

    PostgreSQL 15 正式发布!工作负载、开发体验等方面有显著提升2022 年 10 月 13 日- PostgreSQL 全球开发组今天宣布发布 PostgreSQL 15,这是世界上最先进的开源数据库的最新版本。

    PostgreSQL 15 建立在最近版本

    2023-06-10
    163
  • mysql标识列和事务 – 自律即自由[亲测有效]

    mysql标识列和事务 – 自律即自由[亲测有效]1 #标识列 2 /* 3 又称为自增长列 4 含义:可以不用手动的插入值,系统提供默认的序列值 5 6 7 特点: 8 1、标识列必须和主键搭配吗?不一定,但要求是一个key 9 2、一个表可以有几

    2023-03-20
    147
  • Python中使用XPath选择器的简单示例

    Python中使用XPath选择器的简单示例随着互联网的快速发展,网络爬虫技术也得到了广泛应用。数据挖掘和爬虫技术成为了不少企业和研究人员不可或缺的工具。而XPath选择器是在Python爬虫技术中,常用的一种方式。XPath依靠路径表达式来选取HTML或XML文档的节点,用于高效地提取所需的数据。

    2024-05-18
    75
  • mysql的视图_mysql数据库笔记

    mysql的视图_mysql数据库笔记1 #视图 2 /* 3 含义:虚拟表,和普通表一样使用 4 mysql5.1版本出现的新特性,是通过表动态生成的数据 5 6 比如:舞蹈班和普通班级的对比 7 创建语法的关键字 是否实际占用物理空间

    2023-03-26
    199
  • SQL — 基本书写规则[通俗易懂]

    SQL — 基本书写规则[通俗易懂] 1、SQL 语句要以分号;结尾 2、SQL 语句不区分大小写 SQL 不区分关键字的大小写。例如,不管写成 SELECT 还是 select,解释都是一样的。表名和列名也是如此。 提示:关键…

    2023-03-27
    171

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注