DES加密算法实现
好久没写博客了,正好趁着实现网络工程与安全的DES算法的功夫,把代码发上来。
DES的介绍可见:DES加密
原理不赘述了。。实在太多,其实就是一个形式化算法,按部就班的实现就可以,只不过有些繁琐,我写了3个晚上 = =。
主要实现了利用DES算法对一个文本文档加密,然后再将其内容解密。
程序截图
明文和密钥
加密然后解密
密文和解密后文本
代码
//该程序实现了文本文档的DES加密和解密。 #include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std; //IP置换表
char IP[] = {
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,}; //IP^-1 置换表
char IP_1[] = {
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,}; //扩展置换表E
int E[] = {, , , , , ,
, , , , , ,
, ,,,,,
,,,,,,
,,,,,,
,,,,,,
,,,,,,
,,,,, }; //置换函数P
int P[] = {,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,}; //S盒
int S[][][] ={
{{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}, {{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,}}};
//置换选择1
int PC_1[] = {,,,,,,,
,,,,,,,
,,,,,,,
,,,,,,,
,,,,,,,
,,,,,,,
,,,,,,,
,,,,,,}; //置换选择2
int PC_2[] = {,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,}; //对左移次数的规定
int LS[] = {,,,,,,,,,,,,,,,,}; //存放子密钥
unsigned __int64 K[]; //将含64个二进制字符的字符串转换为二进制形式存储在long long int中
unsigned __int64 str2bit(unsigned char s[])
{
unsigned __int64 bin = ;
int i;
for(i=;i<;i++){
bin = bin<< | (s[i]-''); //s[i]>>j & 1 表示取s[i]的第j位
}
return bin;
} //将含8位字符串转换为二进制形式存储在long long int中,例ABCDEFGH转换为64位二进制数
unsigned __int64 bstr2bit(unsigned char s[])
{
unsigned __int64 bin = ;
int i,j;
for(i=;i<;i++){
for(j=;j<;j++){
bin = bin | ( (unsigned __int64)((s[i]>>j)%) << (j+*(-i)) ); //将s[i]的第j位(低位开始)取出放到bin的对应位置
}
}
return bin;
} //将 unsigned __int64 形式的按十六进制串输出(64位有效的二进制数串)
void DisplayHex64(unsigned __int64 bin)
{
int i,t=;
for(i=;i>=;i--){
t = t<< | ((bin>>i) & );
if(i%==){
if(<=t && t<=)
printf("%d",t);
else if(<=t && t<=)
printf("%c",+t-);
t=;
}
if(i%==)
printf(" ");
}
printf("\n");
}
//将 unsigned __int64 形式的按十六进制串输出(48位有效的二进制数串)
void DisplayHex48(unsigned __int64 bin)
{
int i,t=;
for(i=;i>=;i--){
t = t<< | ((bin>>i) & );
if(i%==){
if(<=t && t<=)
printf("%d",t);
else if(<=t && t<=)
printf("%c",+t-);
t=;
}
if(i%==)
printf(" ");
}
printf("\n");
}
//将 unsigned __int64 形式的按十六进制串输出(32位有效的二进制数串)
void DisplayHex32(unsigned __int64 bin)
{
int i,t=;
for(i=;i>=;i--){
t = t<< | ((bin>>i) & );
if(i%==){
if(<=t && t<=)
printf("%d",t);
else if(<=t && t<=)
printf("%c",+t-);
t=;
}
if(i%==)
printf(" ");
}
printf("\n");
}
//将 unsigned __int64 形式的按十六进制串输出(28位有效的二进制数串)
void DisplayHex28(unsigned __int64 bin)
{
int i,t=;
for(i=;i>=;i--){
t = t<< | ((bin>>i) & );
if(i%==){
if(<=t && t<=)
printf("%d",t);
else if(<=t && t<=)
printf("%c",+t-);
t=;
}
if(i%==)
printf(" ");
}
printf("\n");
} //将 unsigned __int64 形式的二进制串和十六进制输出(64位有效的二进制数串)
void DisplayBin64(unsigned __int64 bin)
{
int i;
for(i=;i>=;i--){
printf("%d",(bin>>i) %);
}
printf("\n");
DisplayHex64(bin);
} //将 unsigned __int64 形式的二进制串和十六进制输出(48位有效的二进制数串)
void DisplayBin48(unsigned __int64 bin)
{
int i;
for(i=;i>=;i--){
printf("%d",(bin>>i) %);
}
printf("\n");
DisplayHex48(bin);
} //将 unsigned __int64 形式的二进制串和十六进制输出(32位有效的二进制数串)
void DisplayBin32(unsigned __int64 bin)
{
int i;
for(i=;i>=;i--){
printf("%d",(bin>>i) %);
}
printf("\n");
DisplayHex32(bin);
} //将 unsigned __int64 形式的二进制串和十六进制输出(28位有效的二进制数串)
void DisplayBin28(unsigned __int64 bin)
{
int i;
for(i=;i>=;i--){
printf("%d",(bin>>i) %);
}
printf("\n");
DisplayHex28(bin);
} /* ------------------------------ 【1】初始置换IP ------------------------------
* 把明文顺序打乱重新排列,置换输出为64位,
* 数据置换后的第一位,第二位分别是原来的58、50位
*/ unsigned __int64 InitReplace(unsigned __int64 bin) //初始置换IP
{
unsigned __int64 t = ;
int i;
for(i=;i<;i++){
t = t | ( (bin>>(-IP[i])) %) << (-i);
}
return t;
} /* ------------------------------ 【2】将置换输出的64位数据分成左右两半 ------------------------------
* 左一半称为L0,右一半称为R0,各32位。
*/ void DivideLR(unsigned __int64 binip,unsigned __int64 &L0,unsigned __int64 &R0) //将置换输出的64位数据分为L0(32位),R0(32位)两部分
{
unsigned __int64 one = ;
L0 = binip >> ;
R0 = binip % (one<<);
} /* ------------------------------ 【3】计算函数的16轮迭代 ------------------------------
* 1~16轮加密迭代
*/ //置换选择1,64->56
unsigned __int64 FunPC_1(unsigned __int64 bink)
{
unsigned __int64 binpc_1 = ;
int i;
for(i=;i<;i++)
binpc_1 |= ( (bink>>(-PC_1[i])) %) << (-i);
return binpc_1;
} //获得第i轮C,D,即Ci-1,Di-1 ==> Ci,Di
void getNextCD(int i,unsigned __int64 &C,unsigned __int64 &D)
{
unsigned __int64 t=,one=;
t = C >> (-LS[i]);
C = (C<<LS[i])%(one<<);
C |= t;
t = D >> (-LS[i]);
D = (D<<LS[i])%(one<<);
D |= t;
} //置换选择2,56->48
unsigned __int64 FunPC_2(unsigned __int64 t)
{
//获得子密钥k
unsigned __int64 k=;
int i;
for(i=;i<;i++)
k |= ( (t>>(-PC_2[i])) %) << (-i);
return k;
} //子密钥生成
void ProduceK(unsigned __int64 bink)
{
unsigned __int64 binpc_1,one=,C=,D=;
binpc_1 = FunPC_1(bink); //置换选择1 //分成2个28位
C = binpc_1 >> ;
D = binpc_1 % (one << ); int i;
for(i=;i<=;i++){
//确定子密钥K[i]
getNextCD(i,C,D);
printf("第%d轮:\n",i);
printf("C[%d] = ",i);
DisplayBin28(C);
printf("D[%d] = ",i);
DisplayBin28(D);
printf("\n"); unsigned __int64 t=;
t = ((C<<) | D)%(one<<);
K[i] = FunPC_2(t); //进行置换选择2,将56位->48位子密钥
}
} //扩展置换E,32->48
unsigned __int64 FunE(unsigned __int64 R)
{
unsigned __int64 t = ;
int i;
for(i=;i<;i++)
t |= ( (R>>(-E[i])) %) << (-i);
return t;
} //S盒代换
unsigned __int64 FunS(unsigned __int64 RX)
{
int x,y,i;
unsigned __int64 one = ,t,RS = ;
for(i=;i<;i++){
t = ( RX >> (*(-i)) ) % (one<<);
x = t>>;
x = (x<<) | (t%);
y = (t<<)%(one<<)>>;
RS = RS<< | S[i][x][y];
}
return RS;
} //置换P运算
unsigned __int64 FunP(unsigned __int64 RS)
{
unsigned __int64 RP = ;
int i;
for(i=;i<;i++)
RP |= ( (RS>>(-P[i])) %) << (-i);
return RP;
} //轮函数f(R,K)
unsigned __int64 f(int i,unsigned __int64 R,unsigned __int64 k)
{
unsigned __int64 RE,RX,RS,RP;
RE = FunE(R); //将32位Ri-1扩展置换为48位
RX = RE ^ k; //将48位RE与子密钥k异或运算
RS = FunS(RX); //S盒代换
RP = FunP(RS); //置换P运算
return RP;
} unsigned __int64 Iteration16(unsigned __int64 L0,unsigned __int64 R0) //进行16次迭代计算
{
int i;
unsigned __int64 Ri = R0;
unsigned __int64 Li = L0;
printf("L0=");
DisplayBin32(L0);
printf("R0=");
DisplayBin32(R0);
printf("\n");
for(i=;i<=;i++){
printf("第%d轮加密:\n",i);
unsigned __int64 t = Ri; //轮函数
Ri = f(i,Ri,K[i]);
printf("f(R%d,K%d)=",i-,i);
DisplayBin32(Ri); //Li-1和轮函数结果异或运算得到Ri
Ri = Li ^ Ri;
//轮函数运算结果即是Li
Li = t; //输出Li和Ri
printf("L%d=",i);
DisplayBin32(Li);
printf("R%d=",i);
DisplayBin32(Ri);
printf("\n");
} return Ri<< | Li;
} unsigned __int64 InverseIteration16(unsigned __int64 L0,unsigned __int64 R0) //进行逆16次迭代计算,为解密过程
{
int i;
unsigned __int64 Ri = R0;
unsigned __int64 Li = L0;
printf("L0=");
DisplayBin32(L0);
printf("R0=");
DisplayBin32(R0);
printf("\n");
for(i=;i>=;i--){
printf("第%d轮加密:\n",-i);
unsigned __int64 t = Ri; //轮函数
Ri = f(i,Ri,K[i]);
printf("f(R%d,K%d)=",i-,i);
DisplayBin32(Ri); //Li-1和轮函数结果异或运算得到Ri
Ri = Li ^ Ri;
//轮函数运算结果即是Li
Li = t; //输出Li和Ri
printf("L%d=",i);
DisplayBin32(Li);
printf("R%d=",i);
DisplayBin32(Ri);
printf("\n");
} return Ri<< | Li;
} /* ------------------------------ 【4】逆初始置换IP^-1 ------------------------------
* 把数据打乱重排,产生64位密文。
*/ unsigned __int64 InverseInitReplace(unsigned __int64 bin) //进行逆初始置换IP^-1
{
unsigned __int64 t = ;
int i;
for(i=;i<;i++){
t = t | ( (bin>>(-IP_1[i])) %) << (-i);
}
return t;
} void printOut(FILE* fo,unsigned __int64 bin)
{
char ans[]={},t=;
int i,cnt=;
for(i=;i>=;i--){
t = t<< | ((bin>>i)%);
if(i%==){
ans[cnt++] = t;
t = ;
}
}
fwrite(ans,,,fo);
} //加密函数
void Encryption()
{
//文件读入
FILE* fm = fopen(".\\Plaintext.txt","rb"); //明文文件
FILE* fk = fopen(".\\key.txt","r"); //密钥文件
FILE* fo = fopen(".\\Ciphertext.txt","wb"); //密文文件 if(fm==NULL){
printf("明文文件input.txt打开失败\n");
}
if(fk==NULL){
printf("密钥文件key.txt打开失败\n");
}
if(fo==NULL){
printf("加密后文件output.txt打开失败\n");
} unsigned char s[]={},key[]={};
int i; fscanf(fk,"%s",key); //读取密钥 while(fread(s,,,fm)){
printf("----------------------------------------------------------------------------\n");
printf("\n");
printf("当前读取的明文串为:\n");
printf("%s\n",s);
unsigned __int64 bin = bstr2bit(s); //将加密串转换为2进制形式
printf("\n");
unsigned __int64 bink = str2bit(key); //将密钥串转换为2进制形式
printf("\n"); //输出
printf("\n");
printf("【输入】\n");
printf("输入的二进制明文串:\n");
DisplayBin64(bin);
printf("输入的二进制密钥串:\n");
DisplayBin64(bink);
printf("\n"); //IP置换
printf("明文初始化置换后:\n");
unsigned __int64 binip = InitReplace(bin);
DisplayBin64(binip);
printf("\n"); //获得L0,R0
printf("将明文分割为L0,R0两部分:\n");
unsigned __int64 L0,R0;
DivideLR(binip,L0,R0); //将binip分割为L0和R0
//输出L0,R0
printf("L0为:");
DisplayBin32(L0);
printf("R0为:");
DisplayBin32(R0);
printf("\n"); //生成16个子密钥,并输出
printf("--------------- 子密钥生成过程 ---------------\n");
memset(K,,sizeof(K));
ProduceK(bink);
printf("生成的16个子密钥:\n");
for(i=;i<=;i++){
printf("K[%d]为 ",i);
DisplayBin48(K[i]);
//printf("\n");
}
printf("\n"); //16次加密迭代
printf("--------------- 16轮加密迭代 --------------- \n");
unsigned __int64 binIter = Iteration16(L0,R0);
printf("迭代加密后的结果:\n");
DisplayBin64(binIter);
printf("\n"); //逆初始置换IP_1
unsigned __int64 binip_1 = InverseInitReplace(binIter);
printf("最后,逆初始置换:\n");
DisplayBin64(binip_1);
printf("\n"); //输出最终加密结果
printf("【加密结果】\n明文 \"%s\" 的DES加密的结果为:\n",s);
DisplayBin64(binip_1); printf("\n");
printf("----------------------------------------------------------------------------\n");
printf("\n"); //将加密结果写到加密文件中
printOut(fo,binip_1); memset(s,,sizeof(s));
}
//文件关闭
fclose(fm);
fclose(fk);
fclose(fo);
} //解密函数
void Decryption()
{
//文件读入
FILE* fm = fopen(".\\Ciphertext.txt","rb"); //密文文件
FILE* fk = fopen(".\\key.txt","r"); //密钥文件
FILE* fo = fopen(".\\DecrypPlaintext.txt","wb"); //解密后文件 if(fm==NULL){
printf("密文文件 Ciphertext.txt 打开失败\n");
}
if(fk==NULL){
printf("密钥文件 key.txt 打开失败\n");
}
if(fo==NULL){
printf("解密后文件 DecrypPlaintext.txt 打开失败\n");
} unsigned char s[]={},key[]={};
int i; fscanf(fk,"%s",key); //读取密钥 while(fread(s,,,fm)){
printf("----------------------------------------------------------------------------\n");
printf("\n");
printf("当前读取的密文串为:\n");
printf("%s\n",s);
unsigned __int64 bin = bstr2bit(s); //将密文串转换为2进制形式
printf("\n");
unsigned __int64 bink = str2bit(key); //将密钥串转换为2进制形式
printf("\n"); //输出
printf("----------------------------------------------------------------------------\n");
printf("\n");
printf("【输入】\n");
printf("输入的二进制密文串:\n");
DisplayBin64(bin);
printf("输入的二进制密钥串:\n");
DisplayBin64(bink);
printf("\n"); //IP置换
printf("密文初始化置换后:\n");
unsigned __int64 binip = InitReplace(bin);
DisplayBin64(binip);
printf("\n"); //获得L0,R0
printf("将密文分割为L0,R0两部分:\n");
unsigned __int64 L0,R0;
DivideLR(binip,L0,R0); //将binip分割为L0和R0
//输出L0,R0
printf("L0为:");
DisplayBin32(L0);
printf("R0为:");
DisplayBin32(R0);
printf("\n"); //生成16个子密钥,并输出
printf("--------------- 子密钥生成过程 ---------------\n");
memset(K,,sizeof(K));
ProduceK(bink);
printf("生成的16个子密钥:\n");
for(i=;i<=;i++){
printf("K[%d]为 ",i);
DisplayBin48(K[i]);
//printf("\n");
}
printf("\n"); //16次加密迭代
printf("--------------- 16轮加密迭代 --------------- \n");
unsigned __int64 binIter = InverseIteration16(L0,R0);
printf("迭代解密后的结果:\n");
DisplayBin64(binIter);
printf("\n"); //逆初始置换IP_1
unsigned __int64 binip_1 = InverseInitReplace(binIter);
printf("最后,逆初始置换:\n");
DisplayBin64(binip_1);
printf("\n"); //输出最终加密结果
printf("【解密结果】\n密文 \"%s\" 的DES解密的结果为:\n",s);
DisplayBin64(binip_1); printf("\n");
printf("----------------------------------------------------------------------------\n"); //将加密结果写到加密文件中
printOut(fo,binip_1); memset(s,,sizeof(s));
}
//文件关闭
fclose(fm);
fclose(fk);
fclose(fo);
} int Menu(int &Case)
{
int in; FILE* fk = fopen(".\\key.txt","r"); //密钥文件
if(fk==NULL){
printf("密钥文件key.txt打开失败\n");
} unsigned char key[];
fscanf(fk,"%s",key); //读取密钥
unsigned __int64 bink = str2bit(key); //将密钥串转换为2进制形式
printf("【%d】当前密钥(key)为:(二进制形式)\n",Case++);
DisplayBin64(bink);
printf("(密钥存储在 key.txt 中)\n");
printf("\n"); printf("请选择,是使用DES算法加密还是解密?\n");
printf("(加密过程:Plaintext.txt => Ciphertext.txt\n");
printf(" 解密过程:Ciphertext.txt => DecrypPlaintext.txt )\n");
printf("[1] 加密\n");
printf("[2] 解密\n");
printf("[0] 退出程序\n"); scanf("%d",&in);
getchar();
fflush(stdin); // 清空缓冲区 if(in== || in== || in==){
return in;
}
else{
printf("输入错误,请重新输入!\n");
printf("\n");
printf("----------------------------------------------------------------------------\n");
printf("\n");
return -;
}
} /*
* 主函数
*/
int main()
{
int Case=,in;
while(){
in = Menu(Case);
switch(in){
case -: //输入错误,进入下次循环
break;
case : //退出程序
printf("谢谢使用 :)\n");
return ;
case : //加密
Encryption();
break;
case : //解密
Decryption();
break;
}
}
return ;
}
Freecode : www.cnblogs.com/yym2013
DES加密算法实现的更多相关文章
- 在.NET Core 里使用 BouncyCastle 的DES加密算法
.NET Core上面的DES等加密算法要等到1.2 才支持,我们可是急需这个算法的支持,文章<使用 JavaScriptService 在.NET Core 里实现DES加密算法>需要用 ...
- 浅谈DES加密算法
一.DES加密算法介绍 1.要求密钥必须是8个字节,即64bit长度 2.因为密钥是byte[8] , 代表字符串也可以是非可见的字节,可以与Base64编码算法一起使用 3.加密.解密都需要通过字节 ...
- JAVA使用DES加密算法加密解密
程序中使用了.properties文件作为参数配置文档,好处是灵活配置各项参数 一旦对数据库的一些参数进行了配置,势必涉及数据库的IP,端口,用户名和密码 properties文件全是unicode编 ...
- 对称密码——DES加密算法
前言 本篇博文将介绍对称密码算法中的DES密码的算法原理与代码实现(Java) DES算法原理 DES加密算法是对称加密算法(加密和解密使用同一个密钥)中的一种,DES也是分组密码,以64位为分组对明 ...
- des加密算法java&c#
项目中用到的数据加密方式是ECB模式的DES加密得到的十六进制字符串.技术支持让写一个.net版的加密算法.这里做一下记录. java版: 16进制使用的是bouncycastle. import c ...
- android和.net webservice中的DES加密算法
也是看了一堆的例子,本身并不会写加密算法,好在只要会用就行了,我们把在app中使用的参数加密,然后在.net端的webservice中进行解密,本身并没有什么问题,但是android下和.net下的d ...
- DES加密算法的C++实现
<信息安全技术>这门课又在讲 DES 加密算法了,以前用纯C写过一次,这次我用 C++ 重新写了一个,写篇文章以备后用.本文介绍了 DES 算法加密的大致步骤和整体流程. 一.DES算法原 ...
- .net中DES加密算法研究
/// <summary> /// DES加密算法 /// </summary> /// <param name="toEncrypt">要加密 ...
- 使用 JavaScriptService 在.NET Core 里实现DES加密算法
文章<ASP.NET Core love JavaScript>和<跨平台的 NodeJS 组件解决 .NetCore 不支持 System.Drawing图形功能的若干问题> ...
- DES加密算法
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开 ...
随机推荐
- HDU 3999 二叉排序树
The order of a Tree Problem Description The shape of a binary search tree is greatly related to the ...
- PHP 文件下载流程
前台HTML: 添加download属性,不打开download.php页面 <a style='color:blue' href='download.php' download='data/C ...
- 4.1---二叉树是否平衡(CC150)
//也就是把高度在递归过程中给一并算了.public class Balance { public static boolean checkBalance(TreeNode root, int[] d ...
- 火狐浏览器,关闭ssl v3防护
某些网站,没有及时更新,导致火狐觉得有安全隐患,不给访问. --------------- Hello bcasey9090, go to about:config, copy the next bo ...
- rabbitmq使用
1. 用户管理 用户管理包括增加用户,删除用户,查看用户列表,修改用户密码. 相应的命令 (1) 新增一个用户 rabbitmqctl add_user Username Password (2 ...
- SQL Server日期和时间的格式
在SQL Server数据库中,SQL Server日期时间格式转换字符串可以改变SQL Server日期和时间的格式,是每个SQL数据库用户都应该掌握的.本文我们主要就介绍一下SQL Server日 ...
- poj 1060
http://poj.org/problem?id=1060 题意:多项式的运算的题目,不过这个运算有个特点,就是只要是同项的多项式,无论相加还是相减,都为0,给你三个多项式,分别为a,b,c. 要你 ...
- Zlib 在windows上的编译
1.下载http://www.zlib.net 下载,最新版本1.2.8 2.解压后,实际已提供了在vc下编译的工程,目录为:zlib-1.2.8\contrib\vstudio. 其中的zlibst ...
- java web 学习 --第四天(Java三级考试)
第三天的学习内容:http://www.cnblogs.com/tobecrazy/p/3453041.html jsp内置对象 out out 属性类型:JspWriter 作用域:page 作用: ...
- MySQL 5.6 Threadpool(优先队列)介绍及性能测试【转】
本文来自:http://www.gpfeng.com/?p=540&utm_source=tuicool&utm_medium=referral 背景介绍 MySQL常用(目前线上使用 ...