网站高并发解决方案-理论知识2

网站高并发解决方案-理论知识2

本文转自:http://www.php20.cn/article/sw/%E9%AB%98%E5%B9%B6%E5%8F%91/244

一、CDN代理层

在高并发下,为了解决带宽问题,全站必须做前后端分离操作,所有前端资源都可进行CDN代理,进行缓存静态资源,分散服务器带宽压力。

同时,app端,小程序端等本地资源无需担心这个问题。

二、防火墙层

1、在大多数并发情况下,都可能存在恶意请求,例如CC,DDOS攻击,通过脚本,1秒请求100次,1000次请求,我们可直接认定该请求以及请求后的客户端为恶意请求,拒绝该客户端请求。

2、由于数据网络的特殊性,可能存在多个手机网络共用一个ip的情况,理论上不能直接封停ip,而是需要根据该次请求客户端附带的其他信息(ua标识)来用于拒绝。

3、同时,对已登录用户做好限流,同一个用户,理论上一次性不可能超过10次接口请求(静态资源不算)

4、防火墙其他规则可执行研究

三、网关层

网关层可以和防火墙层可以为同一个逻辑,通过防火墙后,进行请求调度,例如 Nginx 负载均衡。网关层需要对请求进行调度,将请求通过轮询/空闲判定,权重判定等方式,把请求分配到多台服务器,进行服务器压力分担。

注意:所有的内部通信,最好都为内网环境,降低带宽压力,以及提升响应

四、微服务层

微服务层是个概念性层,可要可不要,在微服务层,可对请求进行服务分散,在大压力情况下,有必要关闭一些微服务应用进行服务降级,保证主要服务的运行。

例如:在淘宝双十一时,关闭了退款通道。

例如:当商城高并发时,关闭查看历史订单功能,关闭小游戏功能,等等。

微服务层可能会出现分布式事务问题,有一定的难度。

五、进程模型优化

在传统 php-fpm 模型中,单进程作为同步阻塞模型,一个进程在同一时间只能处理一个请求,当出现io阻塞后,进程会一直被请求占用,直到io结束,如果需要提升并发,就必须增加进程数,增加进程数意味着cpu调度进程压力增大,导致cpu耗费资源加大。

通过 swoole 的多进程多协程并发模型,一个进程可同时处理多个请求,cpu只要调度少量的进程,即可实现处理更多的请求。

六、php 解释层优化

在正常php-fpm中,php运行需要经过以下几个步骤:加载php文件->逐行解释->运行 一个php项目,一次请求可能存在10-50个文件(laravel框架更多),每次请求进来,都需要重新引入文件,加载文件,导致磁盘io负载暴涨。

我们可以通过opcache对文件进行解释加载进内存,二次请求不会再去重新加载php文件如果不想做opcache,也可直接将项目代码放进内存文件系统中,也可通过swoole、wokerman等常驻内存框架提升一点速度,也可实现一次加载永久存在的目的,提升性能。php作为服务端语言,主要瓶颈在于io,在高并发下,这些优化也是有限的。没有过于追求的必要。

七、服务器缓存层

理论上,一次接口请求进来,非即时性查询,高频请求,都必须先经过缓存层,通过缓存减小数据库压力。

例如:商品缓存,文章缓存等等。

注意:为了避免缓存穿透,需要对所有缓存定时更新期限,并且将失效时间进行错开,确保不会出现一瞬间所有缓存失效,导致请求全部进入数据库。为了避免内存溢出,,需要对所有缓存字段进行管理,做失效时间,定时删除垃圾数据。

八、并发锁相关

在并发下,如果对数据准确度有一定要求的话,将涉及到并发锁功能。

在MySQL中,MyISAM引擎虽然插入、查询性能高,但是它使用表锁,如果在大表更新情况下,将造成锁表,导致全服务器请求都将锁住等待,所以MyISAM不能用于大表更新等业务,只适合做日志记录。

同时,InnoDB支持事务,行级锁,在更新用户金额时,可使用InnoDB存储引擎的表存储用户金额,进行行级更新。

可查看:并发锁

注意:InnoDB虽然作为行级锁,但是也需要考虑操作表的单位数量,例如用户金额,理论上只有用户自身请求,并发量并不大,就算是锁表,也不会影响到其他用户的操作。(如果你非得用脚本点1000下下单,那也是你牛逼,给你锁表1000秒也是你自找的)。但如果是商品库存,将不能直接使用 InnoDB 实现行锁,原因是该数据可能会被成千上万的用户进行请求,当锁住后,其他请求都需要等待,极度影响用户体验。

九、异步队列压力

如果是高并发场景,且是瞬时并发,例如商品秒杀只有1秒,其他时间都为低流量请求情况,可通过队列转为异步处理,,前端等待n秒获取异步结果。通过异步队列,可分散瞬时并发带来的压力,使得服务器不会瞬间宕机导致出错。

注:异步队列处理缺点在于用户不能及时获取到结果,但是一般不会太久,如果超出3秒,可直接丢弃该请求,直接给用户返回失败。如果是不需要获取返回结果的,例如插入日志,发送邮件,可直接使用队列。

十、数据库主从

为了避免数据库压力过大,可对数据库做主从环境,非主要数据全部从从数据库读取,减少主数据库压力。

可查看:关于mysql集群主从服务器搭建

十一、数据库分表

可查看:mysql分表详解

通过对数据库进行分表,降低单表锁表情况,分散单表查询压力。

十二、数据库索引优化

通过优化数据库索引,保障查询时命中索引,减少临时表

可查看: Mysql索引优化

十三、尽量不要出现报错

虽然 notice 报错不会影响服务正常运行,但是一次报错,涉及到了php底层的错误拦截机制,会影响非常多的性能,所以尽量不要出现任何报错。

解决任何可能存在的代码报错情况,改用自己的方式拦截,记录日志。

网站高并发解决方案-理论知识

网站高并发解决方案-理论知识1

本文章转自:http://www.php20.cn/article/sw/%E9%AB%98%E5%B9%B6%E5%8F%91/138

当面试官问:“网站高并发怎么做?”时,该怎么回?
在高并发下,我们(初级程序员)能做什么?

接下来我将从数据库、服务器硬盘、服务器带宽、使用什么服务器、PHP代码逻辑的一些优化等这5个方面简单说说我的看法。

一、MySQL方面

MySQL方面,我们主要要从以下几点去考虑:

1、索引

MySQL其实没有想象中的那么差,相反,MySQL表数据,只要查到了索引,都不会慢,(1.5亿数据表查索引0.0几秒),所以MySQL索引是个好东西,用好之后,查询效率自然很快。所以,数据表设计,一定要考虑全面,给查询频繁使用的字段增加索引,或者使用组合索引。

数据库索引学习传送门:https://www.cnblogs.com/zhaobingqing/p/7071331.html

2、查询数据缓存

常用的一些配置数据,或者某些变更不频繁查询频繁的数据可以通过Redis,Memcache,File(不推荐)等方式增加缓存来处理,避免数据库频繁查询造成额外的数据库性能消耗。

场景一:进网站的轮播图,由于变更不频繁,可以设置缓存1天,当轮播图修改更新时,更新缓存

场景二:10万个会员的聊天室,进来需要查询聊天记录,由于聊天记录变更频繁并且查询频繁,可设置缓存1-3秒,缓存失效才去取一次数据库,将大部分查询都进入缓存中查询,大大降低了数据库压力。

3、查询逻辑优化

场景一:当你想在一个数据量为1000万的访问表,统计会员A的访问记录时,你会发现,就算给会员id增加了索引,也会很慢,因为这个涉及到了数据命中条数

MySQL命中条数越多,则查询越慢。

优化方案:由于是访问表,不算是重要数据,可增加一个统计表,统计每天的访问数量,当你要查会员A的访问总数,则可以直接去查sum统计表中的数据,大大提高了性能。

场景二:某个抽奖程序,A奖品限制每天只能出1万个,判断当天是否超出限制时,一般情况是count(1) 查出奖记录表,这样做是不正确的,正确做法:

1、增加个库存字段,每天自动更新为10000,每次抽中减一,判断是否出完只需要查看库存字段

2、增加个计数表,按每天为单位,每次抽中则字段num+1,判断是否超出限制,只需要查出当天记录的num字段比对就行

场景三:高并发下,会员抢10万个红包怎么做?

每天新增1万条奖品记录,并生成缓存队列(Redis),每次抢完则从队列中取数据,抢完批量更新回数据库。

场景四:当有一个表,字段数有50,而你取数据只需要10个字段时,尽量把select * 写成 select 字段名,字段名,可以让mysql节省没必要的返回数据,从而影响效率。

二、服务器硬盘方面

大多数人,可能不知道有iops(硬盘每秒输入输出量)这个东西存在,所以在硬盘方面的优化直接被忽略了。

下图是阿里云的各硬盘比对

1

通俗来讲,就是硬盘的每秒读取文件的数量有限,举个例子,你的程序从启动到输出include了100个文件,高效云盘的iops是3000,代表着你的程序,每秒最多只可以访问3000/100=30的并发数(只是理论数据,当在大并发下,操作系统会适当的优化)

这就是laravel框架慢的原因,加载的文件太多了。

优化方案?优化方案,在前几个月,我的圈子有讨论过,具体方案有以下几种:

1、将PHP框架,编译成一个PHP文件,这样一次请求下来,只有一个文件的输出,大大的降低了硬盘的压力,其实,tp3.2就已经有了这个功能,只是大家没注意而已。http://document.thinkphp.cn/manual_3_2.html#app_compile

2、在swoole文档中,韩大有说https://wiki.swoole.com/wiki/page/836.html

2

在linux中,/dev/shm是映射的内存路径,当框架启动时,将框架代码复制到该文件夹下面去运行(注意,内存,关机数据就会没有,只能保存固定的业务代码,不能保存业务数据)。windows也有内存盘,也可以实现该操作。内存读取速度非常快,所以并不用担心磁盘I/O问题。

三、服务器带宽方面

服务器的带宽是非常贵的,而网站的访问都离不开带宽,下图是我的博客一次请求下来的网页大小。

3

假设一次请求就需要600kb,这相当于什么呢?当我服务器带宽只有1M(出口带宽128kb/s)时,某次访问进来,最少需要600/128约等于5秒的时间。相当于我服务器的秒并发量只有1/6。

何况这只是个博客,商城呢?假设商城的请求大小有1M,服务器带宽有100M(12.8M/s)的话,秒并发量最多只有12.8。这就是带宽方面的限制了。

当然,浏览器在一次请求之后,会智能的缓存页面(js,css,图片等静态文件),这样大大的节省了服务器带宽,但是新进来的用户,或者有用户禁止了缓存页面,就得请求这么多数据了。

至于优化方案,我的方案是上cdn(内容分发网络)。它的大致原理是:将域名转到他们的dns服务器,由他们进行管理域名的请求ip。

例如:我的[blog.huanghui.xyz](blog.huanghui.xyz),将dns转移到百度云cdn,那么所有访问blog.huanghui.xyz的请求都会被百度云cdn接管。

在某个地区(百度云cdn有多个地区的服务器,保证网站资源第一时间响应给用户)第一次请求时,由于百度云还没有缓存,将会请求正确的服务器地址(百度云cdn后台域名解析),把数据返回给用户端并缓存到百度云cdn。

当有缓存之后,百度云将不再请求服务器的资源,将百度云缓存的静态数据,直接返回给用户端,这就是cdn的作用了。

四、使用Nginx服务器

可能大家都知道,高并发下,都得使用Nginx服务器,这是为什么呢?

咳咳,可以看以下文章https://www.cnblogs.com/yum777/p/6244935.html

五、PHP代码逻辑

再好的架构,也会死在垃圾代码上面,上面的一系列优化好了,那就是PHP方面了,主要注意以下几点:

1、多使用PHP内置函数(内置函数写在了PHP C底层,无需编译,速度快)

2、判断逻辑,(当有个奖品需要判断是每周2万个,每天最多5000个时,先判断是否超过了每周限制,当真的超出了每周限制时,将节省每天判断的时间)

3、避免循环运行SQL语句(需要插入/更新多条数据时,请在循环外批量插入/更新)

http://www.php20.cn/article/sw/%E6%95%B0%E6%8D%AE/105 tp5内部好像已经实现了批量更新功能。

4、尽量减少查询数据库的次数

能用一条语句查出的,尽量不要多条。例如,有个订单表,有2种付款方式(payment_type=1,payment_type=2),对应1个付款金额的字段(total_money),当你需要统计2种付款方式总计金额时,大多数人会根据payment_type=1,payment_type=2进行查询2次数据,该怎么优化呢?

可以使用MySQL的判断查询:http://www.php20.cn/article/sw/%E5%88%A4%E6%96%AD/95

5、过滤掉恶意请求

通过验证码、手机短信等方法,将机器人排除在外,为了避免恶意请求,可限制每秒请求次数不得超过10(普通人哪能点击这么快),当超过时,则系统底层拒绝响应,等到下一秒才可以继续请求。

六、其他

本文一切都是理论知识,算是实践过,但是并发量不大,所以大神们勿喷,互相学习,如果有错误或者有其他优化方案,希望大神们批评指正。

八年phper的高级工程师面试之路

八年phper的高级工程师面试之路

这是一篇反面教材,希望也能引起部分程序员的警惕。

最近半个月时间,经过几次面试,差不多已经对自己有了定位————距离腾讯T3岗位还是有一点距离。

因为在一家小公司呆的习惯了(6年),公司没有人在技术层面超过我,作为技术核心,感觉自己很牛,活在一个小圈子里面,几乎不会主动去了解新技术,甚至对php以及js本身都不能算精通。

所以变故出现的时候,我才发现自己的技术脱节有多厉害,虽然之前的面试并没有做专门的准备,但是与6年前找工作的情形相比,差距非常的大。

下面是我整理的一部分遇到的面试题,我尽量用我所知道的知识来进行陈述,肯定会有很多误解以及遗漏,希望大家能够指正。

公司1:

1、mysql_real_escape_string mysql_escape_string有什么本质的区别,有什么用处,为什么被弃用?

答:mysql_real_escape_string需要预先连接数据库,并可在第二个参数传入数据库连接(不填则使用上一个连接)

两者都是对数据库插入数据进行转义,但是mysql_real_escape_string转义时,会考虑数据库连接的字符集。

它们的用处都是用来能让数据正常插入到数据库中,并防止sql注入,但是并不能做到100%防止sql注入。

再问:为什么不能100%防止?

答;因为客户端编码以及服务器端编码不同,可能产生注入问题,但是其实这种场景不多见。

继续答:被弃用的原因是官方不再建议使用mysql_xx的数据库操作方式,建议使用pdo和mysqli,因为不管从性能跟安全来看,mysqli都比mysql要好。

衍生出来的问题是mysqli的连接复用(持久化)问题,这一块我并没有答好。

2、什么是内存泄漏,js内存泄漏是怎么产生的?

答:内存泄漏是因为一块被分配内存既不能被使用,也不能被回收,直到浏览器进程结束。

产生泄漏的原因是闭包维持函数内局部变量,不能被释放,尤其是使用闭包并存在外部引用还setInterval的时候危害很大。

备注:我觉得这块回答并不好,因为肯定不是闭包的原因。

我查了一下资料,从比较浅的方位来再回答一下这个问题:

产生泄漏的原因有好几种:

(1) 页面元素被删除,但是绑定在该元素上的事件未被删除;

(2) 闭包维持函数内局部变量(外部不可控),使其得不到释放;

(3) 意外的全局变量;

(4) 引用被删除,但是引用内的引用,还存在内存中。

从上述原因上看,内存泄漏产生的根本原因是引用无法正确回收,值类型并不能引发内存泄漏。

对于每个引用,都有自己的引用计数,当引用计数归零或被标记清除时,js垃圾回收器会认为该引用可以回收了。

3、什么是闭包,跟原型链、作用域链有什么关联

答:闭包是指存在于一个作用域链分支的函数域内的函数,该函数可以向上逐级访问作用域链上的变量,直到找到为止。当闭包存在外部引用时,js会维持闭包自身以及所在函数作用域链的内存状态。

备注:这个是我自己瞎说的。

继续答:跟原型链没有什么关联,函数的原型(prototype)主要用于实现继承,原型链可用于追溯继承关系,与作用域链类似,都是向上逐级访问属性,直到被找到,原型链的顶层是null,可以理解为所有的object都继承至null,所以null的类型是object。

继续答:作用域链可以看作是一个树形结构,由根节点window向下扩散,下层节点可以访问上层节点,但是上层节点无法访问下层节点,产生闭包的函数作用域属于节点中的一个,向下扩散后闭包函数产生叶子节点,叶子节点之间可以互相访问,当访问的变量在叶子节点中无法找到时,向上层节点查找,直到被找到为止,这个概念有点类似原型链上的属性查找。

4、一台电脑配置无限好,可以同时打开多少个网页

答:65535-1000 = 64535(端口数)

5、ip地址能被伪造吗?

答:http头部可以被篡改,但是只能修改X_FORWARDED_FOR,真实ip地址(REMOTE_ADDR)很难修改(除非是路由器去修改),因为真实ip是底层会话ip地址,而且因为TCP 3次握手的存在,连接无法建立,伪造的意义不大,至于UDP的话,一般是内网才使用UDP通信。

6、有100万个奖品,每个人可以中奖3次,先到先得,怎么控制并发,不能发超,并保证完全的先到先得模式

答:百万奖品在打乱后预先insert到数据库,所有中奖操作,均只能update,不能insert。进来抽奖的用户使用memcahe原子加锁,实现抽奖次数自增,当抽奖次数到达3时,返回不中奖。

再问:预先插入需要很多资源,如果奖品数量上了1亿怎么办?

答:使用redis队列存储请求,跑守护进程异步发奖,产生的问题是用户无法实时看到中奖情况。

再问:这样肯定不行。

再答:使用全局内存加锁确保抽奖过程是单进程在跑,但是会面临大并发阻塞问题。

再问:内存比较宝贵,在不用内存加锁的情况下怎么办,并且如果碰到1亿奖池的情况,预先插入数据库肯定不好,怎么办?

答:设置奖品概率,分三张表,都使用innodb引擎,一张存中奖记录(预先插入一行),一张存奖品发放概况,一张存用户抽奖情况(uin唯一索引),大并发情况下,利用mysql的排他锁进行并发控制。流程如下:

begin

查询用户抽奖次数,加排他锁

对用户抽奖次数的更新/插入

锁行查询发放情况

获得抽奖结果(某些奖品发完之后,动态变更概率)

更新发放表

插入中奖记录

commit

再问:遇到脏读怎么办?

答:这方面不是很了解

再问:innodb的master线程在什么情况下fork其他子线程?

答:不知道

7、数据链路层的数据是怎么校验的,有哪些校验方式

答:crc32,别的校验可能是取模校验奇偶数吧。

备注:答个crc校验就行了。

8、b+树的查询时间复杂度是多少,哈希表是多少,为什么数据库索引用b+树存储,而不是哈希表,数据库索引存储还有其他数据结构吗

答:O(log(n)),O(1)

因为哈希表是散列的,在遇到key>’12’这种查找条件时,不起作用,并且空间复杂度较高。

备注:b+数根据层数决定时间复杂度,数据量多的情况下一般4-5层,然后用二分法查找页中的数据,时间复杂度远小于log(n)。

9、apache是怎么跟php通讯的,sapi是什么

答:使用sapi通讯,sapi是php封装的对外数据传递接口,通常有cgi/fastcgi/cli/apache2handler四种运行模式。

10、php的垃圾回收机制?

答:垃圾回收是指当php运行状态结束时,比如遇到了exit/die/致命错误/脚本运行结束时,php需要回收运行过程中创建的变量、资源的内存。

ZEND引擎维护了一个栈zval,每个创建的变量和资源都会压入这个栈中,每个压入的数组结构都类似:[refcount => int, is_ref => 0|1, value => union, type => string],变量被unset时,ref_count如果变成0,则被回收。

当遇到变量循环引用自身时,使用同步回收算法回收。

备注:PHP7已经重写了zal的结构体。

11、jquery的sizzle引擎工作原理

答:除了直到是DOM元素查找引擎之外,一无所知。

12、seajs的工作原理,如何解决重复加载库的问题,如何进行资源的同步加载

答:建立映射关系并缓存起来;资源并不能真正同步加载,只是返回一个回调。

13、memcache跟redis的区别

答:可存储数据结构不同;redis支持持久化存储。

14、md5逆向原理

答:先用字典查找,再尝试暴力破解。

再问:没有更好的方法了吗?

答:没有了。

备注:嗯,事实上也确实没有特别好的办法,只能使用TB级的海量特征库用数据库存起来,然再分片查找。

15、父类方法是protected,子类重构为private,会发生什么?

答:会发生fatal错误,因为继承的方法或属性只能维持或放大权限,不能缩小,比如protected重载为public是可行的。

16、一个网页从输入地址回车,到完整展示网页内容这段时间里,做了哪些工作,越详细越好。

答:

0、浏览器本地缓存匹配;

1、本地hosts映射对比;

2、本地dns缓存解析;

3、远程dns解析获得服务器ip地址;

4、浏览器发送tcp连接请求包(syn);

5、请求包经过传输层、网络层、数据链路层封装通过网卡到达路由器;

6、路由器转发数据包到所属运营商服务器;

7、运营商服务器通过寻址最短路径通过中继节点到达指定ip地址;

8、服务器端可能存在反向代理或者负载均衡,都是直接转发请求至上游服务器,当然也可以制定安全防御规则直接丢弃请求包;

9、上游服务器收到连接请求,在自身可用的情况下,返回(syn+ack);

10、浏览器校验ack,再次发送(syn+ack);

11、服务器校验ack切换连接状态至established,然后根据请求传输数据包;

12、当transform-encoding为chunked时,浏览器开始渲染页面;

13、四次挥手,连接关闭;

14、渲染数据完成。

备注:还有很多东西不懂,一些东西完全是自己瞎蒙的,因为时间原因,以后有时间详细画一下。

17、keep-alive的概念

答:长连接机制,表示keep-alive-timeout时间内,如果连接没有closed,再次传输数据不再需要三次握手了。

备注:这里也有很多疑问,需要好好捋一捋。

18、linux文件压缩操作命令,shell脚本等

备注:因为平时开发都是在windows环境,对linux了解不足,这一块几乎是0分。

公司2:

这个是被鄙视最惨的一家了,首先会有笔试,相对来说并不复杂,但是有些坑,很多已经忘记了。

印象深刻的是我说自己熟悉常用设计模式,然后让我画UML类图,我就懵逼了,所以在写简历的时候,最好是写自己非常熟悉的,如果只是一知半解,并没有必要放到简历中。

公司3:

这里仅列举几个问到的问题:

1、设计一个中继服务器,转发客户A->客户B的请求;

2、myisam跟innodb有什么区别;

3、php进程死锁产生的原因是什么?怎么自动排查与自动恢复?

4、有class A { public function b($a, $b, $c){}};

怎么使用[‘b’ => 2, ‘a’ => 1, ‘c’ => 3],对进行A::b进行调用,并顺利赋值?

5、php5.2->php7.1的各版本演进历史,新增特性等?

6、画一个tcp三次握手图

在我看来8年的程序员怎么也不应该是这样子的,温水煮青蛙的教训非常惨痛,招招打在要害才知道疼,好在现在认识到问题还不晚,等到了35岁这个年纪,可能就真的晚了.

PHP使用Redis常见7种使用场景

PHP使用Redis常见7种使用场景

本篇文章将简单大致介绍PHP使用Redis常见的7种使用场景

Redis是一个开源的使用 ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

PHP使用 Redis 应用场景

  • 1、简单字符串缓存实战
  • 2、简单队列实战
  • 3、简单发布订阅实战
  • 4、简单计数器实战
  • 5、排行榜实战
  • 6、简单事务的悲观锁实战
  • 7、简单事务的乐观锁实战

简单字符串缓存实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_cache_key = 'Test_bihu'; // 缓存字符串键
// set 应用 要缓存的数据
$arr_cache_data = [
'name' => 'huihui',
'sex' => '男',
'age' => '30'
];
$redis->set($str_cache_key, json_encode($arr_cache_data));
$redis->expire($str_cache_key, 30); # 设置30秒后过期
$json_data = $redis->get($str_cache_key);
$data = json_decode($json_data);
var_dump($data->age); // 输出数据

// hset 应用
$arr_web_site = [
'google' => [
'google.com',
'google.com.hk'
]
];
$redis->del($str_cache_key);
$redis->hSet($str_cache_key, 'google', json_encode($arr_web_site));
$json_data = $redis->hGet($str_cache_key, 'google');
$data = json_decode($json_data);
var_dump($data); // 输出数据

简单队列实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_queue_name = 'Test_queue';
// 入队列
$redis->rPush($str_queue_name, json_encode(['uid' => 1, 'name' => 'Hui']));
$redis->rPush($str_queue_name, json_encode(['uid' => 2, 'name' => 'Tom']));
$redis->rPush($str_queue_name, json_encode(['uid' => 3, 'name' => 'John']));
echo '---入队列成功---<br /><br />';

// 查看队列
$str_count = $redis->lRange($str_queue_name, 0, -1);
echo '当前队列数据为:<br />';
var_dump($str_count);

// 出队列
$redis->lPop($str_queue_name);
echo '<br /><br />---出队列成功---<br /><br />';

// 查看队列
$str_count = $redis->lRange($str_queue_name, 0, -1);
echo '当前队列数据为:<br />';
var_dump($str_count);

简单发布订阅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
// 以下是 pub.php 文件的内容 cli下运行
ini_set('default_socket_timeout', -1);
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_channel = 'Test_channel';
// 发布
$redis->publish($str_channel, "来自{$str_channel}频道的推送");
echo "------ {$str_channel} ------ 频道消息推送成功~ <br />";
$redis->close();


// 以下是 sub.php 文件的内容 cli下运行
ini_set('default_socket_timeout', -1);
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_channel = 'Test_channel';
// 订阅
echo "------ 订阅{$str_channel}这个频道,等待消息推送... ------<br /><br />";
$redis->subscribe([$str_channel], 'callBackFun');
function callBackFun($redis, $channel, $msg) {
var_dump([
'redis' => $redis,
'channel' => $channel,
'msg' => $msg
]);
}

先运行 sub.php,订阅会挂起。
然后运行 pub.php,订阅会收到发布。

简单计数器实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_key = "Test_comments";

// 设置初始值
$redis->set($str_key, 0);
$redis->incr($str_key); // +1
var_dump($redis->get($str_key));
$redis->incr($str_key); // +1
var_dump($redis->get($str_key));
$redis->incr($str_key); // +1
var_dump($redis->get($str_key));
$str_now_count = $redis->get($str_key);
echo "--- 当前数量为{$str_now_count}. ---";

排行榜实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);
$str_key = "Test_score";

// 存储数据
$redis->zAdd($str_key, '50', json_encode(['name' => 'Tom']));
$redis->zAdd($str_key, '70', json_encode(['name' => 'John']));
$redis->zAdd($str_key, '90', json_encode(['name' => 'Jerry']));
$redis->zAdd($str_key, '30', json_encode(['name' => 'Job']));
$redis->zAdd($str_key, '100', json_encode(['name' => 'LiMing']));

$data_one = $redis->zRevRange($str_key, 0, -1, true);
echo "--- {$str_key}由大到小排序 ---<br /><br />";
var_dump($data_one);

echo PHP_EOL;

$data_two = $redis->zRange($str_key, 0, -1, true);
echo "<br /><br />--- {$str_key}由小到大排序 ---<br /><br />";
var_dump($data_two);

简单的悲观锁实战

概念理解:悲观锁(Pessimistic Lock),顾名思义,就是很悲观。每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。

场景:如果项目中使用了缓存且对缓存设置了超时时间。当并发量比较大的时候,如果没有锁机制,那么缓存过期的瞬间,大量并发请求会穿透缓存直接查询数据库,造成雪崩效应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php
class MyRedis
{
public $redis;

public function __construct($host, $port) {
$this->redis = new \Redis();
$this->redis->connect($host, $port);
}

/**
* 获取锁
* @param string $key 锁标识
* @param int $expire 锁过期时间
* @return bool
*/
public function lock($key = '', $expire = 5)
{
$is_lock = $this->redis->setnx($key, time() + $expire);
if (!$is_lock) { // 不能获取锁
// 判断锁是否过期
$lock_time = $this->redis->get($key);
// 锁已经过期,删除锁,重新获取
if (time() > $lock_time) {
$this->unlock($key);
$is_lock = $this->redis->setnx($key, time() + $expire);
}
}
return $is_lock ? true : false;
}

/**
* 释放锁
* @param string $key 锁标识
* @return int
*/
public function unlock($key = '')
{
return $this->redis->del($key);
}
}
$host = '127.0.0.1';
$port = 6379;
$redis_obj = new MyRedis($host, $port);

// 定义锁标识
$key = 'Test_lock';
// 获取锁
$is_lock = $redis_obj->lock($key, 10);
if ($is_lock) {
echo '成功获取锁' . PHP_EOL;
echo 'do sth...<br />' . PHP_EOL;
sleep(5);
echo 'success<br />';
$redis_obj->unlock($key);
} else { // 获取锁失败
echo '请求太频繁<br />';
}

简单事务的乐观锁

概念理解:乐观锁(Optimistic Lock), 顾名思义,就是很乐观。每次去拿数据的时候都认为别人不会修改,所以不会上锁。watch命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key。这样就可以对指定的key加乐观锁了。 注意watch的key是对整个连接有效的,事务也一样。如果连接断开,监视和事务都会被自动清除。
当然了exec,discard,unwatch命令都会清除连接中的所有监视。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
$host = '127.0.0.1';
$port = 6379;
$redis = new \Redis();
$redis->connect($host, $port);

$str_key = 'Test_age';
$redis->set($str_key, 10);
$age = $redis->get($str_key);
echo "------ Current Age:{$age} ------" . PHP_EOL; // 10
$redis->watch($str_key);

// 开启事务
$redis->multi();
// 在这个时候新开了一个新会话执行
$redis2 = new \Redis();
$redis2->connect($host, $port);
$redis2->set($str_key, 30); // 新会话
$new_age = $redis->get($str_key);
echo "------ Current Age:{$new_age} ------" . PHP_EOL; // 30

$redis->set($str_key, 20);
$redis->exec();
$age = $redis->get($str_key);
echo "------ Current Age:{$age} ------" . PHP_EOL; // 30
// 当exec执行时,如果监视的key从调用watch后发生了变化,则整个事务会失败

以上就是PHP使用Redis常见7种使用场景的详细内容。

PHP如何实现简单的Socket

PHP如何实现简单的 Socket

本篇文章简单说下PHP如何实现简单的Socket。主要以代码为例。

首先使用PHP制作的 Socket 服务端,其主要是设置 Socket 的 IP 地址及端口号;然后监听端口,如果有客户端连接的话,则接收连接请求和数据,最后处理数据并且返回数据即可。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
// 确保在连接客户端时不会超时
set_time_limit(0);
// 设置IP和端口
$address = '127.0.0.1';
$port = 2048; // 调试的时候,可以换端口来调试程序

/**
* 创建一个SOCKET
* AF_INET是 ipv4, 如果用ipv6,则参数为 AF_INET6
* SOCKET_STREAM为 socket 的 tcp 类型,如果是 UDP 则使用 SOCK_DGRAM
*/
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die('socket_create()失败的原因是:' . socket_strerror(socket_last_error()) . "\n");

// 阻塞模式
socket_set_block($sock) or die('socket_set_lock()失败的原因是:' . socket_strerror(socket_last_error()) . "\n");

// 绑定到socket端口
$result = socket_bind($sock, $address, $port) or die('socket_bind()失败的原因是:' . socket_strerror(socket_last_error()) . "\n");

// 开始监听
$result = socket_listen($sock, 4) or die('socket_listen()失败的原因是:' . socket_strerror(socket_last_error()) . "\n");

echo "OK\n Binding the socket on {$address}:{$port} Successed!...";
echo "OK\n Now ready to accept connections.\nListening on the socket...\n";

do { // 从不停止后台运行
// 这里接收连接请求并调用一个子连接 Socket 来处理客户端和服务端间的信息
$msgsock = socket_accept($sock) or die('socket_accept() failed! reason:' . socket_strerror(socket_last_error()) . "\n");

// 读取客户端数据
echo "Read client data\n";
// socket_read函数会一直读取客户端数据,直到遇见\n,\t或者\0字符。PHP脚本把这些字符看做是输入的结束符
$buf = socket_read($msgsock, 8192);
echo "Received msg from client: {$buf}\n";

// 数据传送 向客户端写入返回结果
$msg = "welcome \n";
socket_write($msgsock, $msg, strlen($msg)) or die('socket_write() failed! reason:' . socket_strerror(socket_last_error()) . "\n");
// 一旦输出被返回到客户端,父/子socket都应该通过 socket_close($msgsocket)函数来终止
socket_close($msgsock);
} while (true);
socket_close($sock);

然后我们可以通过 tcp 客户端:(telnet工具进行测试)

1
telnet 127.0.0.1 2048

发送数据给服务端,服务端返回数据给客户端,然后关闭这个客户端的连接。

以上就是PHP如何实现简单的Socket的详细内容。

Linux中清空或删除大文件内容的五种方法

Linux中清空或删除大文件内容的五种方法

在 Linux 终端下处理文件时,有时我们想直接清空文件的内容但又不想使用任何Linux命令行编辑器去打开这些文件。那怎样才能达到这个目的呢?在这篇文章中,我将介绍几种借助一些实用的命令来清空文件内容的方法。

注意:由于在Linux中一切皆文件,你需要时刻注意,确保你将要清空的文件不是重要的用户文件或者系统文件。清空重要的系统文件或者配置文件可能会引发严重的应用失败或者系统错误。

提示:在下面的示例中,我们将使用名为 access.log 的文件来作为示例样本。

1、通过重定向到Null来清空文件内容

清空或者让一个文件成为空白的最简单方式,是像下面那样,通过 shell 重定向 null (不存在的事物)到该文件:

1
2
3
4
5
# 重定向到Null来清空文件内容 
> access.log

# 查看文件大小
du -sh access.log

2、使用’true’命令重定向来清空文件

下面我们将使用:符号,它是 shell 的一个内置命令,等同于true命令,它可被用来作为一个 no-op(即不进行任何操作)。另一种清空文件的方法是将 :或者true内置命令的输出重定向到文件中,具体如下:

1
2
: > access.log
true > access.log

3、使用 cat/cp/dd 实用工具及 /dev/null 设备来清空文件

在 Linux 中, null 设备基本上被用来丢弃某个进程不再需要的输出流,或者作为某个输入流的空白文件,这些通常可以利用重定向机制来达到,所以 /dev/null 设备文件是一个特殊的文件,它将清空送到它这里来的所有输入,而它的输出则可被视为一个空文件。另外,你可以通过使用 cat命令 显示 /dev/null 的内容然后重定向输出到某个文件,以此来达到清空该文件的目的。

1
cat /dev/null > access.log

下面,我们将使用 cp 命令复制 /dev/null 的内容到某个文件来达到清空该文件的目的,具体如下所示:

1
cp /dev/null access.log

而下面的命令中,if 代表输入文件,of 代表输出文件。

1
dd if=/dev/null of=access.log

4、使用 echo 命令清空文件

在这里,你可以使用 echo命令 将空字符串的内容重定向到文件中,具体如下:

1
2
3
echo "" > access.log
或者
echo > access.log

注意:你应该记住空字符串并不等同于 null 。字符串表明它是一个具体的事物,只不过它的内容可能是空的,但 null 则意味着某个事物并不存在。基于这个原因,当你将 echo命令 的输出作为输入重定向到文件后,使用cat命令来查看该文件的内容时,你将看到一个空白行(即一个空字符串)。

要将 null 做为输出输入到文件中,你应该使用 -n 选项,这个选项将告诉 echo 不再像上面的那个命令那样输出结尾的那个新行。

1
echo -n "" > access.log

5、使用 truncate 命令来清空文件内容

truncate 可被用来将一个文件缩小或者扩展到某个给定的大小。
你可以利用它和 -s 参数来特别指定文件的大小。要清空文件的内容,则在下面的命令中将文件的大小设定为 0:

1
truncate -s 0 access.log

使用curl发送http请求时获取响应信息

使用curl发送http请求时获取响应信息

使用curl发送get请求时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
function httpGetReq() {
# 设置请求的url地址
$url = 'https://www.example.com';

# 设置请求头信息
$req_header = array(
"Authorization: xxx",
"Accept: */*",
"Content-Type: application/json; charset=utf-8"
);

$curl = curl_init($url);
curl_setopt($curl, CURLOPT_POST, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FAILONERROR, false);
# 开启ssl验证 可以不开启
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
# 设置gzip压缩 可以不设置
curl_setopt($curl, CURLOPT_ENCODING , "gzip");
curl_setopt($curl, CURLOPT_HTTPHEADER, $req_header);
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_HEADER, true);
$response = curl_exec($curl);

// 获取请求头信息
$curl_header_req = curl_getinfo($curl, CURLINFO_HEADER_OUT);

// 获取响应头和响应体
$curl_header_res_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
# 获取响应头
$curl_header_res = substr($response, 0, $curl_header_res_size);
# 获取响应体
$curl_body_res = substr($response, $curl_header_res_size);
# 获取响应状态码
$http_status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
}

使用curl发送post请求时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
function httpPostReq() {
# 设置请求的url地址
$url = "https://example.com";

# 设置请求头
$req_header = array(
"Authorization: xxx",
"Accept: */*",
"Content-Type: application/json; charset=utf-8"
);

# 设置请求体
$body = [
'name' => 'xiaohuihui',
'age' => 18
];

$curl = curl_init($url);
# 发送post请求
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FAILONERROR, false);
// 开始ssl验证 可以不开启
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
// 设置gzip编码 可以不设置
curl_setopt($curl, CURLOPT_ENCODING , "gzip");
curl_setopt($curl, CURLOPT_HTTPHEADER, $req_header);
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_HEADER, true);
$response = curl_exec($curl);

# 获取请求头信息
$curl_header_req = curl_getinfo($curl, CURLINFO_HEADER_OUT);

# 获取响应头和响应体信息
$curl_header_res_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);

# 获取响应头信息
$curl_header_res = substr($response, 0, $curl_header_res_size);

# 获取响应体信息
$curl_body_res = substr($response, $curl_header_res_size);

# 获取响应状态码
$http_status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

curl_close($curl);

# 解析响应体
$decoded_response = rawurldecode($curl_body_res);
}

mysql新增表或字段时判断是否存在

mysql新增表或字段时判断是否存在

新增数据表和字段是很常见的,在如果已经存在或者字段存在时就会报错。在升级程序时往往是一大堆的 sql 语句,而单一的 sql 语句是没法在新增时判断表或者字段是否存在的,必须写成存储过程。

下面是一个存储过程的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE PROCEDURE `add_col_homework`() -- 新增一个存储过程
BEGIN

IF NOT EXISTS (SELECT column_name FROM information_schema.colums WHERE table_name = 'ot_user' AND column_name = 'sfzzh') -- 判断字段是否存在
THEN
-- 不存在则新增字段
ALTER TABLE ot_stamp ADD COLUMN `sfzzh` int(10);
END IF;

END;

call add_col_homework();-- 运行该存储过程

drop PROCEDURE add_col_homework; -- 删除该存储过程

mysql数据库多表各种join用法

mysql数据库多表各种join用法

在数据库查询中,往往会需要查询多个表的数据。比如查询会员信息同时关于这个会员的订单信息,如果分语句查询的话,效率会低很多,就需要用到join关键字来连表查询了。

下面是例子分析:

会员表:user

user_id user_name
1 张三
2 李四
3 王老五

订单表:order

order_id user_id order_name
1 3 机械键盘套装
2 1 鼠标套装
3 1 机械键盘套装
4 5 机械键盘套装
5 3 显卡
6 3 散热器

如果我想查询会员名张三的订单数据,平常的方法是:

1
2
3
4
5
# 先查询张三的id
SELECT user_id FROM user WHERE user_name = '张三' LIMIT 1;

# 获取到张三的id为1之后,再通过会员id来查询订单
SELECT * FROM order WHERE user_id = 1;

如果用join连接的话,直接一条语句就可以搞定:

1
SELECT o.* FROM user AS u LEFT JOIN order AS o ON u.user_id = o.user_id WHERE u.user_name = '张三';

这样就可以直接查询到张三的订单信息了。


join分:

left join: 返回左表所有符合条件的记录和右表连接相等的记录。
inner join: 只返回两个表中连接字段相等的行。
right join: 返回右表所有符合条件的记录和左表连接相等的记录。

语法为:

1
2
3
4
5
6
7
8
9
10
FROM table1 LEFT JOIN(INNER RIGHT) table2 ON table1.field1 compopr table2.field2

说明:
table1,table2 参数用于指定将记录组合的表的名称。

field1,field2 参数指定被连接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但他们不需要有相同的名称。

compopr 参数指定关系比较运算符:"=","<",">","<=",">=" 或 "<>"。

如果在 INNER JOIN 操作中要连接包含 Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误。

下面是几个例子:

INNER JOIN

1
2
3
SELECT o.* FROM user AS u INNER JOIN order AS o ON o.user_id = u.user_id WHERE u.user_name = '李四';

查询结果是空的。因为 inner join 只返回连接相等的行,order 表是没有关于李四的订单记录的。

LEFT JOIN

1
2
3
4
5
6
7
SELECT o.user_id,u.user_name FROM user AS u LEFT JOIN order AS o ON o.user_id = u.user_id WHETE u.user_name = '李四';

查询结果:
user_id user_name
2 李四

因为orderb表没有内容,所有只返回左表的数据。

RIGHT JOIN 和 LEFT JOIN 相反

1
2
3
4
5
6
7
8
9
10
11
例如我想查询买了机械键盘套装的会员有多少

SELECT u.*,o.* FROM user AS u RIGHT JOIN order AS o ON o.user_id = u.user_id WHERE o.order_name = '机械键盘套装';

查询结果为:
user_id user_name order_id user_id order_name
3 张三 1 3 机械键盘套装
1 张三 3 1 机械键盘套装
4 5 机械键盘套装

因为会员id 5数据不存在,所以只返回订单表的内容。

这就是 mysql join 区别和用法了。

innodb和myisam数据表类型的区别

innodb和myisam数据表类型的区别

MyISAM 和 InnoDB 讲解

InnoDB 和 MyISAM 是许多人在使用 MySQL 时最常用的两个表类型,这两个表类型各有优劣,视具体应用场景而定。基本的差别为:MyISAM 类型不支持事务处理等高级操作,而 InnoDB 类型支持。MyISAM 类型的表强调的是性能,其执行速度比 InnoDB 类型更快,但是不提供事务支持,而 InnoDB 提供事务支持以及外部键等高级数据库功能。

以下是一些细节和具体实现的差别:

  • 1、InnoDB 不支持 FULLTEXT 类型的索引。
  • 2、InnoDB 中不保存表的具体行数,也就是说,执行 select count(*) from table 时,InnoDB 要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即可。注意的是,当 count(*) 语句包含 where 条件时,两种表的操作是一样的。
  • 3、对于 AUTO_INCREMENT 类型的字段,InnoDB 中必须包含只有该字段的索引,但是在 MyISAM 表中,可以和其他字段一起建立联合索引。
  • 4、DELETE FROM table 时,InnoDB 不会重新建立表,而是一行一行地删除。
  • 5、LOAD TABLE FROM MASTER 操作对 InnoDB 是不起作用的,解决方法是首先把 InnoDB 表改成 MyISAM 表,导入数据后再改成 InnoDB 表,但是对于使用的额外的 InnoDB 特性(例如外键)的表不适用。

另外,InnoDB表的行锁也不是绝对的,假如在执行一个 SQL 语句时,MySQL 不能确定要扫描的范围,InnoDB 表同样会锁全表,例如:update table set num = 1 where name like "%aaa%"

两种类型最主要的差别就是 InnoDB 支持事务处理与外键和行级锁。而 MyISAM 不支持。所有 MyISAM 往往就容易被人认为只适合在小项目中使用。


作为使用 MySQL 的用户角度出发,InnoDB 和 MyISAM 都是我比较喜欢的,如果数据库平台要达到需求:99.9%的稳定性,方便的扩展性和高可用性来说的话,MyISAM 绝对是首选。原因如下:

  • 1.平台上承载的大部分项目是读多写少的项目,而 MyISAM 的读性能是比 InnoDB 强不少的。
  • 2.MyISAM 的索引和数据都是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而 InnoDB 是索引和数据是紧密捆绑的,没有使用压缩从而造成 InnoDB 比 MyISAM 体积庞大不少。
  • 3.经常隔1,2个月就会发生应用开发人员不小心 update 一个表 where 写的范围不对,导致这个表没法正常用了,这个时候 MyISAM 的优越性就体现出来了,随便从当天拷贝的压缩包取出对应表的文件,随便放到一个数据库目录下,然后 dump 成 sql 再导回到主库,并把对应的 binlog 补上。如果是 InnoDB,恐怕不可能有这么快速度,别和我说让 InnoDB 定期用导出 xxx.sql 机制备份,因为最小的一个数据库实例的数据量基本都是几十G大小。
  • 4.从接触的应用逻辑来说,select count(*)order by 是最频繁的,大概能占了整个 sql 总语句的60% 以上的操作,而这种操作 InnoDB 其实也是会锁表,很多人以为 InnoDB 是行级锁,那个只是 where
    对它主键是有效,非主键的都会锁全表的。
  • 5.还有就是经常有很多应用部门需要我给他们定期备份某些表的数据,MySIAM 的话很方便,只要发给他们对应那表的frm.MYD,MYI的文件,让他们自己在对应版本的数据库启动就行,而 InnoDB 就需要导出 xxx.sql 了,因为光给别人文件,受字典数据文件的影响,对方是无法使用的。
  • 6.如果和 MYISAM 比 insert 写操作的话,InnoDB 还达不到 MyISAM 的写性能,如果是针对基于索引的update操作,虽然 MyISAM 可能会逊色 InnoDB,但是那么高并发的写,从库能否追的上也是一个问题,还不如通过多实例分库分表架构来解决。
  • 7.如果是用 MyISAM 的话,merge 引擎可以大大加快应用部门的开发速度,他们只要对这个merge表做一些 select count(*) 操作,非常适合大项目总量约几亿的 rows 某一类型(如日志,调查统计)的业务表。

当然 InnoDB 也不是绝对不用,用事务的项目就用 InnoDB 的。另外,可能有人会说你 MyISAM 无法抗太多写操作,但是可以通过架构来弥补。

本站总访问量 | 本页面被访问 | 您是第位小伙伴

© 炫彩信息科技有限公司 版权所有 备案号 : 赣ICP备19008485号