一次sql请求,返回分页数据和总条数
日常搬砖,总少不了需要获取分页数据和总行数。
一直以来的实践是编码两次sql请求,分别拉分页数据和totalCount。
最近我在思考:
常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新,显而易见的优势:
① 能显著减低“客户端和服务器之间的网络往返次数”,提高吞吐量
② 简化客户端代码逻辑
1. mysql 默认单sql请求单语句
mysql客户端选项client_multi_statements
默认为false:会禁止多条 SQL 语句的执行,这意味着在单个sql请求中只有第一条 SQL 语句会被执行,后续的 SQL 语句将被忽略。
这是一种提高数据库操作安全性的方法,可以有效防止 SQL 注入攻击和意外执行多条语句带来的风险。
MySQL客户端支持修改这样的设定 :client_multi_statements=true。
劣势:存在sql注入的风险, 错误处理比较复杂。
(1) go-sql-driver开启多语句支持: multiStatements=true
(2)
SELECT * FROM `dict_plugin` limit 20 ,10;
SELECT count(*) as totalCount from `dict_plugin`;
将会形成2个数据集,golang的实践如下:
results, err = p.Query(querystring)for results.Next() {err = results.Scan(&...)}if !results.NextResultSet() {log.ErrorF(ctx, "expected more result sets: %v", results.Err())}for results.Next() {err = results.Scan(&totalCount)}
既然提到了开启client_multi_statements 有sql注入的风险,我们就展开聊一聊。
2. sql注入
我们先看下sql注入的原理:
有这样的业务sql:
var input_name string
query: = "select * from user where user_name='" + input_name+"'"
sql.Query(query)
如果从界面输入的input_name
="janus';delete from user; --",
会形成恶意sql:select * from user where user_name='janus';delete from user; --' 。
这个时候,客户端的client_multi_statements默认值为false
就能于水火之间挽救数据库:执行第一个sql之后,后面的恶意sql都不会执行。
由此可知,client_multi_statements=false
,确实可以显著降低sql注入的风险,但是还是没有办法避免单sql注入, 比如从界面密码框注入' OR '1'='1
会绕过登录认证。
query:= "select * from user where user='" + input_name +"' and pwd='" +input_pwd +"'" select * from user where user='xxx' and pwd='' OR '1'='1' -- 会绕过认证逻辑。
3. 参数化查询防止sql注入
参数化查询可以防止sql注入风险[1]
// Correct format for executing an SQL statement with parameters.var queryStr = "SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?"
var args string = "55 union select * from `dict_plugin_Test`"rows, err := db.Query(queryStr, args)
sql查询内部会利用提供的参数1创建预编译语句, 在运行时,实际是执行带参的预编译后的语句。
在服务器收到的查询日志如下:
2024-08-13T08:07:18.922818Z 26 Connect root@localhost on tcinfra_janus_sharing using TCP/IP
2024-08-13T08:07:18.924525Z 26 Prepare SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?
2024-08-13T08:07:18.924671Z 26 Execute SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = '55 union select * from `dict_plugin_Test`'
2024-08-13T08:07:18.925273Z 26 Close stmt
判断mysql数据库开启了查询日志:show variables like '%general_log%';
打开sql查询日志的开关:set global general_log = on; 。
注意:参数占位符根据DBSM和驱动而有所不同,例如,Postgres 的pq驱动程序接受占位符形式是 $1而不是?。
3.1 预编译语句
数据库预编译后, SQL语义结构和数据分离,这样即使输入包含恶意代码,它也只会被当作数据处理,不会影响已经被解析固定的SQL语义结构。
预编译语句包含两次 sql交互:
① 预编译阶段(Prepare Phase):
客户端向服务器发送一个包含 SQL 语句(带有参数占位符)的请求。
sql服务器对SQL 语句进行语法和语义检查,然后对其进行预编译,并为其分配一个标识符(Statement ID)。
服务器返回一个确认响应,表示预编译语句已经成功准备好。
② 执行阶段(Execute Phase):
客户端发送执行请求,包含预编译语句的标识符和实际参数值。
服务器将参数值绑定到预编译语句的占位符上,然后执行该语句。
服务器返回执行结果(如结果集或影响的行数)。
图示如下:
客户端 服务器| ||----预编译语句(Prepare)------>|| ||<-------确认响应(OK)----------|| ||---执行语句(Execute) + 参数---->|| ||<----------查询结果-------------|
我们了解到预编译语句,将SQL语义和数据分离,通过两次sql交互(在预编译阶段固定了sql语义结构), 有效防止了SQL注入攻击, 另一方面,预编译语句在重复执行某一sql语句时确实有加快查询结果的效果。
golang的预编译的写法与常规的sql查询类似:
stmt, err := p.Prepare("SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?")
var args string = "55 union select * from `dict_plugin_Test`"
results, err := stmt.Query(args)
if err != nil {fmt.Printf("query fail: %v", err)return err
}
defer stmt.Close()for results.Next() {err = results.Scan(.....)......
}
btw, C# 其实也支持预编译语句版本的sqlCommand:SqlCommand.Prepare()
总结
本文通过我们最初开始数据库编程时的一个实践, 提出在【一次sql请求中执行多次sql查询】的猜想;
了解到client_multi_statements= false 确实能避免一部分sql注入风险;
之后落地到sql注入的原理, 给出了参数化查询(预编译语句)能防止sql注入的核心机制。
参考资料
[1]
参数化查询可以防止sql注入风险: https://go.dev/doc/database/sql-injection
自古以来,同步/异步都是八股文第一章
async/await 贴脸输出,这次你总该明白了
流量调度、微服务可寻址性和注册中心
Go语言正/反向代理的姿势
两将军问题和TCP三次握手
"家长进校园"之《计算机和人工智能》
点“赞”戳“在看”
相关文章:

一次sql请求,返回分页数据和总条数
日常搬砖,总少不了需要获取分页数据和总行数。 一直以来的实践是编码两次sql请求,分别拉分页数据和totalCount。 最近我在思考: 常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新,显而易见的优势: ① 能…...
2.5 pyautogui 实现微信自动回复
第四节:实战微信自动回复 课程目标 学习如何通过pyautogui完成微信自动回复 课程内容 编码实现 import pyautogui as pg import time from pyautogui import ImageNotFoundException import pyperclip from cnocr import CnOcr import random ocr CnOcr() msg…...

观存储历史,论数据未来
数据存储 这几天我反复观看了腾讯云社区的《中国数据库前世今生》纪录片,每次的感受都大相径庭。以下是我在这段时间里对纪录片的两个不同感想,希望感兴趣的小伙伴们也能去观看一番。 一个是关于国产数据库的发展趋势的探讨:https://blog.c…...
linux:对目录的操作
一、对目录操作 1,打开目标目录 2.读取目录,, 3.关闭目录 目录 当文件看,只不过操作函数和操作文件函数不一样。 *1.opendir DIR *opendir(const char *name); 功能:打开一个目录获得一个目录流指针 参数:name:目录名 返回值…...

详解Redis 高可用的方式 Redis Cluster
Redis 高可用方式 Redis 提供了多种高可用性方案,主要包括以下几种方式: 主从复制(Replication) 主从复制是最基本的高可用性方案,通过将数据从一个主节点复制到多个从节点来实现数据的冗余和读写分离。主节点负责所…...
$clog2(1)=0
项目场景: 写ip 时, 使用参数化的方式实现2w1r 时,出现计算读返回index 时,减下溢! 问题描述 verilog中会使用parameter 参数化,例如使用dpth 和$clog2(dpth)addr 。 常见的写法没有什么问题。 module …...

开发学习日记1
用这个系列博客记录下学习开发的一些小收获 git的使用: 说来惭愧,学到了大二,git的使用还是一团糟,记录一下如何使用git进行团队合作开发 当要加入其他人的项目时首先你要创建自己的分支(克隆一下其他分支ÿ…...

孙宇晨领航波场TRON:引领数字资产迈向崭新纪元
在风起云涌的数字资产领域,孙宇晨这个名字始终与创新、突破和引领紧密相连。作为波场TRON的创始人,他不仅是一位远见卓识的领导者,更是推动数字资产迈向新纪元的坚实力量。 自波场TRON诞生以来,孙宇晨便以其敏锐的洞察力…...
python运维(twenty-four day)
一、python基础 1、环境python2、python3 [rootpython ~]# yum list installed | grep python #检查是否有python包 [rootpython ~]# yum list installed | grep epel #检查是否有epel包 [rootpython ~]# yum -y install epel-release [rootpython ~]# yum -y instal…...
Eureka原理实践
1. 简介 1.1. 概述 Eureka是Netflix开源的一个服务注册与发现框架,它在微服务架构中扮演着至关重要的角色。 Eureka由两个核心组件组成: Eureka Server(服务注册中心):负责存储、管理和提供服务实例信息,如服务名、IP地址、端口号等。Eureka Server通常采用集群部署以保…...
Ant-Design-Vue快速上手指南+排坑
1. 简介 1.1. 概述 Ant-Design-Vue是由阿里巴巴开源的一个基于Vue.js框架的企业级UI设计语言。它旨在帮助开发者构建设计优雅、体验流畅的企业级应用。Ant-Design-Vue提供了一系列高质量的Vue组件,包括表单、表格、布局、导航、图标等,可以帮助开发者快速搭建应用程序界面。…...

mysql5.7安装
1.创建一个software文件 2.先下载mysql的repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 3安装源包 rpm -ivh mysql-community-release-el7-5.noarch.rpm 可能会报错 改成命令 rpm -ivh mysql-community-release-el7-5.noarch.rpm --nodeps…...

UE开发中的设计模式(三) —— 对象池模式
在FPS游戏中,射击会生成子弹,在命中敌人后子弹会被销毁,那么会导致子弹对象频繁地创建和销毁,会造成运行效率降低且会产生内存碎片问题,而对象池模式可以很好地解决这个问题。 文章目录 问题提出概述问题解决总结 问题…...
Mocha测试框架:JavaScript自动化测试的瑞士军刀
在JavaScript开发中,自动化测试是确保代码质量和可靠性的关键环节。Mocha是一个广泛使用的JavaScript测试框架,它支持多种断言库,允许开发者编写简洁、灵活的测试用例。Mocha特别适用于Node.js环境,但也可以在浏览器中运行。本文将…...
flask实现Streaming内容传输
当传输大量内存,以至于超出内存大小,一般http服务器会报500错误,这时可以使用Streaming流的方式来传输内容,类似ChatGPT和视频流那样的输出方式,flask里要用到生成器和直接响应。 from flask import stream_with_cont…...

seata的使用(SpringBoot项目整合seata)
文章目录 1、解压 seata-server-1.7.1.zip2、启动 双击 seata-server.bat3、启动 seata 控制台用户界面4、所有分布式事务相关数据库要有undo-log5、项目引入seata依赖6、项目添加seata配置7、代码实现: 1、解压 seata-server-1.7.1.zip 2、启动 双击 seata-server.…...

docker容器和宿主机网络不通
防火墙未开启,检查网络配置无异常 解决: [rootlocalhost ~]# vim /etc/sysctl.confnet.bridge.beidge-nf-call-iptables 1 net.bridge.beidge-nf-call-ip6tables 1[rootlocalhost ~]# sysctl -p [rootlocalhost ~]# systemctl restart docker 如果网…...
编程学习之旅:高效记录与整理笔记的艺术
引言:知识的海洋与导航的灯塔 在编程的浩瀚星空中,每一位学习者都像是勇敢的航海家,驾驶着知识的帆船,在无尽的信息海洋中探索未知的领域。然而,这片海洋既充满了机遇,也潜藏着挑战。信息的过载、知识的碎…...

dev c++中,在C++11模式下编译带M_PI宏的文件报错的解决办法
一、问题描述 当使用C11的模式,编译引用了math库中的M_PI的源文件时,报M_PI未声明的错误。 二、问题原因 因为M_PI是GNU扩展的宏,它不属于C11的标准,而-stdc11,表示以C11的标准进行编译,因此会产生以上问…...
【ubutnu24.04】k8s部署2:摸索修复问题
1.30.0 一直init失败有人说版本兼容问题重新安装了最新的1.31.0 版本kubeadm init 仍旧失败。安装依赖项 sudo apt-get install -y apt-transport-https ca-certificates curl gpgroot@PerfSvr:/home/zhangbin/perfwork/k8sadmin# sudo apt-get install -y apt-transport-https…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...