一、开发背景

在项目开发过程中中遇到了以下三个需求:

  1. 多个用户同时上传数据;

  2. 数据库需要支持同时读写;

  3. 1分钟内存储上万条数据;

根据对Mysql的测试情况,遇到以下问题:

  1. 最先遇到压力的是服务器,在写入2500-3000条数据时,服务器崩溃了;

  2. 当数据库写入时,耗时太长,10000条数据,大概需要505.887s,相当于8分钟,如下:

  a. 表结构:

  

  b. 数据库Procedure:

DROP PROCEDURE IF EXISTS my_insert;
CREATE PROCEDURE my_insert()
BEGIN
DECLARE n int DEFAULT 1;
loopname:LOOP
INSERT INTO car_pathinfo_driver_cpy(id, linkphone,cartype,carcolor,carnumber,drivername,pubtimes)VALUES(n+500,'','雪弗兰','白','豫A190XS','siker','');
SET n=n+1;
IF n=10000 THEN
LEAVE loopname;
END IF;
END LOOP loopname;
END;
CALL my_insert();

  c. 运行结果如下:

  

  3. 不断的数据库写入导致数据库压力过大;

出现以上问题,是由于mysql是基于磁盘的IO,基于服务响应性能考虑,就需要给数据做缓存,所以决定使用Mysql+redis缓存的解决方案,将业务热数据写入Redis缓存,使得高频业务数据可以直接从内存读取,提高系统整体响应速度。

二、使用Redis+Mysql需要考虑的问题
  使用redis缓存+mysql数据库存储能解决:

  1. 数据读写的速度

  2. 服务器的压力问题

  同时,就需要考虑同步问题了,Redis和Mysql的同步问题

三、Redis+mysql同步解决方案

  1.写Redis->redis写mysql,读Mysql。

  以下是一个Redis+mysql同步的示例,该示例测试了写入100000条数据的效率,先向Redis写入100000条数据,再将数据读出,写入Mysql。

    批量写入缓解了服务器的压力。

stdafx.h

// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
// #pragma once #include "targetver.h" #include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <assert.h>
#include <vector>
#include "hiredis.h"
#include <Windows.h>
#include "mysql.h" #ifdef _DEBUG
#pragma comment(lib, "hiredis_d.lib")
#pragma comment(lib, "Win32_Interop_d.lib")
#else
#pragma comment(lib, "hiredis.lib")
#pragma comment(lib, "Win32_Interop.lib") #endif
#pragma comment(lib, "AdvAPI32.Lib")
#pragma comment(lib, "DbgHelp.Lib")
#pragma comment (lib, "Shlwapi.lib")
#pragma comment(lib,"libmysql.lib") using namespace std; typedef struct testData
{
int iHeight;
int iWidth;
char szValue[];
char szHValue[];
}stTestData, *pstTestData;

test.h

#include "stdafx.h"
#include "DBHandle.h" int main()
{ DBHandle *dbHandle = new DBHandle();
thread tWriteDataToRedis(&DBHandle::writeHsetToRedis, *dbHandle);
tWriteDataToRedis.join(); return ;
}

DBHandle.h

#pragma once
#include <mutex>
#include <thread> class DBHandle
{
public:
DBHandle();
~DBHandle(); bool connectRedis(string strIp, int iPort, string strPwd);
void freeRedis(); int getRedisDBSize(); bool writeHsetToRedis();
bool readDataFromRedis(); bool connectMysql();
void FreeMysqlConnect(); bool insertDataToMysql(string strData); redisContext* m_pRedisContext;
MYSQL m_mysql;
MYSQL_RES *res; //行的一个查询结果集 };

DBHandle.cpp

#include "stdafx.h"
#include "DBHandle.h" DBHandle::DBHandle()
{
m_pRedisContext = NULL;
} DBHandle::~DBHandle()
{
if (m_pRedisContext != NULL)
{
m_pRedisContext = NULL;
}
} bool DBHandle::connectRedis(string strIp, int iPort, string strPwd)
{
//redis默认监听端口为6387 可以再配置文件中修改
char szBuf[] = {};
strcpy_s(szBuf, sizeof(strIp), strIp.c_str());
m_pRedisContext = redisConnect(szBuf, iPort);
if (NULL == m_pRedisContext || m_pRedisContext->err)
{
return false;
} //输入Redis密码
strcpy_s(szBuf, sizeof(strPwd), strPwd.c_str());
redisReply *pRedisReply = (redisReply*)redisCommand(m_pRedisContext, "AUTH %s", szBuf);
if (NULL != pRedisReply)
{
freeReplyObject(pRedisReply);
}
if (NULL == pRedisReply->str)
{
return false;
}
return true; } void DBHandle::freeRedis()
{
redisFree(m_pRedisContext);
if (m_pRedisContext != NULL)
{
m_pRedisContext = NULL;
}
} int DBHandle::getRedisDBSize()
{
//查看list长度
int iListLen = ;
//redisReply *pRedisReply = (redisReply *)redisCommand(m_pRedisContext, "LLen datalist");
redisReply *pRedisReply = (redisReply *)redisCommand(m_pRedisContext, "DBSIZE");
if (NULL != pRedisReply)
{
if (NULL == pRedisReply->integer)
{
return false;
}
iListLen = pRedisReply->integer;
freeReplyObject(pRedisReply);
}
if (NULL == pRedisReply)
{
printf("%s \r\n", m_pRedisContext->errstr);
return false;
} return iListLen;
} bool DBHandle::writeHsetToRedis()
{
bool bFlag = connectRedis("127.0.0.1", , "");
if (false == bFlag)
{
return false;
} time_t st = time(NULL);//秒
stTestData data = {};
int i = ;
while (i<)
{ data.iHeight = i;
data.iWidth = ;
char szBuf[] = {};
sprintf_s(szBuf, "width%d", i);
strcpy_s(data.szValue, , szBuf);
sprintf_s(data.szHValue, "%s%d", "heighttest", i); //向Redis写入数据hset location (interger)1 "width"
sprintf_s(szBuf, "hset location%d value %s", i, data.szValue);
redisReply *pRedisReply = (redisReply *)redisCommand(m_pRedisContext, szBuf);
if (NULL != pRedisReply)
{
freeReplyObject(pRedisReply);
}
i++;
} printf("write finish");
readDataFromRedis(); time_t et = time(NULL);
int iUsed = st - et;
printf("used time is %d", iUsed);
freeRedis();
return true; } bool DBHandle::readDataFromRedis()
{
/*bool bFlag = connectRedis("127.0.0.1", 6379, "123456");
if (false == bFlag)
{
return false;
}*/ printf("read start"); int iSize = getListSize();
if (iSize <= )
{
return false;
}
bool bSuc = connectMysql();
if (bSuc == false)
{
return false;
} int iCount = iSize;//计数
while (iCount > )
{
//用get命令获取数据
redisReply *pRedisReply = (redisReply*)redisCommand(m_pRedisContext, "RPOP datalist");
if (NULL == pRedisReply)
{
return false;
}
if (NULL != pRedisReply->str)
{
string str = pRedisReply->str;
insertDataToMysql(str);
freeReplyObject(pRedisReply);
}
iCount--;
} printf("read finish"); return true; } bool DBHandle::connectMysql()
{
mysql_init(&m_mysql); // Connects to a MySQL server
const char host[] = "192.168.4.8";
const char user[] = "root";
const char passwd[] = "";
const char db[] = "topproductline";
unsigned int port = ;
const char *unix_socket = NULL;
unsigned long client_flag = ; /*A MYSQL* connection handler if the connection was successful,
NULL if the connection was unsuccessful. For a successful connection,
the return value is the same as the value of the first parameter.*/
if (mysql_real_connect(&m_mysql, host, user, passwd, db, port, unix_socket, client_flag)) {
printf("The connection was successful.\n");
return true;
}
else {
printf("Error connecting to database:%s\n", mysql_error(&m_mysql));
return false;
}
} void DBHandle::FreeMysqlConnect()
{
mysql_free_result(res);
mysql_close(&m_mysql);
} bool DBHandle::insertDataToMysql(string strData)
{
char szQuery[] = {};
sprintf_s(szQuery, "insert into a_test (type) values ('%s');", strData.c_str());
if (mysql_query(&m_mysql, szQuery)) {
printf("Query failed (%s)\n", mysql_error(&m_mysql));
return false;
}
else {
printf("Insert success\n");
return true;
}
}

  测试结果:

  2.写redis->写mysql,读Redis->未找到->读Mysql

数据库应用之--Redis+mysql实现大量数据的读写,以及高并发的更多相关文章

  1. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis比多线程的memcached效率要高得多(为什么redis是单线程的但是还可以支撑高并发)?

    1.redis和memcached有什么区别? 这个事儿吧,你可以比较出N多个区别来,但是我还是采取redis作者给出的几个比较吧 1)Redis支持服务器端的数据操作:Redis相比Memcache ...

  2. 心知天气数据API 产品的高并发实践

    心知天气数据API 产品的高并发实践 心知天气作为国内领先的商业气象服务提供商,天气数据API 产品从公司创立以来就一直扮演着很重要的角色.2009 年API 产品初次上线,历经十年,我们不断用心迭代 ...

  3. .NET MVC同页面显示从不同数据库(mssql、mysql)的数据

    控制器: private readonly VipViewModel _model = new VipViewModel(); public static string Msg;// GET: Sys ...

  4. PHP解决网站大数据大流量与高并发

    1:硬件方面 普通的一个p4的服务器每天最多能支持10万左右的IP,如果访问量超过10W那么需要专用的服务器才能解决,如果硬件不给力软件怎么优化都是于事无补的.主要影响服务器的速度 有:网络-硬盘读写 ...

  5. MySQL 高级性能优化架构 千万级高并发交易一致性系统基础

    一.MySQL体系架构 由图,可以看出MySQL最上层是连接组件.下面服务器是由连接池.管理服务和工具组件.SQL接口.查询解析器.查询优化器.缓存.存储引擎.文件系统组成. 1.连接池 管理.缓冲用 ...

  6. PHP 网站大数据大流量与高并发 笔记

    前端: 1.域名开启cdn 2.大文件使用oss php: 1.模板编译缓存 服务器: 1.负载均衡 数据库: 1.读写分离 待完善

  7. php如何处理大数据高并发

    大数据解决方案 使用缓存: 使用方式:1,使用程序直接保存到内存中.主要使用Map,尤其ConcurrentHashMap. 使用缓存框架.常用的框架:Ehcache,Memcache,Redis等. ...

  8. redis作为mysql的缓存服务器(读写分离,通过mysql触发器实现数据同步)

    一.redis简介Redis是一个key-value存储系统.和Memcached类似,为了保证效率,数据都是缓存在内存中.区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录 ...

  9. 【Redis 向Redis中批量导入mysql中的数据(亲自测试)】

    转自:https://blog.csdn.net/kenianni/article/details/84910638 有改动,仅供个人学习 问题提出:缓存的冷启动问题 应用系统新版本上线,这时候 re ...

随机推荐

  1. [洛谷P5367]【模板】康托展开

    题目大意:给定一个$n$的排列,求它在$n$的全排列中的名次 题解:康托展开,对于一个全排列,第$i$为有$n+1-i$种选择,用变进制数表示,这一位就是$n+1-i$进制.记排列中第$[1,i)$中 ...

  2. 分享大麦UWP版本开发历程-03.GridView或ListView 滚动底部自动加载后续数据

    今天跟大家分享的是大麦UWP客户端,在分类.订单或是搜索时都用到的一个小技巧,技术粗糙大神勿喷. 以大麦分类举例,默认打开的时候,会为用户展示20条数据,当用户滚动鼠标或者使用手势将列表滑动到倒数第二 ...

  3. IdentityServer4之Jwt身份验证方案分析

    一,准备内容 在之前讲过的asp.net core 实现OAuth2.0四种模式系列中的IdentityApi客户端用到了以下配置代码 public void ConfigureServices(IS ...

  4. flutter apk 打包

    https://blog.csdn.net/weixin_33738578/article/details/87998565 http://www.cnblogs.com/sangwl/p/10400 ...

  5. rabbitMq 学习笔记(二) 备份交换器,过期时间,死信队列,死信队列

    备份交换器 备份交换器,英文名称为 Altemate Exchange,简称庙,或者更直白地称之为"备胎交换器". 生产者在发送消息的时候如果不设置 mandatory 参数, 那 ...

  6. Django:web认识,jinja2模块,如何安装Django

    一内容概要 1.HTTP协议 1.1简介 ​ 超文本传输协议(英文:Hyper Text Transfer Protocol,HTTP)是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP是 ...

  7. Js保存图片到本地

    注:此方法是使用hbuilderx云打包之后才能用,否则在浏览器中会报 plus is not defined 官方文档 http://www.html5plus.org/doc/zh_cn/gall ...

  8. IOS模拟器调试ANE

    来源:http://www.tuicool.com/articles/AFRJzi 利用iOS模拟器来检测和调试AIR应用程序补充篇 Air3.4来了 除去可以直接往模拟器里面部署应用,还可以往真机里 ...

  9. mysql基础指令知识

    桌面指令(cmd)进入mysql客户端 第一步:安装mysql,配置环境变量 第二步:手动开启服务 第三步:输入如下指令: mysql [-h localhost -P 3306] -u  用户名 - ...

  10. Chrome快捷键统计

    Chrome快捷键: Chrome 个人常用快捷键 1 将当前网页保存为书签 Ctrl + d 2 重新加载当前网页 Ctrl + r或F5 3 打开书签管理器 Ctrl + Shift + o 4 ...