千万级数据并发解决方案(理论+实战) 高并发解决思路 方案
千万级数据并发解决方案(理论+实战)课程地址
项目地址
场景
秒杀 高并发
新闻系统 超大数据量
一般的网站写入的少读取的次数多
模糊查询
数据量少的时候可以用 like
数据量多的时候用 Elasticsearch搜索引擎占用磁盘空间比较大
生成数据
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `article_tmp`;
CREATE TABLE `article_tmp` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`url` varchar(255) NOT NULL,
`descs` varchar(255) NOT NULL,
`author` varchar(255) NOT NULL,
`add_time` varchar(255) NOT NULL,
`from` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=201 DEFAULT CHARSET=utf8;
INSERT INTO `article_tmp` VALUES ('1', '模型驱动设计的构造块(下)——DDD', 'https://www.cnblogs.com/afei-24/p/16985996.html', '3. 领域对象的生命周期 每个对象都有生命周期,如下图所示。对象自创建后,可能会经历各种不同的状态,直至最终消亡——要么存档,要么删除。当然很多对象是简单的临时对象,仅通过调用构造函数来创建,用来做一些计算,然后由垃圾收集器回收。这类对象没必要搞得那么复杂。但有些对象具有更长的生命周期,其中一部分时 ...', 'Ruby_Lu', '2023-01-06 07:22', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('2', 'IT编程相关内容汇总 - 进阶者系列 - 学习者系列文章', 'https://www.cnblogs.com/lzhdim/p/17028789.html', '笔者工作了十多年了,对于技术也有一定的经验,但是IT编程技术的更新是挺快的,特别是各种框架,各种中间件啥的都涌现出来了。这篇博文笔者打算将IT编程的前端、后端、数据库和移动端做一个博文知识汇总,让阅读笔者博客的读者能够有一个系统化学习编程技术的博文。前面已经有一个博文进行过相关的介绍,但是那个比较普 ...', 'lzhdim', '2023-01-06 00:00', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('3', '小白致力于成为前后端开发程序员', 'https://www.cnblogs.com/pocn/p/16857245.html', '小白有个烦恼,做前端项目的时候,遇到两种情况一种是在vue框架下,使用HTML写页面,script部分代码里面的方法基本上使用JS来写;一种同样在vue框架下,通过安装的框架来构建页面,script中使用的方法也多是安装的框架中封装好的方法。小白是个倒霉催的孩子,负责的项目比较多,常常在两种情况下切 ...', 'BOBO~', '2023-01-05 22:51', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('4', 'BST查找结构与折半查找方法的实现与实验比较', 'https://www.cnblogs.com/Az1r/p/17028980.html', '简介 作业:查找结构与排序方法 作业题目: BST 查找结构与折半查找方法的实现与实验比较 要求编写程序实现 BST 存储结构的建立(插入)、删除、查找和排序算法; 实现折半查找算法;比较 BST 查找与折半查找方法的时间性能。 作业要求: 1. 设计 BST 的左右链存储结构,并实现 BST 插入 ...', '江水为竭', '2023-01-05 22:12', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('5', 'Java开发学习(五十)----MyBatisPlus快速开发之代码生成器解析', 'https://www.cnblogs.com/xiaoyh/p/16468217.html', '1、代码生成器原理分析 造句: 我们可以往空白内容进行填词造句,比如: 在比如: 观察我们之前写的代码,会发现其中也会有很多重复内容,比如: 那我们就想,如果我想做一个Book模块的开发,是不是只需要将红色部分的内容全部更换成Book即可,如: 所以我们会发现,做任何模块的开发,对于这段代码,基本上 ...', '|旧市拾荒|', '2023-01-05 21:57', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('6', 'VMware搭建内网渗透环境', 'https://www.cnblogs.com/LeslieSec/p/17028722.html', '网络结构: 攻击机:kali 192.168.1.103 DMZ区域:防火墙 WAN:192.168.1.104 LAN:192.168.10.10 winserver03 LAN:192.168.10.11 用户办公区域:路由器 WAN:192.168.10.20 LAN:192.168.20.1 ...', '林烬', '2023-01-05 21:21', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('7', '自动增长配置不合理导致的性能抖动', 'https://www.cnblogs.com/zhuancloud/p/17028870.html', '背景 客户收到了SQL专家云告警邮件,在凌晨2点到3点之间带有资源等待的会话数暴增,请我们协助分析。 现象 登录SQL专家云,进入活动会话的趋势分析页面,下钻到2点钟一个小时内的数据,看到每分钟的等待数都在100左右,2点15分时达到200。 转到活动会话原始数据页面,看到大量会话都在等待,等待类型 ...', '格瑞趋势技术团队', '2023-01-05 21:07', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('8', 'C | 指针', 'https://www.cnblogs.com/lijiuliang/p/17028849.html', '1.什么是指针 指针是一种变量,也称指针变量,它的值不是整数、浮点数和字符,而是内存地址。指针的值就是变量的地址,而变量有拥有一个具体值。因此,可以理解为变量直接引用了一个值,指针间接地引用了一个值。一个存放变量地址的类型称为该变量的“指针”。 指针变量的大小? 以32位系统为例,每个字节(即一个内 ...', '就良同学', '2023-01-05 20:54', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('9', '07.synchronized都问啥?', 'https://www.cnblogs.com/wyz1994/p/17028834.html', '大家好,我是王有志。关注王有志,一起聊技术,聊游戏,从北漂生活谈到国际风云。最近搞了个抽奖送书的活动,欢迎点击链接参与。 如果Java面试有什么是必问的,synchronized必定占据一席之地。初出茅庐时synchronized的用法,成长后synchronized的原理,可谓是Java工程师的“ ...', '王有志', '2023-01-05 20:48', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('10', 'JavaScript 中如何拦截全局 Fetch API 的请求和响应?', 'https://www.cnblogs.com/wenruo/p/17028832.html', '本文翻译自 Intercepting JavaScript Fetch API requests and responses 拦截器是可用于预处理或后处理 HTTP 请求的代码块,有助于全局错误处理、身份验证、日志记录等。在本文中,你将学习如何拦截 JavaScript Fetch API 请求。...', '我不吃饼干呀', '2023-01-05 20:47', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('11', '.gitignore文件配置以及gitee提交报Push rejected...错误解决', 'https://www.cnblogs.com/wren/p/17028830.html', '.gitignore文件配置 .gitignore 文件可以用来忽略被指定的文件或文件夹的改动。记录在.gitignore文件里的文件或文件夹是不会被 git 跟踪到,也就是被忽略的文件是不会被上传到远程仓库的,如果文件已经存在于远程仓库中就无法通过.gitignore文件来忽略。 下面总结了一些可 ...', '请叫我阿杰', '2023-01-05 20:46', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('12', 'Ventoy制作启动盘和使用VMware测试启动盘(论文版)', 'https://www.cnblogs.com/alittlemc/p/17027815.html', 'Ventoy是可用于制作启动U盘的开源工具,在占用少量引导分区容量后,其他空间依旧可以正常当一般的U盘读写文件。它的最大特点是只要将iso、win、img、efi等之类的镜像文件和引导文件移动到U盘中。比如导出微PE、杏雨梨云的可启动iso镜像文件,移动到U盘中,在启动时选择微PE、杏雨梨云任意一个... ...', 'alittlemc', '2023-01-05 20:45', 'https://www.cnblogs.com');
INSERT INTO `article_tmp` VALUES ('13', 'JUC并发编程详解(通俗易懂)', 'https://www.cnblogs.com/ZhangHao-Study/p/16994667.html', '一、JUC简介 在Java5.0提供了java.util.concurrent包,简称JUC,即Java并发编程工具包。JUC更好的支持高并发任务。 具体的有以下三个包: java.util.concurrent java.util.concurrent.atomic java.util.concu ...', '九八十', '2023-01-05 20:39', 'https://www.cnblogs.com');在laravel中 创建
要提前配置好数据库
//创建新的命令类
php artisan make:command Tests
//创建后使用命令
php artisan app:tests在Tests文件中写入
use DB;
public function handle()
{
echo Date('Y-m-d H-i-s')."\r\n";
$lists = DB::table('article_tmp')->get()->toArray();
for ($i = 0; $i < 10000000; $i++) {
$randindexArr = array_rand($lists,2);
$data['title'] = $data['descs'] = '';
foreach($randindexArr as $val){
$data['url']=$lists[$val]->url;
$data['cid']=rand(1,50);
$data['title'] .=mb_substr($lists[$val]->title,0,rand(2,16));
$data['descs'] .=mb_substr($lists[$val]->descs,rand(0,16),rand(32,64));
$data['author']=$lists[$val]->author;
$data['add_time']=$lists[$val]->add_time;
$data['from']=$lists[$val]->from;
}
DB::table('article')->insert($data);
};
echo Date('Y-m-d H-i-s')."\r\n";
}运行命令 php artisan app:tests 生成数据
mysql查询原则
数据多 并发高
字段越多 体积越大
[*]程序用到哪几个字段就查询哪几个字段,尽量避免select * 查询
[*]一次用多少数据,就返回多少数据:加limit限制
[*]尽量用 = ,and 不要用 >,< ,,or
[*]字段设计尽量 不为null
添加索引
[*]针对热点字段添加索引
[*]索引不一定是越多越好 因为会影响 insert,update,delete 的效率 (修改表数据的时候 ,mysql需要同步更新索引)
[*]索引最好在开发的时候创建好,尽量避免在生产环境中创建索引(数据量大的时候,创建耗时较长,会影响业务)
一旦发生扫表 效率是最低的
EXPLAIN 查看执行计划
EXPLAIN SELECT * FROM article WHERE author = "就良同学" and cid = 50;
最重要的两个字段possible_keyskey 如果为空 说明发生扫表
possible_keys 可能会用都到的索引
key 实际会用到的索引
rows 预估值
select_type (SIMPLE) 简单查询
idselect_typepossible_keyskeyrows1SIMPLEcid,authorauthor700大数据量下进行的查询 最好先查看执行计划
EXPLAIN SELECT * FROM article WHERE author = "就良同学" and cid = 50;
使用缓存
一个简单使用本地文件来当缓存
public function index()
{
$data= '';
$cache_file = 'article_cid.txt';
if(!file_exists($cache_file)){
//创建文件
file_put_contents($cache_file,'');
}
//读取数据
$data = file_get_contents($cache_file);
if($data){
$data= json_decode($data);
dump($data);
return ;
}
//没有数据
$data=DB::table('article')->where('cid',21)->paginate(20);
file_put_contents($cache_file,json_encode($data));
dump($data);
}缓存策略
旁路缓存策略
Cache-Aside
[*]读:
[*]先到缓存里面读数据,如果读到了就直接返回数据
[*]如果读不到数据,从数据库中读数据,然后存到 cache中
[*]写:
[*]方案一. 更新完数据后 ,删除缓存
[*]方案一. 更新完数据后,不删除缓存,直接更新它
缺陷1:首次请求 数据一定不在 cache里面 ,需要到数据库中查询一下.
解决方方案 : 可以提前把数据放到 cache里面 , '预热数据'
缺陷2:对于写操作比较频繁的数据,直接造成缓存命中率较低。这时候缓存的意义就不大了。
解决方案:
[*]要么不缓存这种数据,要么换缓存策略
[*]数据库数据和Cache中的数据不要求强一致:异步更新DB和Cache,
[*]数据库数据和Cache中的数据必须强一致:
比如说 点赞量 浏览量 这些不是非常重要的数据,用异步来处理
读写穿透策略
Read/Write-Through
[*]读:
[*]从Cache读数据,如果读到数据就直接返回;如果读不到数据的话,从数据库中加载,然后写入到Cache里
[*]写:
[*]先查一下Cache,如果Cache中不存在的话,直接更新数据库;如果Cache中存在,先更新Cache,然后Cache自己更新数据库(相当于同步更新数据库和Cache)
缺点:参考【Cache-Aside(旁路缓存策略)】
异步缓存写入策略
Write-Behind
[*]写:只操作Cache,不操作数据库。什么时候写到数据库?异步写入数据库(可以写一个命令行程序定时把Cache中的数据更新到数据库)
应用场景:文章或商品的浏览量、点赞量、关注量等对数据的实时性要求不高的场景。
什么样的数据不适合放到Cache里?
[*]体积太大的数据不适合。(1、消耗内存。2、网络负载比较高(web和redis不在同一台机器上))
[*]频繁变动的数据,考虑一下是否需要放到缓存中
ab压力测试
apachebench的缩写
原理:ab程序会创建多个并发的访问线程,模拟多个访问者同时对某一个url进行访问。所以,它可以测试的服务器类型可以是apache,也可以是nginx、tomcat服务器,也可以是IIS服务器
注意:ab程序对发出负载的机器要求很低,也就是说它占用的cpu和内存非常低。但是因为它模拟大量的用户请求,所以会给目标服务器造成巨大的负载。比较类似CC攻击。所以我们自己测试的时候,注意控制好请求量。
ab的安装
[*]windows:apache的bin目录里的ab.exe
[*]linux:apache的/usr/bin/ab
[*]只想用ab工具,不想安装Apache,怎么办?
1、服务器是windows server:在开发机上,到apache的bin目录下,拷贝ab.exe到服务器上就行。
2、服务器是centos:安装httpd-tools,yum -y install httpd-toolsab的版本:ab -V
ab程序的参数说明
-n在测试会话中所执行的请求个数。默认时,仅执行一个请求。
-c一次产生的请求个数。默认是一次一个。
-t测试所进行的最大秒数。其内部隐含值是-n 50000,它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。
-p包含了需要POST的数据的文件。
-P对一个中转代理提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即, 是否发送了401认证需求代码),此字符串都会被发送。
-T POST数据所使用的Content-type头信息。
-v设置显示信息的详细程度-4或更大值会显示头信息,3或更大值可以显示响应代码(404,200等),2或更大值可以显示警告和其他信息。
-V显示版本号并退出。
-w以HTML表的格式输出结果。默认时,它是白色背景的两列宽度的一张表。
-i执行HEAD请求,而不是GET。
-x设置<table>属性的字符串。
-X对请求使用代理服务器。
-y设置<tr>属性的字符串。
-z设置<td>属性的字符串。
-C对请求附加一个Cookie:行。其典型形式是name=value的一个参数对,此参数可以重复。
-H对请求附加额外的头信息。此参数的典型形式是一个有效的头信息行,其中包含了以冒号分隔的字段和值的对(如,"Accept-Encoding:zip/zop;8bit")。
-A对服务器提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即,是否发送了401认证需求代码),此字符串都会被发送。
-h显示使用方法。
-d不显示"percentage served within XX table"的消息(为以前的版本提供支持)。
-e产生一个以逗号分隔的(CSV)文件,其中包含了处理每个相应百分比的请求所需要(从1%到100%)的相应百分比的(以微妙为单位)时间。由于这种格式已经“二进制化”,所以比'gnuplot'格式更有用。
-g把所有测试结果写入一个'gnuplot'或者TSV(以Tab分隔的)文件。此文件可以方便地导入到Gnuplot,IDL,Mathematica,Igor甚至Excel中。其中的第一行为标题。
-i执行HEAD请求,而不是GET。
-k启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求。默认时,不启用KeepAlive功能。
-q如果处理的请求数大于150,ab每处理大约10%或者100个请求时,会在stderr输出一个进度计数。此-q标记可以抑制这些信息。ab测试结果
如:ab -c 10 -n 1000 http://lv.php.com/index/index
Concurrency Level:10
模拟的客户端的数量(10个客户端)
Time taken for tests: 19.113 seconds
处理完1000个请求所话费的总时间
Complete requests: 1000
总共完成了多少个请求
Failed requests: 0
失败的请求数
Total transferred: 896000 bytes
所有请求的响应数据长度的总长度,包含每个http请求的头信息(header)和正文数据(body)
HTML transferred: 673000 bytes
所有请求数据的正文的长度
Requests per second: 52.32 [#/sec] (mean)
每秒钟执行的请求数---吞吐率。值越大越好
(第一个)Time per request: 191.131 (mean)
客户端平均每个请求的等待时间
(第二个)Time per request: 19.113 (mean, across all concurrent requests)
服务器端平均每个请求的等待时间
Transfer rate: 45.78 received
数据传输速率。值越大越好
Connection Times (ms)
minmean[+/-sd] median max
(连接时间)Connect: 0 0 0.4 0 1
(处理时间)Processing: 98189 103.5 179 1342
(等待时间)Waiting: 98189 103.5 179 1342
(总时间 )Total: 98190 103.5 179 1342
Percentage of the requests served within a certain time (ms)
50% 179
66% 182
75% 184
80% 186
90% 194
95% 200
98% 245
99% 284
100% 1342 (longest request)
完成本次测试的时间分布,一般关注90%的处理时间es扩展 (elasticsearch )
官网: https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html
文档:
https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/getting-started-php.html
软件下载: https://elasticsearch.cn/download/
扩展安装
composer require elasticsearch/elasticsearch
ElasticSearch重置密码步骤(忘记密码的情况)
1.停止Elasticsearch服务
2.编辑elasticsearch.yml文件,设置以下两项为false;
xpack.security.enabled: false
xpack.security.transport.ssl.enabled: false
3.重启es服务,删除.security-7索引
curl -XDELETE -u elastic:changeme http://localhost:9200/.security-7
3.关闭ES服务设置以下两项为true;
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
4.重启es服务,进入es的bin目录下
./elasticsearch-setup-passwords interactive
依次设置每个账号密码即可获取密码:
进入es安装目录的bin文件中执行一下命令会返回最新的密码
命令 : elasticsearch-reset-password -u elastic
默认用户名: elastic
密码:QhW4NJSQGzP13o2kVI6o
修改密码的其他命令
https://devpress.csdn.net/cloud-native/65195642aae4e179c0b77cc4.html
开启各种安全验证后需要ssl证书才能正常登录
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12证书位置 config\certs\http_ca.crt
config/es.php文件配置
return [
'host'=>['https://127.0.0.1:9200'],
'user'=>'elastic',
'pwd'=>'VX_IuVXQ6_WcfmBO8Mxc',
];//初始化
use Elastic\Elasticsearch\ClientBuilder;
use Elastic\Elasticsearch\Exception\ClientResponseException;
use Elastic\Elasticsearch\Exception\ServerResponseException;
$client = ClientBuilder::create()
->setHosts(config('es.host'))
->setBasicAuthentication(config('es.user'), config('es.pwd'))
->setCABundle('cacrt/http_ca.crt')
->build();分词器要下载对应的版本
https://github.com/medcl/elasticsearch-analysis-ik
下载地址
https://github.com/medcl/elasticsearch-analysis-ik/releases
下载后解压到 plugins/ik 目录下面 重启 es即可
//创建映射
curl -XPOST http://localhost:9200/index/_mapping -H 'Content-Type:application/json' -d'
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}'案例 增删查改
页:
[1]