PHP面试过程控制原理中的高并发考点应该如何解答

php中,高并发状态下文件的读写
对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失。
例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这
里B也正好也打开了同一个文件,也准备更新里面的数据。当A把写好的文件保存时,这里其实B已经打开了文件。但当B再把文件保存回去时,这里已经造成了数
据的丢失,因为这里B用户完全不知道它所打开的文件在它对其进行更改时,A用户也更改了这个文件,所以最后B用户保存更改时,用户A的更新就被会丢失。
对于这样的问题,一般的解决方案时当一进程对文件进行操作时,首先对其它进行加锁,意味着这里只有该进程有权对文件进行读取,其它进程如果现在读,是完全
没有问题,但如果这时有进程试图想对其进行更新,会遭到操作拒绝,先前对文件进行加锁的进程这时如果对文件的更新操作完毕,这就释放独占的标识,这时文件
又恢复到了可更改的状态。接下来同理,如果那个进程在操作文件时,文件没有加锁,这时,它就可以放心大胆的对文件进行锁定,独自享用。
所以一般的方案会是:
$fp = fopen("/tmp/lock.txt", "w+");
if (flock($fp, LOCK_EX)) {
fwrite($fp, "Write something here\n");
flock($fp, LOCK_UN);
echo "Couldn't lock the file !";
fclose($fp);
但在PHP中,flock似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。好像在很多linux/unix系统中,都会有这样的情况发生。
所以使用flock之前,一定要慎重考虑。
那么就没有解决方案了吗?其实也不是这样的。如果flock()我们使用得当,完全可能解决死锁的问题。当然如果不考虑使用flock()函数,也同样会有很好的解决方案来解决我们的问题。
经过我个人的搜集和总结,大致归纳了解决方案有如下几种。
方案一:对文件进行加锁时,设置一个超时时间.
大致实现如下:
if($fp = fopen($fileName, 'a')) {
$startTime = microtime();
$canWrite = flock($fp, LOCK_EX);
if(!$canWrite) usleep(round(rand(0, 100)*1000));
} while ((!$canWrite) && ((microtime()-$startTime) & 1000));
if ($canWrite) {
fwrite($fp, $dataToSave);
fclose($fp);
超时设置为1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。
方案二:不使用flock函数,借用临时文件来解决读写冲突的问题。
大致原理如下:
1。将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。
2。当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。
3。如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。
4。但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回false,说明文件这时有其它进程在进行操作。
大致实现代码如下:
$dir_fileopen = "tmp";
function randomid() {
return time().substr(md5(microtime()), 0, rand(5, 12));
function cfopen($filename, $mode) {
global $dir_
clearstatcache();
$id = md5(randomid(rand(), TRUE));
$tempfilename = $dir_fileopen."/".$id.md5($filename);
} while(file_exists($tempfilename));
if (file_exists($filename)) {
$newfile =
copy($filename, $tempfilename);
$newfile =
$fp = fopen($tempfilename, $mode);
return $fp ? array($fp, $filename, $id, @filemtime($filename)) :
function cfwrite($fp,$string) { return fwrite($fp[0], $string); }
function cfclose($fp, $debug = "off") {
global $dir_
$success = fclose($fp[0]);
clearstatcache();
$tempfilename = $dir_fileopen."/".$fp[2].md5($fp[1]);
if ((@filemtime($fp[1]) == $fp[3]) || ($fp[4]==true && !file_exists($fp[1])) || $fp[5]==true) {
rename($tempfilename, $fp[1]);
unlink($tempfilename);
//说明有其它进程 在操作目标文件,当前进程被拒绝
$success =
$fp = cfopen('lock.txt','a+');
cfwrite($fp,"welcome to beijing.\n");
fclose($fp,'on');
对于上面的代码所使用的函数,需要说明一下:
1.rename();重命名一个文件或一个目录,该函数其实更像linux里的mv。更新文件或者目录的路径或名字很方便。
但当我在window测试上面代码时,如果新文件名已经存在,会给出一个notice,说当前文件已经存在。但在linux下工作的很好。
2.clearstatcache();清除文件的状态.php将缓存所有文件属性信息,以提供更高的性能,但有时,多进程在对文件进行删除或者更新操作
时,php没来得及更新缓存里的文件属性,容易导致访问到最后更新时间不是真实的数据。所以这里需要使用该函数对已保存的缓存进行清除。
方案三:对操作的文件进行随机读写,以降低并发的可能性。
在对用户访问日志进行记录时,这种方案似乎被采用的比较多。
先前需要定义一个随机空间,空间越大,并发的的可能性就越小,这里假设随机读写空间为[1-500],那么我们的日志文件的分布就为log1~到log500不等。每一次用户访问,都将数据随机写到log1~log500之间的任一文件。
在同一时刻,有2个进程进行记录日志,A进程可能是更新的log32文件,而B进程呢?则此时更新的可能就为log399.要知道,如果要让B进程也操作log32,概率基本上为1/500,差不多约等于零。
在需要对访问日志进行分析时,这里我们只需要先将这些日志合并,再进行分析即可。
使用这种方案来记录日志的一个好处时,进程操作排队的可能性比较小,可以使进程很迅速的完成每一次操作。
方案四:将所有要操作的进程放入一个队列中。然后专门放一个服务完成文件操作。
队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系。
对于以前几种方案,各有各的好处!大致可能归纳为两类:
1。需要排队(影响慢)比如方案一、二、四
2。不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三。因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了。试想一
下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程。但采取方案一、二就完全不一样,虽然写的时间需要等待(当获
取锁不成功时,会反复获取),但读文件是很方便的。添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。PHP秒杀系统 高并发高性能的极致挑战
,需要的来_高并发吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:182贴子:
PHP秒杀系统 高并发高性能的极致挑战
,需要的来
PHP秒杀系统 高并发高性能的极致挑战
,需要的来
大流量,高并发的网站架构是一个系统工程,主要围绕解耦化,服务化以及缓存化的思路,整体做到水平扩展,从而支撑海量访问.
谢谢楼主!
好人一生平安
楼主来一份谢谢哈
我也需要一份,谢谢楼主。
谢谢楼主,我需要一份,
谢谢楼主!
楼主还在吗
需要的自己加过来,人太多发不过来了
给我也来一份
楼主求一份谢谢
作者,跪求一份儿,
楼主还有吗
感谢楼主大大
新年发大财
devil_ 感谢楼主
,感谢老哥!!!
贴吧热议榜
使用签名档&&
保存至快速回贴直击 百度 新浪 PHP MySql FE常见经典实际面试题(中)直击 百度 新浪 PHP MySql FE常见经典实际面试题(中)Zhangmai工作室百家号好,接着上次的讲。MySQL:mysql -u root -pgrant(revoke) all (privileges) on testdb to dba@'192.168.1.%'grant select on testdb.* to dba@ -- dba 可以查询 testdb 中的表FLUSH PRIVILEGESalter table student add role tinyint unsigned DEFAULT 1 COMMENT '创建者角色 1客服 2商家运营'; //增加字段alter table student change old_field new_field char(10) //更改字段alter table student modify column status tinyint(3) UNSIGNED DEFAULT '0'; // 改字段类型create index IDX_testNoPK_Name on testNoPK (name); //新建索引alter table old_name rename to new_ //改表名alter TABLE table_name DROP field_ // 删字段1. 关键字顺序:(8)SELECT(9)DISTINCT(1)FROM(3)JOIN(2)ON(4)WHERE(5)GROUP BY(6)WITH {CUTE|ROLLUP}(7)HAVING(10)ORDER BY(11)LIMIT每步关键字执行的结果都会形成一个虚表,编号大的关键字执行的动作都是在编号小的关键字执行结果所得的虚表上进行(或者说编号大的关键字处理的对象是编号小的关键执行过后得到的虚表),以此类推。2. 备份:mysqldump : 适用于所有的存储引擎, 支持温备、完全备份、部分备份、对于InnoDB存储引擎支持热备xtrabackup: 一款非常强大的InnoDB/XtraDB热备工具, 支持完全备份、增量备份mysqldump:mysqldump [options] db_name [tbl_name ...] 恢复需要手动CRATE DATABASESmysqldump [options] --databases db_name ... 恢复不需要手动创建数据库mysqldump [options] --all-databases 恢复不需要手动创建数据库mysqldump -u用户名 -p密码 -d(结构) -t(数据) 数据库名 表名 --where="筛选条件" & 导出文件路径mysqldump --all-databases --lock-all-tables & backup.sql #备份数据库到backup.sql文件中cp /var/lib/mysql/mysql-bin.000003 /root #备份二进制文件service mysqld stop #停止MySQLrm -rf /var/lib/mysql/* #删除所有的数据文件service mysqld start #启动MySQLsource backup.sql #恢复数据,所需时间根据数据库时间大小而定3. 数据库范式:第一范式就是无重复的列,指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值第二范式就是属性完全依赖于主键,而不是主键部分,假如实例中存在多个属性组成的主键 order_id,project_id,project_name,price第三范式就是非主键列必须直接依赖于主键,不能存在传递依赖 order_id,ordersn,user_id,user_name,user_city4. mysql分区分表什么是分表,从表面意思上看呢,就是把一张表分成N多个小表http://www.open-open.com/lib/view/open7.html什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同一个磁盘上,也可以在不同的磁盘上a)mysql的分表是真正的分表,一张表分成很多表后,每一个小表都是完正的一张表,都对应三个文件,一个.MYD数据文件,.MYI索引文件,.frm表结构文件。分表呢是利用了merge存储引擎(分表的一种),alluser 是总表,下面有二个分表,user1,user2。他们二个都是独立的表,取数据的时候,我们可以通过总表来取。这里总表是没有.MYD,.MYI这二个 文件的,也就是说,总表他不是一张表,没有数据,数据都放在分表里面b)分区不一样,一张大表进行分区后,他还是一张表,不会变成二张表,但是他存放数据的区块变多了。根据一定的规则把数据文件和索引文件进行了分割c)分表后,单表的并发能力提高了,磁盘I/O性能也提高了。并发能力为什么提高了 呢,因为查寻一次所花的时间变短了,如果出现高并发的话,总表可以根据不同的查询,将并发压力分到不同的小表里面。d)mysql提出了分区的概念,我觉得就想突破磁盘I/O瓶颈,想提高磁盘的读写能力,来增加mysql性能。在这一点上,分区和分表的测重点不同,分表重点是存取数据时,如何提高mysql并发能力上;而分区呢,如何突破磁盘的读写能力,从而达到提高mysql性能的目的。mysql的分表根据业务需求,使用不同维度进行分表,如用户id或者时间或区间等维度方式进行模运算,计算数据库集群个数,定位数据库表位置垂直拆分:解决问题:表与表之间的io竞争不解决问题:单表中数据量增长出现的压力方案: 把产品表和用户表放到一个server上 订单表单独放到一个server上水平拆分:解决问题:单表中数据量增长出现的压力不解决问题:表与表之间的io争夺mysql中myisam与innodb的区别1&.InnoDB支持事物,而MyISAM不支持事物2&.InnoDB支持行级锁,而MyISAM支持表级锁3&.InnoDB支持MVCC, 而MyISAM不支持4&.InnoDB支持外键,而MyISAM不支持5&.InnoDB不支持全文索引,而MyISAM支持select count(*)哪个更快,为什么myisam更快,因为myisam内部维护了一个计数器,可以直接调取5. 索引唯一索引:不允许两行具有相同的索引值主键索引:主键索引是唯一索引的特殊类型。数据库表通常有一列或列组合,其值用来唯一标识表中的每一行。该列称为表的主键。它们的一些比较:(1)对于主健/unique constraint , oracle/sql server/mysql等都会自动建立唯一索引;(2)主键不一定只包含一个字段,所以如果你在主键的其中一个字段建唯一索引还是必要的;(3)主健可作外健,唯一索引不可;(4)主健不可为空,唯一索引可;(5)主健也可是多个字段的组合;(6)主键与唯一索引不同的是:a.不允许为空;b.每个表只能有一个。mysql索引的实现:在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的MyISAM索引实现:(非聚集索引)MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是(主键+地址)数据记录的地址,根据地址读取相应的数据记录InnoDB索引实现:(聚集索引)InnoDB也使用B+Tree作为索引结构从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。(主键+地址)而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。(主键+数据记录)这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。B-tree,B是balance,一般用于数据库的索引。使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。而B+tree是B-tree的一个变种,MySQL就普遍使用B+tree实现其索引结构。6. innodb的事务与日志的实现方式错误日志:记录出错信息,也记录一些警告信息或者正确的信息慢查询日志:设置一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询的日志文件中。二进制日志:记录对数据库执行更改的所有操作查询日志:记录所有对数据库请求的信息,不论这些请求是否得到了正确的执行7. 数据库事务的四个特性及含义原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。一致性:在事务开始之前和事务结束以后,数据库的完整性约束(指数据库中数据的正确性和相容性,其目的是防止垃圾数据的进出)没有被破坏。隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。事物的实现:1、四个特性2、隔离性的实现:事务的隔离性由存储引擎的锁来实现3、原子性和持久性的实现:redo log 称为重做日志(也叫事务日志),用来保证事务的原子性和持久性.redo恢复提交事务修改的页操作,redo是物理日志,页的物理修改操作.4、一致性的实现:undo log 用来保证事务的一致性. undo 回滚行记录到某个特定版本,undo 是逻辑日志,根据每行记录进行记录.undo 存放在数据库内部的undo段,undo段位于共享表空间内.undo 只把数据库逻辑的恢复到原来的样子.8. InnoDB的日志:逻辑日志和物理日志.逻辑日志:有关操作的信息日志成为逻辑日志. 如binlog(二进制日志)物理日志:新值和旧值的信息日志称为物理日志区别:binlog记录的是sql语句, 重做日志则记录的是对每个页的修改物理日志记录的是修改页的的详情,逻辑日志记录的是操作语句. 物理日志恢复的速度快于逻辑日志慢查询日志:slow_query_log: on 开启slow_query_log_file: /tmp/slow_query.loglong_query_time: 10 10s9. explain: sql性能分析explain select * from user where id = 1;id select_type table type possible_keys key key_len ref rows Extraid 表示查询中执行 select 子句或操作表的顺序,id 值越大优先级越高,越先被执行select_type 查询类型{simple:表示不需要union操作或者不包含子查询的简单select查询。有连接查询时,外层的查询为simple,且只有一个primary:一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary。且只有一个union:union连接的两个select查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是uniondependent union:与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响union result:包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为nullsubquery:除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquerydependent subquery:与dependent union类似,表示这个subquery的查询要受到外部表查询的影响derived:from字句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select}table输出行所引用的表type 重要的项,显示连接使用的类型,按最 优到最差的类型排序system表仅有一行(=系统表)。这是 const 连接类型的一个特例。constconst 用于用常数值比较 PRIMARY KEY 时。当 查询的表仅有一行时,使用 System。eq_refconst 用于用常数值比较 PRIMARY KEY 时。当 查询的表仅有一行时,使用 System。ref连接不能基于关键字选择单个行,可能查找 到多个符合条件的行。 叫做 ref 是因为索引要 跟某个参考值相比较。这个参考值或者是一 个常数,或者是来自一个表里的多表查询的 结果值。ref_or_null如同 ref, 但是 MySQL 必须在初次查找的结果 里找出 null 条目,然后进行二次查找。index_merge说明索引合并优化被使用了。unique_subquery在某些 IN 查询中使用此种类型,而不是常规的 ref:value IN (SELECT primary_key FROM single_table WHERE some_expr)index_subquery在 某 些 IN 查 询 中 使 用 此 种 类 型 , 与 unique_subquery 类似,但是查询的是非唯一 性索引: value IN (SELECT key_column FROM single_table WHERE some_expr)range只检索给定范围的行,使用一个索引来选择 行。key 列显示使用了哪个索引。当使用=、 &&、&、&=、&、&=、IS NULL、&=&、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可 以使用 range。index全表扫描,只是扫描表的时候按照索引次序 进行而不是行。主要优点就是避免了排序, 但是开销仍然非常大。all最坏的情况,从头到尾全表扫描。possible_keys指出 MySQL 能在该表中使用哪些索引有助于 查询。如果为空,说明没有可用的索引。keyMySQL 实际从 possible_key 选择使用的索引。 如果为 NULL,则没有使用索引。key_len 使用的索引的长度。在不损失精确性的情况 下,长度越短越好。ref 显示索引的哪一列被使用了rows MYSQL 认为必须检查的用来返回请求数据的行数extraA:distinct:在select部分使用了distinc关键字B:no tables used:不带from字句的查询或者From dual查询C:使用not in()形式子查询或not exists运算符的连接查询,这种叫做反连接。即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表。D:using filesort:排序时无法使用到索引时,就会出现这个。常见于order by和group by语句中E:using index:查询时不需要回表查询,直接通过索引就可以获取查询的数据。F:using join buffer(block nested loop),using join buffer(batched key accss):5.6.x之后的版本优化关联查询的BNL,BKA特性。主要是减少内表的循环数量以及比较顺序地扫描查询。G:using sort_union,using_union,using intersect,using sort_intersection:using intersect:表示使用and的各个索引的条件时,该信息表示是从处理结果获取交集using union:表示使用or连接各个使用索引的条件时,该信息表示从处理结果获取并集using sort_union和using sort_intersection:与前面两个对应的类似,只是他们是出现在用and和or查询信息量大时,先查询主键,然后进行排序合并后,才能读取记录并返回。H:using temporary:表示使用了临时表存储中间结果。临时表可以是内存临时表和磁盘临时表,执行计划中看不出来,需要查看status变量,used_tmp_table,used_tmp_disk_table才能看出来。I:using where:表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server层进行过滤。查询条件中分为限制条件和检查条件,5.6之前,存储引擎只能根据限制条件扫描数据并返回,然后server层根据检查条件进行过滤再返回真正符合查询的数据。5.6.x之后支持ICP特性,可以把检查条件也下推到存储引擎层,不符合检查条件和限制条件的数据,直接不读取,这样就大大减少了存储引擎扫描的记录数量。extra列显示using index conditionJ:firstmatch(tb_name):5.6.x开始引入的优化子查询的新特性之一,常见于where字句含有in()类型的子查询。如果内表的数据量比较大,就可能出现这个K:loosescan(m..n):5.6.x之后引入的优化子查询的新特性之一,在in()类型的子查询中,子查询返回的可能有重复记录时,就可能出现这个10. 主从服务器大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够。到了数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢失的话,后果更是 不堪设想。这时候,我们会考虑如何减少数据库的联接,一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如:memcached,如果资金丰厚的话,必然会想到假设服务器群,来分担主数据库的压力。利用MySQL主从配置,实现读写分离,减轻数据库压力。这种方式,在如今很多网站里都有使用,也不是什么新鲜事情,概述:搭设一台Master服务器(win8.1系统,Ip:192.168.0.1),搭设两台Slave服务器(虚拟机——一台Ubuntu,一台 Windows Server 2003)原理:主服务器(Master)负责网站NonQuery操作,从服务器负责Query操作,用户可以根据网站功能模特性块固定访问Slave服务器,或者自己写个池或队列,自由为请求分配从服务器连接。主从服务器利用MySQL的二进制日志文件,实现数据同步。二进制日志由主服务器产生,从服务器响应获取同步数据库。11. 设计表慎重选择表名关于编码的设定(GBK/GB2312,TF8)关于表引擎的选择(MYISAM,INNODB,CSV, MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能)关于属性数据类型的选择关于索引数据库优化慢查询 记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息,加快检索速度1、选取最适用的字段属性在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。2、使用连接(JOIN)来代替子查询(Sub-Queries)连接(JOIN).. 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的.换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为: A.aID = B.bID).B表记录不足的地方均为NULL.3、事务尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来4、使用外键锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。例如5、使用索引索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(), MIN()和ORDERBY这些命令的时候,性能提高更为明显。那该对哪些字段建立索引呢?一般说来,索引应建立在那些将用于JOIN, WHERE判断和ORDER BY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引6、优化的查询语句(1)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。(2)应尽量避免在 where 子句中使用!=或&&操作符,否则将引擎放弃使用索引而进行全表扫描。(3)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描(4)应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描(5)in 和 not in 也要慎用,否则会导致全表扫描,对于连续的数值,能用 between 就不要用 in 了很多时候用 exists 代替 in 是一个好的选择:select num from a where num in(select num from b)用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)(6)应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where num/2=100应改为:select id from t where num=100*2(7)任何地方都不要使用 select * from t ,用具体的字段列表代替“*”消息队列:MQ消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改另一个进程,为了隔离这两个进程,在两进程间抽离出一层(一个模块),所有两进程之间传递的消息,都必须通过消息队列来传递,单独修改某一个进程,不会影响另一个;不同进程(process)之间传递消息时,为了实现标准化,将消息的格式规范化了,并且,某一个进程接受的消息太多,一下子无法处理完,并且也有先后顺序,必须对收到的消息进行排队,因此诞生了事实上的消息队列;解决问题:1系统解耦:项目开始时,无法确定最终需求,不同进程间,添加一层,实现解耦,方便今后的扩展。2消息缓存:系统中,不同进程处理消息速度不同,MQ,可以实现不同Process之间的缓冲,即,写入MQ的速度可以尽可能地快,而处理消息的速度可以适当调整(或快、或慢)。3排序保证:即,满足队列的FIFO,先入先出策略;4异步通信:很多场景下,不会立即处理消息,这是,可以在MQ中存储message,并在某一时刻再进行处理;场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式2.3流量削锋流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。可以控制活动的人数可以缓解短时间内高流量压垮应用用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面秒杀业务根据消息队列中的请求信息,再做后续处理RabbitMQConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。Connection是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑。ConnectionFactory为Connection的制造工厂。Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。Queue(队列)是RabbitMQ的内部对象,用于存储消息RabbitMQ中的消息都只能存储在Queue中,生产者P生产消息并最终投递到Queue中,消费者C可以从Queue中获取消息并消费。多个消费者可以订阅同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。消费者在消费完消息后发送一个回执给RabbitMQ,RabbitMQ收到消息回执(Message acknowledgment)后才将该消息从Queue中移除;如果RabbitMQ没有收到回执并检测到消费者的RabbitMQ连接断开,则RabbitMQ会将该消息发送给其他消费者(如果存在多个消费者)进行处理。多个消费者同时订阅同一个Queue中的消息,Queue中的消息会被平摊给多个消费者。这时如果每个消息的处理时间不同,就有可能会导致某些消费者一直在忙,而另外一些消费者很快就处理完手头工作并一直空闲的情况。我们可以通过设置prefetchCount来限制Queue每次发送给每个消费者的消息数。生产者将消息投递到Queue中,实际上这在RabbitMQ中这种事情永远都不会发生。实际的情况是,生产者将消息发送到Exchange(交换器,X),由Exchange将消息路由到一个或多个Queue中(或者丢弃)。Exchange是按照什么逻辑将消息路由到Queue的:1、routing key:生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则,而这个routing key需要与Exchange Type及binding key联合使用才能最终生效。2、Binding:RabbitMQ中通过Binding将Exchange与Queue关联起来,这样RabbitMQ就知道如何正确地将消息路由到指定的Queue了。3、Binding key:在绑定(Binding)Exchange与Queue的同时,一般会指定一个binding key;生产者将消息发送给Exchange时,一般会指定一个routing key;当binding key与routing key相匹配时,消息将会被路由到对应的Queue中消费者:创建连接–&创建channel–&创建交换机–&创建队列–&绑定交换机/队列/路由键–&接收消息$conn_args = array('host' =& '192.168.1.93','port' =& '5672','login' =& 'guest','password' =& 'guest','vhost'=&'/');$e_name = 'e_linvo'; //交换机名$q_name = 'q_linvo'; //队列名$k_route = 'key_1'; //路由key//创建连接和channel$conn = new AMQPConnection($conn_args);if (!$conn-&connect()) {die("Cannot connect to the broker!\n");$channel = new AMQPChannel($conn);//创建交换机$ex = new AMQPExchange($channel);$ex-&setName($e_name);$ex-&setType(AMQP_EX_TYPE_DIRECT); //direct类型$ex-&setFlags(AMQP_DURABLE); //持久化echo "Exchange Status:".$ex-&declare()."\n";//创建队列$q = new AMQPQueue($channel);$q-&setName($q_name);$q-&setFlags(AMQP_DURABLE); //持久化echo "Message Total:".$q-&declare()."\n";//绑定交换机与队列,并指定路由键echo 'Queue Bind: '.$q-&bind($e_name, $k_route)."\n";//阻塞模式接收消息echo "Message:\n";while(True){$q-&consume('processMessage');//$q-&consume('processMessage', AMQP_AUTOACK); //自动ACK应答$conn-&disconnect();/*** 消费回调函数* 处理消息*/function processMessage($envelope, $queue) {$msg = $envelope-&getBody();echo $msg."\n"; //处理消息$queue-&ack($envelope-&getDeliveryTag()); //手动发送ACK应答生产者:发送消息逻辑:创建连接–&创建channel–&创建交换机对象–&发送消息//配置信息//$q_name = 'q_linvo'; //无需队列名//消息内容$message = "TEST MESSAGE! 测试消息!";//创建交换机对象//发送消息//$channel-&startTransaction(); //开始事务for($i=0; $i&5; ++$i){echo "Send Message:".$ex-&publish($message, $k_route)."\n";//$channel-&commitTransaction(); //提交事务需要注意的地方是:queue对象有两个方法可用于取消息:consume和get。前者是阻塞的,无消息时会被挂起,适合循环中使用;后者则是非阻塞的,取消息时有则取,无则返回false。web server:分析问题1、 架构设计是否合理2、 数据库设计是否合理3、 代码是否存在性能方面的问题4、 系统中是否有不合理的内存使用方式5、 系统中是否存在不合理的线程同步方式6、 系统中是否存在不合理的资源竞争尽量静态化程序功能规则,禁止外部的盗链代码静态资源分服务器,分流缓存,资源共享优化sql,db读写分离负载均衡,集群队列负载(web服务器的吞吐量,对于网络应用数据的处理能力)--cpu 内存,磁盘io带宽 (单位时间(一般指的是1秒钟)内能传输的数据量)吞吐量是指单位时间内系统处理的请求数量,体现系统的整体处理能力QPS(TPS):每秒钟request/事务 数量并发数: 系统同时处理的request/事务数负载均衡(Load Balance)是集群技术(Cluster)的一种应用。负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力。目前最常见的负载均衡应用是Web负载均衡。根据实现的原理不同,常见的web负载均衡技术包括:DNS轮询、IP负载均衡和CDN。其中IP负载均衡可以使用硬件设备或软件方式来实现。任何的负载均衡技术都要想办法建立某种一对多的映射机制:一个请求的入口映射到多个处理请求的节点,从而实现分而治之(Divide and Conquer)。这种映射机制使得多个物理存在对外体现为一个虚拟的整体,对服务的请求者屏蔽了内部的结构。1、DNS轮询是最简单的负载均衡方式。以域名作为访问入口,通过配置多条DNS A记录使得请求可以分配到不同的服务器。2、CDN(Content Delivery Network,内容分发网络)。通过发布机制将内容同步到大量的缓存节点,并在DNS服务器上进行扩展,找到里用户最近的缓存节点作为服务提供节点。3、IP负载均衡是基于特定的TCP/IP技术实现的负载均衡。比如NAT、DR、Turning等(1)NAT网络地址转换,将IP 数据包头中的IP 地址转换为另一个IP 地址的过程五层网络协议栈应用层、传输层、网络层、链路层和物理层应用层: http、ftp、telnet、smtp、pop3应用层协议分布在多个端系统上,一个端系统中的应用程序使用协议与另一个端系统中的应用程序交换信息分组。我们将这种位于应用层的信息分组称为报文(message)传输层:提供应用程序进程间的数据传输服务,这一层上主要定义了两个传输协议,传输控制协议即TCP和用户数据报协议UDP,都能传输应用层报文.TCP向它的应用程序提供了面向连接的服务。这种服务包括了应用层报文向目的地的确保传递和流量控制(即发送方/接收方速率匹配)。TCP也将长报文划分为短报文,并提供拥塞控制机制,因此当网络拥塞时,源抑制其传输速率。UDP协议向它的应用程序提供无连接服务网络层:(ip协议)将称为数据报(datagram)的网络层分组从一合主机移动到另一台主机。源主机中的因特网传输层协议(TCP或UDP)向网络层递交运输层报文段和目的地址,就像你向邮政信件提供目的地址一样数据链路层:负责将IP数据报封装成合适在物理网络上传输的帧格式并传输,或将从物理网络接收到的帧解封,取出IP数据报交给网络层。网络层通过一系列路由器在源和目的地之间发送分组。为了将分组从一个节点(主机或路由器)移动到路径上的下一个节点,网络层必须依靠链路层的服务。特别是在每个节点,网络层将数据报下传给链路层,链路层沿着路径将数据报传递给下一个节点。在该下个节点,链路层将数据报上传给网络层。物理层:负责将比特流在结点间传输,即负责物理传输。该层的协议既与链路有关也与传输介质有关。链路层的任务是将整个帧从一个网络元素移动到邻近的网络元素,而物理层的任务是将该帧中的一个一个比特从一个节点移动到下一个节点。该层中的协议仍然是链路相关的,并且进一步与链路(例如,双绞铜线、单模光纤)的实际传输媒体相关。例如,以太网具有许多物理层协议:关于双绞铜线的,关于同轴电缆的,关于光纤的,等等。HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的,端到端的通信tcp协议: 对应于传输层ip协议: 对应于网络层TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。HTTP:HTTP协议即超文本传送协议,是基于请求与响应模式的无状态,无连接,应用层的协议,是建立在TCP协议之上的一种应用。端到端通信GET, POST, PUT, DELET短连接:客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接无状态:同一个客户端的请求之间没有对应关系,对http服务器来说,它并不知道这两个请求来自同一个客户端。为了解决这个问题,Web程序引入了Cookie机制来维护状态.基本过程:客户端请求连接,服务器应答,客户端请求,服务器响应,关闭连接TCP:面向连接的传输层协议,在正式收发数据前,必须和对方建立可靠的连接,进程之间通信建立起一个TCP连接需要经过“三次握手”:第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”.四次挥手:第一次挥手:客户端向服务器发送一个FIN报文段;此时,客户端进入FIN_WAIT_1状态;这表示客户端没有数据要发送给服务器了;第二次挥手:服务器收到了客户端发送的FIN报文段,向客户端回一个ACK报文段,客户端进入FIN_WAIT_2状态;服务器告诉客户端,我“同意”你的关闭请求;第三次挥手:服务器向客户端发送FIN报文段,请求关闭连接,同时服务器进入LAST_ACK状态;第四次挥手:客户端收到主服务器发送的FIN报文段,向服务器发送ACK报文段,然后客户端进入TIME_WAIT状态;服务器收到客户端的ACK报文段以后,就关闭连接;此时,客户端等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,客户端也可以关闭连接了。UDP:面向非连接的UDP协议,面向非连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送3、SOCKET原理3.1套接字(socket)概念套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。3.2 建立socket连接建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。FPMCGI 是 Web Server 与后台语言交互的协议,有了这个协议,开发者可以使用任何语言处理 Web Server 发来的请求,动态的生成内容。但 CGI 有一个致命的缺点,那就是每处理一个请求都需要 fork 一个全新的进程,随着 Web 的兴起,高并发越来越成为常态,这样低效的方式明显不能满足需求。就这样,FastCGI 诞生了,CGI 很快就退出了历史的舞台。FastCGI,顾名思义为更快的 CGI,它允许在一个进程内处理多个请求,而不是一个请求处理完毕就直接结束进程,性能上有了很大的提高。至于 FPM (FastCGI Process Manager),它是 FastCGI 的实现,任何实现了 FastCGI 协议的 Web Server 都能够与之通信。FPM 之于标准的 FastCGI,也提供了一些增强功能,具体可以参考官方文档:PHP: FPM Installation。FPM 是一个 PHP 进程管理器,包含 master 进程和 worker 进程两种进程:master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个 (具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方,下图是我本机上 fpm 的进程情况,1一个 master 进程,3个 worker 进程:从 FPM 接收到请求,到处理完毕,其具体的流程如下:FPM 的 master 进程接收到请求master 进程根据配置指派特定的 worker 进程进行请求处理,如果没有可用进程,返回错误,这也是我们配合 Nginx 遇到502错误比较多的原因。worker 进程处理请求,如果超时,返回504错误请求处理结束,返回结果PM 从接收到处理请求的流程就是这样了,那么 Nginx 又是如何发送请求给 fpm 的呢?这就需要从 Nginx 层面来说明了。我们知道,Nginx 不仅仅是一个 Web 服务器,也是一个功能强大的 Proxy 服务器,除了进行 http 请求的代理,也可以进行许多其他协议请求的代理,包括本文与 fpm 相关的 fastcgi 协议。为了能够使 Nginx 理解 fastcgi 协议,Nginx 提供了 fastcgi 模块来将 http 请求映射为对应的 fastcgi 请求。在这个配置文件中,我们新建了一个虚拟主机,监听在 80 端口,Web 根目录为 /home/rf/projects/wordpress。然后我们通过 location 指令,将所有的以 .php 结尾的请求都交给 fastcgi 模块处理,从而把所有的 php 请求都交给了 fpm 处理,从而完成 Nginx 到 fpm 的闭环。serverlisten 80;server_name www.test.com www.aaa.com www.bbb.com#deny 127.0.0.1;#deny 1.1.1.1;if ($host != 'www.test.com') {rewrite ^/(.*)$ http://www.test.com/$1index index.html index.htm index.root /data/access_log /tmp/access.if ($http_user_agent ~ 'baidu|1111') {return 403;#对论坛admin.php的设置location ~ .*admin\.php$ {allow 127.0.0.1;include fastcgi_#fastcgi_pass unix:/tmp/php-fcgi.fastcgi_pass 127.0.0.1:9000;fastcgi_index index.fastcgi_param SCRIPT_FILENAME /data/www$fastcgi_script_location / {缓存系统:基于内存的分布式存储系统memcache: 用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能,采用hash表的方式每条数据由key和value组成将内存空间分为一组slab每个slab下又有若干个page,每个page默认是1M,如果一个slab占用100M内存的话,那么这个slab下应该有100个page每个page里面包含一组chunk,chunk是真正存放数据的地方,同一个slab里面的chunk的大小是固定的有相同大小chunk的slab被组织在一起,称为slab_classMemCache的LRU算法不是针对全局的,是针对slab的分布式mc:普通hash(取余);一致性hash(0 - 2^32-1环,key和server都做hash处理,分布在环上,key存储在顺时针最近server上,增加或减少server,数据失效降到最低)Mongo:基于分布式文件存储的数据库、文档是 MongoDB 中数据的基本单位,类似于关系数据库中的行(但是比行复杂)。多个键及其关联的值有序地放在一起就构成了文档数据以BSON(二进制JSON)格式存储在磁盘中数据类型:整形,字符串,数组,二进制,日期,文档支持查询主要解决海量数据的访问效率问题Redis数据类型String: get、set、incr、decrHash:比如我们要存储一个用户信息对象数据,包含以下信息:用户ID,为查找的key,存储的value用户对象包含姓名name,年龄age,生日birthday 等信息Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,如:hmset user:001 name "李三" age 18 birthday ""List: lpush,rpush,lpop,rpop,lrange list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作Set: set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的Redis持久化方式:一是快照(snapshotting),二是只追加文件(append-only file AOF)快照:快照的核心原理就是把redis在某个时间内存内的所有数据都写入硬盘 save 900 1 #900秒内如果超过1个key被修改,则发起快照保存AOF: 系统会把所有的redis数据进行的写操作的命令记录到硬盘上,这样一来恢复的时候,再执行一次命令就OK了aof优势在于:就算出问题了,最多丢失1秒内的更新数据aof的劣势:aof文件的体积可能会很大(可能比快照文件还大),另一方面,系统重启的时候回从aof里读命令,如果aof文件太大,读命令也就要还很久使用场景:会话缓存,队列(list 和 set),排行榜/计数器(在内存中对数字进行递增或递减的操作实现的非常好)。还有FE,算法和高并发处理的之后会整理处理,敬请期待。本文由百家号作者上传并发布,百家号仅提供信息发布平台。文章仅代表作者个人观点,不代表百度立场。未经作者许可,不得转载。Zhangmai工作室百家号最近更新:简介:有些东西不一定坏。作者最新文章相关文章}

我要回帖

更多关于 化工分离过程试题 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信