CTP开发(2)行情模块的开发

news2024/11/29 12:42:40

我在做CTP开发之前,也参考了不少其他的资料,发现他们都是把行情和交易做在同一个工程里的。我呢之前也做过期货相关的交易平台,感觉这种把行情和交易做在一起的方法缺乏可扩展性。比如我开了多个CTP账户,要同时交易,这种做在一起的方法就很难实现;另外,如果我还要接入其他的交易所,该怎么接?

下面是我的软件架构图:

软件架构图

为了便于说明CTP行情、交易的开发,这个架构图是最初始版的。以后会在这个基础上进行深化,开发成可以实战的可扩展的交易平台。

理论上,1个CTP账号可以在6个地方进行多点登录;另外在实战中,有的客户会有多个CTP账号同时进行量化交易。所以,这个软件架构图是能满足多CTP账号同时交易的要求的。

1、类文件说明

(1)CTPMdSpi.h、CTPMdSpi.cpp

继承CThostFtdcMdSpi类,里面实现所有的回调函数,是最重要的类。

(2)MarketL1Core.h、MarketL1Core.cpp

主类,里面包含着程序调用的主要逻辑关系。

(3)Main.cpp

写main()函数的类。

2、代码说明

(1)CTPMdSpi.h

#pragma once

#include <string>
#include <vector>

#include "ctp/ThostFtdcMdApi.h"

class CTPMdSpi : public CThostFtdcMdSpi
{
public:
	CTPMdSpi() = default;
	~CTPMdSpi() = default;

	///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	virtual void OnFrontConnected();
	
	///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
	///@param nReason 错误原因
	///        0x1001 网络读失败
	///        0x1002 网络写失败
	///        0x2001 接收心跳超时
	///        0x2002 发送心跳失败
	///        0x2003 收到错误报文
	virtual void OnFrontDisconnected(int nReason);
		
	///心跳超时警告。当长时间未收到报文时,该方法被调用。
	///@param nTimeLapse 距离上次接收报文的时间
	virtual void OnHeartBeatWarning(int nTimeLapse);
	
	///登录请求响应
	virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///登出请求响应
	virtual void OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///请求查询组播合约响应
	virtual void OnRspQryMulticastInstrument(CThostFtdcMulticastInstrumentField *pMulticastInstrument, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///错误应答
	virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	///订阅行情应答
	virtual void OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///取消订阅行情应答
	virtual void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///订阅询价应答
	virtual void OnRspSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///取消订阅询价应答
	virtual void OnRspUnSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
							CThostFtdcRspInfoField *pRspInfo, 
							int nRequestID, 
							bool bIsLast);

	///深度行情通知
	virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData);

	///询价通知
	virtual void OnRtnForQuoteRsp(CThostFtdcForQuoteRspField *pForQuoteRsp);

private:
	void ReqUserLogin();
	void SubscribeMarketData();
    bool IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo);

private:
    static int iRequestID;
};

(2)CTPMdSpi.cpp

#include <iostream>
#include <fstream>
#include <string.h>
#include <chrono>
#include <thread>

#include "CTPMdSpi.h"

using namespace std;

int CTPMdSpi::iRequestID = 0;

extern CThostFtdcMdApi *g_pMdApi;
extern std::string g_brokerId;
extern std::string g_userId;
extern std::string g_password;
extern std::vector<std::string> *g_pInstrumentIds;

//----------------------------- private methods ------------------------------
void CTPMdSpi::ReqUserLogin(){
	std::cout<<"ReqUserLogin g_brokerId:"<<g_brokerId<<" ,g_userId:"<<g_userId<<", g_password"<<g_password<<std::endl;
	CThostFtdcReqUserLoginField req;
	memset(&req, 0, sizeof(req));
	strcpy(req.BrokerID, g_brokerId.c_str());
	strcpy(req.UserID, g_userId.c_str());
	strcpy(req.Password, g_password.c_str());

	int iResult = g_pMdApi->ReqUserLogin(&req, ++iRequestID);
	cerr << "--->>> Req user Lgoin: " << ((iResult == 0) ? "Success" : "Fail") << endl;
}

void CTPMdSpi::SubscribeMarketData(){
	
	int len = g_pInstrumentIds->size();
	cout<<"SubscribeMarketData len:"<<len<<endl;

    char** ppInstrumentID = new char*[len];
    
    int index = 0;
    vector<string>::iterator it;
    for (it = g_pInstrumentIds->begin(); it != g_pInstrumentIds->end(); it++){
        *(ppInstrumentID + index) = (char*)it->c_str();
        std::cout<<"InstrumentID index["<<index<<"] : "<<*(ppInstrumentID + index)<<endl;
        index ++;
    }

	int iResult0 = g_pMdApi->UnSubscribeMarketData(ppInstrumentID, len);
	cerr << "--->>> UnSubscribeMarketData: " << ((iResult0 == 0) ? "Success" : "Fail") << endl;
	std::this_thread::sleep_for(std::chrono::seconds(1));
	int iResult = g_pMdApi->SubscribeMarketData(ppInstrumentID, len);
	cerr << "--->>> SubscribeMarketData: " << ((iResult == 0) ? "Success" : "Fail") << endl;

    delete[] ppInstrumentID;
}

bool CTPMdSpi::IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo){
	// 如果ErrorID != 0, 说明收到了错误的响应
	bool bResult = ((pRspInfo) && (pRspInfo->ErrorID != 0));
	if (bResult)
		cerr << "--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << endl;
	return bResult;
}

//----------------------------- public methods -------------------------------

///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
void CTPMdSpi::OnFrontConnected(){
    std::cout << "=====FrontConnected Success=====" << std::endl;
    ReqUserLogin();
}

///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
///@param nReason 错误原因
///        0x1001 网络读失败
///        0x1002 网络写失败
///        0x2001 接收心跳超时
///        0x2002 发送心跳失败
///        0x2003 收到错误报文
void CTPMdSpi::OnFrontDisconnected(int nReason){
	std::cerr << "=====FrontDisconnected=====" << std::endl;
	std::cerr << "ErrorCode: " << nReason << std::endl;
}
	
///心跳超时警告。当长时间未收到报文时,该方法被调用。
///@param nTimeLapse 距离上次接收报文的时间
void CTPMdSpi::OnHeartBeatWarning(int nTimeLapse){
	std::cerr << "=====HeartBeat Timeout=====" << std::endl;
	std::cerr << "Timeout lap: " << nTimeLapse << std::endl;
}

///登录请求响应
void CTPMdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){
	if (bIsLast && !IsErrorRspInfo(pRspInfo)){
		std::cout << "=====OnRspUserLogin success=====" << std::endl;
		std::cout << "TradingDay: " << pRspUserLogin->TradingDay << std::endl;
		std::cout << "LoginTime: " << pRspUserLogin->LoginTime << std::endl;
		std::cout << "BrokerID: " << pRspUserLogin->BrokerID << std::endl;
		std::cout << "UserID: " << pRspUserLogin->UserID << std::endl;

		///获取当前交易日
		cerr << "--->>> GetTradingDay: " << g_pMdApi->GetTradingDay() << endl;

		// 请求订阅行情
		SubscribeMarketData();
	}
}

///登出请求响应
void CTPMdSpi::OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){

}

///请求查询组播合约响应
void CTPMdSpi::OnRspQryMulticastInstrument(CThostFtdcMulticastInstrumentField *pMulticastInstrument, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){

}

///错误应答
void CTPMdSpi::OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){
    IsErrorRspInfo(pRspInfo);
}

///订阅行情应答
void CTPMdSpi::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){
    if (bIsLast && !IsErrorRspInfo(pRspInfo)){
    	cout<< "--->>> OnRspSubMarketData: " <<pSpecificInstrument->InstrumentID<<" Success"<<endl;

		char filePath[100] = { '\0' };
		sprintf(filePath, ".//sql_data//%s_market_data.csv", pSpecificInstrument->InstrumentID);
		std::ofstream outFile;
		outFile.open(filePath, std::ios::out); // 新开文件
		outFile << "InstrumentID" << ","
			<< "UpdateTime" << ","
			<< "LastPrice" << ","
			<< "Volume" << ","
			<< "BidPrice1" << ","
			<< "BidVolume1" << ","
			<< "AskPrice1" << ","
			<< "AskVolume1" << ","
			<< "OpenInterest" << ","
			<< "Turnover"
			<< std::endl;

		outFile.close();
    }
}

///取消订阅行情应答
void CTPMdSpi::OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){

}

///订阅询价应答
void CTPMdSpi::OnRspSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){

}

///取消订阅询价应答
void CTPMdSpi::OnRspUnSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
						CThostFtdcRspInfoField *pRspInfo, 
						int nRequestID, 
						bool bIsLast){

}

///深度行情通知
void CTPMdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData){

    // 生成5档盘口和成交数据
	std::cout << "=====OnRtnDepthMarketData=====" << std::endl;
	std::cout << "TradingDay: " << pDepthMarketData->TradingDay << std::endl;
	std::cout << "ExchangeID: " << pDepthMarketData->ExchangeID << std::endl;
	std::cout << "InstrumentID: " << pDepthMarketData->InstrumentID << std::endl;
	std::cout << "ExchangeInstID: " << pDepthMarketData->ExchangeInstID << std::endl; //合约在交易所的代码
	std::cout << "LastPrice: " << pDepthMarketData->LastPrice << std::endl;
	std::cout << "Volume: " << pDepthMarketData->Volume << std::endl;
    
    //生成Tick数据

	// 将行情写入到csv文件中
	// 如果只获取某一个合约行情,可以逐tick地存入文件或数据库
	char filePath[100] = { '\0' };
	sprintf(filePath, ".//sql_data//%s_market_data.csv", pDepthMarketData->InstrumentID);
	//sprintf(filePath, "%s_market_data.csv", pDepthMarketData->InstrumentID);
	std::ofstream outFile;
	outFile.open(filePath, std::ios::app); // 文件追加写入 
	outFile << pDepthMarketData->InstrumentID << ","
		<< pDepthMarketData->UpdateTime << "." << pDepthMarketData->UpdateMillisec << ","
		<< pDepthMarketData->LastPrice << ","
		<< pDepthMarketData->Volume << ","
		<< pDepthMarketData->BidPrice1 << ","
		<< pDepthMarketData->BidVolume1 << ","
		<< pDepthMarketData->AskPrice1 << ","
		<< pDepthMarketData->AskVolume1 << ","
		<< pDepthMarketData->OpenInterest << ","
		<< pDepthMarketData->Turnover << std::endl;
	outFile.close();

}

///询价通知
void CTPMdSpi::OnRtnForQuoteRsp(CThostFtdcForQuoteRspField *pForQuoteRsp){

}

(3)MarketL1Core.h

#pragma once
#include <string>
#include <vector>

#include "CTPMdSpi.h"

class MarketL1Core
{
public:
	MarketL1Core() = default;
	~MarketL1Core();

	bool start();
    bool stop();

    //启动客户端监听
    bool startListenClient();

private:
	bool checkConfig();

    //连接上手服务器(CTP)
    bool connectUpServer();

    
private:
	std::string serviceName;
	std::string serviceIp;
	int servicePort;

	std::string CTP_Address1; //主地址
	std::string CTP_Address2; //备地址

    std::vector<std::string> instrumentIds;
	CThostFtdcMdSpi *pMdUserSpi = new CTPMdSpi;
};

(4)MarketL1Core.cpp

#include <iostream>

#include "MarketL1Core.h"
#include "com/xini_file.h"
#include "Util.h"

using namespace std;

CThostFtdcMdApi *g_pMdApi;
std::string g_brokerId;
std::string g_userId;
std::string g_password;
std::vector<std::string> *g_pInstrumentIds;

//---------------------------------- private methods --------------------------------
bool MarketL1Core::checkConfig(){

    CTP_Address1 = "tcp://218.202.237.33:10213";
    CTP_Address2 = "";

    g_brokerId = "9999";
    g_userId = "xxx"; //填入你自己申请到的账号、密码
    g_password = "xxx"; //
    
    string ids = "sc2302|sc2403|bu2306|IO2303-C-3900|ag2302|WH301"; //需要订阅的行情
    instrumentIds = Util::split(ids, "|");
    g_pInstrumentIds = &instrumentIds;

    cout<<"CTP_Address1:"<<CTP_Address1<<", instrumentIds:"<<ids<<endl;
}

//连接上手服务器(CTP)
bool MarketL1Core::connectUpServer(){
    // 初始化UserApi
	g_pMdApi = CThostFtdcMdApi::CreateFtdcMdApi("./market_data/", true);
	g_pMdApi->RegisterSpi(pMdUserSpi);					                 // 注册事件类
	g_pMdApi->RegisterFront((char*)CTP_Address1.c_str());				 // connect		优先行情地址
	//pMdApi->RegisterFront();							                 // connect		备用行情地址,1B断开,自动连接2B地址

	g_pMdApi->Init();

    std::cout<<"Version:"<<g_pMdApi->GetApiVersion()<<std::endl;
}

//---------------------------------- public methods ---------------------------------
MarketL1Core::~MarketL1Core(){
	if(g_pMdApi){
		g_pMdApi->Join();
		g_pMdApi->Release();
	}

	if(pMdUserSpi){
		delete pMdUserSpi;
		pMdUserSpi = nullptr;
	}
}

bool MarketL1Core::start(){
    // 初始化log

    // 读取配置
    checkConfig();

    connectUpServer();
}

bool MarketL1Core::stop(){

}

(5)split()方法

class Util
{
public:
    static std::vector<std::string> split(std::string str, std::string pattern)
    {
	    std::string::size_type pos;
	    std::vector<std::string> result;
	    str += pattern;//扩展字符串以方便操作  
	    int size = str.size();
	    for (int i = 0; i < size; i++)
	    {
	        pos = str.find(pattern, i);
	        if (pos < size)
	        {
	            std::string s = str.substr(i, pos - i);
	            result.push_back(s);
	            i = pos + pattern.size() - 1;
	        }
	    }
	    return result;
    }
};

(6)main()方法

#include "MarketL1Core.h"

int main(int argc,char *argv[])
{
    MarketL1Core core;
    core.start();
}

好了,基本步骤就这些。最后在Unix操作系统上make通过后,就可以运行了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springMVC的学习拦截器之验证用户登录案例

文章目录实现思路关于环境和配置文件pomspring的配置文件关于idea的通病/常见500错误的避坑实现步骤编写登陆页面编写Controller处理请求编写登录成功的页面编写登录拦截器实现思路 有一个登录页面&#xff0c;需要写一个controller访问页面登陆页面提供填写用户名和密码的表单…

UE4c++日记1(允许 创类、蓝图读写/调用/只读、分类、输出日志打印语句)

目录 1允许创建基于xx的蓝图类 2允许蓝图读写/允许蓝图调用/只读 读写调用 只读 3为变量/函数分类 4输出日志打印一段话 1.先创建一个蓝图类 2.构建对象 3.写提示代码&#xff0c;生成解决方案 4.运行&#xff0c;打开“输出日志” 5.总结 创类-实例化对象&#xff08;构建…

2022年个人年终总结(一)

2022年个人年终总结&#xff08;一&#xff09;考研想法的萌生回顾过去一年-考研心路历程基础阶段&#xff08;1-6月&#xff09;强化阶段&#xff08;7-9月&#xff09;冲刺阶段&#xff08;10-12月&#xff09;感受总结特别感谢2022年是做梦的一年&#xff0c;花了一年的时间…

Zookeeper相关操作

Zookeeper概念 •Zookeeper 是 Apache Hadoop 项目下的一个子项目&#xff0c;是一个树形目录服务。 •Zookeeper 翻译过来就是 动物园管理员&#xff0c;他是用来管 Hadoop&#xff08;大象&#xff09;、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk •Zookeeper 是一个分布式的…

【C++】非递归实现二叉树的前中后序遍历

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;二叉树的…

如何运营个人技术博客

前言 本篇和大家聊聊如何运营个人技术博客&#xff0c;定位下做技术写作的目的&#xff0c;有哪些交流平台和输出方式&#xff0c;如何把控内容质量&#xff0c;整理了一些写作技巧和自己常用的写作工具&#xff0c;最后分享下如何在有限的时间里合理安排保证写作与工作的平衡。…

第九届蓝桥杯省赛 C++ A组 - 付账问题

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;付账问题 &#x1f4e3;专栏定位&#xff1a;为想考甲级PAT的小伙伴整理常考算法题解&#xff0c;祝大家…

理解CSS

CSS 作为前端技术栈中关键一环&#xff0c;对页面元素及样式呈现起到了直接作用。本节课旨在通过对 CSS 的工作流程及原理、页面中 CSS 使用方法等详细解读&#xff0c;帮助前端新手建立对 CSS 的全面而深刻的认知。 CSS概念 CSS 即 Cascading Style Sheets&#xff0c;是用来…

认识涤生大数据的几个月,彻底改变了我

1自我介绍 大家好&#xff0c;我是泰罗奥特曼&#xff0c;毕业于东北的一所不知名一本大学&#xff0c;学校在一个小城市里面&#xff0c;最热闹的地方是一个四层楼的商城&#xff0c;专业是信息管理与信息系统&#xff0c;由于是调剂的&#xff0c;所以我也不知道这个专业是干…

[JavaEE]阻塞队列

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.阻塞队列的概念 2.标准库中的阻塞队列 3.生产者…

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据 1、时间&#xff1a;1999-2019年 2、指标&#xff1a; 可支配收入、城镇居民家庭平均每人全年消费性支出、食品消费支出、医疗保健消费支出、农村居民家庭人均纯收入、农村居民家庭平均每人生活消费支出、食品…

【Unity URP】设置光源层Light Layers

光源层 (Light Layers) 功能允许配置某些光源仅影响特定的游戏对象。 此功能可以用于加亮在暗处的物体。 1.开启光源层&#xff0c;并设置光源层名称 在URP资源中&#xff0c;点击Lighting右侧的垂直省略号图标 (⋮)&#xff0c;勾选Show Additional Properties&#xff0c…

【已解决】WARNING: Ignoring invalid distribution xxx

问题解决方案解释问题 WARNING: Ignoring invalid distribution -umpy (c:\users\xxx\appdata\roaming\python\python36\site-packages) 解决方案 在报错的路径下(c:\users\xxx\appdata\roaming\python\python36\site-packages)&#xff0c;找到~对应文件夹&#xff0c;此处…

Pytorch实战笔记(1)——BiLSTM 实现情感分析

本文展示的是使用 Pytorch 构建一个 BiLSTM 来实现情感分析。本文的架构是第一章详细介绍 BiLSTM&#xff0c;第二章粗略介绍 BiLSTM&#xff08;就是说如果你想快速上手可以跳过第一章&#xff09;&#xff0c;第三章是核心代码部分。 目录1. BiLSTM的详细介绍2. BiLSTM 的简单…

【三年面试五年模拟】算法工程师的独孤九剑秘籍(第十二式)

Rocky Ding公众号&#xff1a;WeThinkIn写在前面 【三年面试五年模拟】栏目专注于分享AI行业中实习/校招/社招维度的必备面积知识点与面试方法&#xff0c;并向着更实战&#xff0c;更真实&#xff0c;更从容的方向不断优化迭代。也欢迎大家提出宝贵的意见或优化ideas&#xff…

【算法】二叉树

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录二叉树数组转化为二叉树二叉树转化为二叉链表二叉树的遍历排序二叉树BST&#xff08;二叉搜索树&…

新思路dp

参考文章思路&#xff1a;点我 题&#xff1a;C. Count Binary Strings 前言&#xff1a;嗯,今天做这个题的时候&#xff0c;想了一堆乱七八糟的解法&#xff0c;想记录一下hhhhhh。 题意&#xff1a;输入以类似于邻接表的形式给出字符串&#xff08;只由000和111组成&#…

深入理解MySQL中的bin log、redo log、undo log

bin log&#xff08;二进制日志&#xff09; 什么是bin log&#xff1f; 记录数据库执行的写入性操作信息&#xff0c;以二进制的形式保存在磁盘中。 由服务层产生&#xff0c;所有储存引擎都支持。 bin log属于逻辑日志。 bin log日志有三种格式&#xff1a;STATMENT、ROW、M…

详解数据库的锁机制及原理

详解数据库的锁机制及原理1.数据库锁的分类2.行锁共享锁排他锁更新锁3.意向锁4.锁机制解释数据库隔离级别1.数据库锁的分类 本图源自CSDN博主&#xff1a;Stephen.W 数据库锁一般可以分为两类&#xff0c;一个是悲观锁&#xff0c;一个是乐观锁 乐观锁一般是指用户自己实现的…

Java 中是如何获取 IP 属地的

细心的小伙伴可能会发现&#xff0c;抖音新上线了 IP 属地的功能&#xff0c;小伙伴在发表动态、发表评论以及聊天的时候&#xff0c;都会显示自己的 IP 属地信息下面&#xff0c;我就来讲讲&#xff0c;Java 中是如何获取 IP 属地的&#xff0c;主要分为以下几步 通过 HttpSer…