从PHP官方文档可以看到,empty用于测试一个变量是否为空,empty($var)等价于
|
|
empty方法的参数只能是一个变量,当传入一个对象的属性时,如果该属性是真实存在的,empty可以正常工作。
如果该属性是通过魔术方法获取的,empty的返回结果不是期望中的,而总是返回true。
PHP版本为5.6.24
|
|
|
|
从PHP官方文档可以看到,empty用于测试一个变量是否为空,empty($var)等价于
|
|
empty方法的参数只能是一个变量,当传入一个对象的属性时,如果该属性是真实存在的,empty可以正常工作。
如果该属性是通过魔术方法获取的,empty的返回结果不是期望中的,而总是返回true。
PHP版本为5.6.24
|
|
|
|
This library could be found on Packagist for an easier management of projects dependencies using Composer.
Github repo: redmodel
|
|
Can use when a model’s key representation has only one dynamic field.
The default choice is “forced”, which would replace the same key if exists.
The default choice is “forced”, which would replace the same key if exists.
Redis native methods such as “set”, “hmset” can use when the query builder contains only one valid query key.
// string model
$model->where('id', 1)->set('maria');
// hash model
$model->where('id', 1)->update([
'name' => 'Maria',
'age' => '22',
]);
// equal to
$model->where('id', 1)->hmset([
'name' => 'Maria',
'age' => '22',
]);
Taking the job to build query keys for model.
|
|
The built query keys which contain unbound fields would be ignored. For example
|
|
为Laravel定制的分布式文件存储服务,使用mongodb作为后端存储引擎。
This library could be found on Packagist for an easier management of projects dependencies using Composer.
Github仓库:fileflake
|
|
每个文件都有一个元数据。
元数据的“引用”类似于Linux文件系统的软链接。
“引用计数”表示目前有多个文件指向当前文件(源文件)。
拥有相同签名的多个文件只存储一个拷贝,它们通过“引用”值与源文件产生关联。
当删除一个软链接文件时,删除文件元数据,软链接文件指向的源文件的引用计数减1。
当删除一个源文件时,源文件的引用计数减1。
当一个文件的引用计数为0时,删除该文件的元数据,并将该文件从后端存储中删除。
存放存储节点元数据,用于负载均衡
fileflake,欢迎star
最近做了一个抢红包的项目,这个项目涉及到了后端开发的多个技术点
以上这些技术点都是为高并发场景服务的。
为了使抢红包的请求能够依次处理,使用悲观锁将红包预先锁定。需要为该悲观锁设定一个最大生存时间,以确保发生不可预知的错误时,不会影响后续的用户抢红包。其余并发进入的请求将等待并争抢红包锁。一个请求处理完成,将红包解锁,下一个成功获取锁的请求将被处理。
处在等待状态的请求不断争抢红包锁。需设定一个最大重试次数,超时则请求处理过程结束。
实现悲观锁的方案有多种,这里采用了redis的原子操作setnx,setnx返回1代表争抢成功。
使用SELECT FOR UPDATE为数据加锁,保证同一时刻只能有一个请求更新数据。在应用层悲观锁的保护下,数据库锁争用、幻读的现象可以减少甚至避免,这样也降低了数据库本身的压力。
抢红包涉及到金钱,在发生异常时需回滚,保证数据一致性。
合理利用数据库索引可以避免不期望的结果。
红包的数据库采用了MySql,分两个表,主表和抢红包记录表。每个用户被限定最多只能抢一次红包,抢红包记录表中的唯一索引(红包id,用户id)可以避免发生意外。
经过应用层悲观锁、数据库锁的保护,发生这种意外的可能性已经微乎其微了。
该算法适用于多人抢红包的场景,可动态调整红包分配金额的平均程度。红包余额需大于红包剩余份数,分配的金额为整数,如果需要分配成小数,将红包余额乘以100,分配结果除以100即可。
红包余额R,红包剩余份数D,方差因子VF(>=0),分配金额G,每一份的最小金额为min
容易看出
|
|
|
|
输出
快速排序是一种分治的排序算法,是实践中最快的排序算法,理论上的时间复杂度为O(N*lgN),最差情况的时间复杂度为O(N^2),但稍加努力就可避免这种情况。
快速排序的步骤
从上面的步骤可以看出,快速排序需要递归运算。
采用三值取中间值的方法选取标记值,从而避免最差情况。
|
|
排序结果正确,需满足条件
条件2容易实现,重点关注条件1
如何确保列表除元素顺序变化外,没有别的变化?
列表L1、L2满足以下三个条件即可
例如,列表L1=[2,3,2,2,3],顺序打乱后得到L2=[2,2,3,3,2],此时L1、L2满足以上三个条件。如果对L2进行以下操作,均会使其中的一个或多个条件不成立。
|
|
MongoDB和MySql是目前最常用的两种数据库。面对一个应用场景,选择Mongodb还是MySql,哪一个更合适?
弄清两者的区别,就是对这个问题最好的回答。从schema、事务支持、关联查询三个方面进行说明。
schema可以理解为约束。MySql的每个数据表都有一个schema,指明了每个字段的类型,这些类型就是对写入数据的约束。例如,一个字段为int型的字段,写入”helloworld”就会因类型不匹配而报错,导致写入失败;一个int unsigned类型的字段,写入-1同样会报错。
schema除了可以约束字段类型,还可以约束索引。例如建立一个唯一索引(userid,classid),假定数据库中已经有一条(userid=100,classid=1)的记录,如果此时再尝试写入一条userid=100,classid=1的数据,就会因为违反了唯一索引的限制而导致写入失败。
MongoDB作为一种schemaless的数据库,对写入的数据是没有类型限制的,这一点也是MongoDB与MySql之间最重要的一个区别。
如果一个应用的数据结构比较固定,需要约束字段类型和索引,则选用MySQL是合适的,反之则应该选用MongoDB。
MySQL的InnoDB存储引擎支持事务,这使得MySQL适合那些对一致性要求较高的应用,在代码运行异常或出现业务异常时可以及时回滚。
截至目前最新的3.2版本,MongoDB还不支持事务。
如果一个应用场景需要更新多个表,在发生异常时需回滚数据,未发生异常时提交数据,选用MySQL是合适的。
MySQL作为一种关系型数据库,天然支持联表查询。如果一个应用的数据结构需拆分成多个表,且表之间有关联关系,应选用MySQL。
Mongodb(<=3.2)不支持联表查询,这意味着一个查询只能针对一个表。
随着工作经验的积累,我日益感觉到,对一名程序员来说,拥有良好的数据库设计能力是很重要的,甚至是最重要的。
程序员界有一句著名的话
Talk is cheap, show me the code
把这句话演变一下,就成了
Code is boring, show me the data structure
面对同样的数据结构,一百个程序员会写出一百种风格的代码。看别人写的代码,往往是很boring的。
代码是围绕数据结构运行的。
客户端展现的动态数据,都是存储在数据库中,这对程序员来说一定是常识了。
为了便于阐述,我们拿简书的文章页面作为样板。
文章的作者、标题、正文、评论、喜欢等等,只要你打开任意两篇文章,两个页面不一样的地方,几乎都是因为在数据库中存储的内容不同。
良好的数据结构可以提升性能,使代码变得简单、清晰。数据结构清晰了,围绕着数据运行的代码自然就清晰了。
提到数据库设计原则,首先会想到第一、第二、第三范式,这些理论能了解最好,本文不再赘述了。
从实践的角度面对一个具体的应用场景,设计数据库时应遵循哪些原则?
数据结构的设计要能达到应用场景的要求,这是最基本的。举个例子,文章的正文存储在了数据表中的某个字段,该字段的长度被设定为10000字,在文章字数没有被限制在10000字以内的前提下,这显然不能满足应用场景的当前需求。需要考虑,什么样的字段类型才能存储大规模的文本数据?
文章页面中的元素,哪些是主体部分,哪些是附属部分?
一篇文章可以没有评论,评论的有无、多少不影响文章本身的完整性,评论可以被添加、删除。由此可见,文章的评论属于附属部分。阅读次数、喜欢该文章的用户与数量同样如此。
拆分的好处在于,首先数据结构更清晰了,其次可以提高读写性能。当文章有了新评论,只需更新存放评论的表。如果不拆分,需要更新的记录占用的磁盘空间很大,这对磁盘IO速度是个考验。
或许你已经注意到了,文章的标题下面有这篇文章的字数。计算文章的字数,有两个时机:
后者的优势在于数据表中少了一个字段,而且这个字段不是必需的。哪个时机更好?个人觉得前者更好,理由如下
如果能够提高应用的性能,适当的冗余是必要的。
页面的头部有文章作者的昵称,这适合作为冗余字段存储在文章主体数据中吗?用户可以随时更改自己的昵称,如果将昵称作为冗余字段,需要额外的工作以保持数据一致性,从这一点看,用户昵称不适合作为冗余字段。
选择作为冗余的字段应不需要额外的工作来保持数据一致性。
如何存储喜欢文章的用户信息才能做智能推荐?一个好的数据结构应该能应对可能出现的新需求。
为了达到应用的要求,最简单的方式是将这些用户放在一条记录里,存储的字段可以是数组类型。这样设计,喜欢文章的用户信息与用户数量都能轻易获取,读写性能也很好。但对于“喜欢该文章的人还喜欢了”此类的智能推荐,这样的设计明显是难以应对的。将用户放在数组里支持“查询喜欢某文章的用户”,对“查询某用户喜欢的文章”的支持就很差或者根本做不到了,这是一种单向查询的数据结构。
随着用户量不断增加,网站业务数据越来越多,文章数量也达到了百万级。这时如果只把文章存在一张数据表里,读写性能必然是会急剧下降的,这可能会导致用户体验变差,用户流失。老板不能容忍,DBA也不能容忍。
合理的解决方案之一是分为两张数据表,一张存储热门文章,另一张存储非热门文章。热门文章的占比很少,相应的加载速度就会好于非热门文章。
本文总结了设计数据库时需遵守的几个原则
认识到数据结构的重要性,才能设计出好的数据结构。
最近在对业务代码进行重构,遇到了一些比较典型的“散发着难闻味道”的代码,可以用又臭又长来形容。
这部分的业务是发布动态,包括以下步骤:
之所以说重构前的代码“又臭又长”,首先最直观的一点,方法的行数超过了200行,其次上面四个步骤的逻辑全都写在了一起,没有按功能模块拆分,这也是方法过长的原因。
如果按功能划分,发布动态包含三个模块:
其中的敏感词模块和话题模块都是为动态模块服务的。一个动态可以有敏感词,也可以没有敏感词,比如用户分享一个链接,这就不需要做敏感词过滤与记录了,所以敏感词模块与动态模块应该是“松耦合”的。同样,一个动态可以包含话题,也可能不包含,所以话题模块与动态模块也应该是松耦合的。
既然敏感词模块与话题模块都是为动态模块服务的,动态模块就有必要与这两个服务模块建立一个服务约定:
在编程的世界里,约定可以抽象成接口
对于敏感词模块和话题模块,只需要实现该接口,动态模块就可以根据约定调用它们提供的服务。
|
|
|
|
这样动态模块就知道,在动态入库前调用服务模块的beforePublish服务,入库后调用服务模块的afterPublish服务。
耦合关系的建立应该由动态发布的调用方决定,动态模块需提供相应的接口,
这样,动态发布的调用方可以通过addServiceModule/removeServiceModule动态地添加/删除服务模块。
服务模块与核心模块是独立的,通过接口建立松耦合的关系;可动态添加/删除服务模块。
服务模块的修改不会直接影响核心模块。
如果需要添加其它服务模块,只需实现服务约定的接口,并将新的服务模块动态添加到核心模块即可。
指持久化数据库,如
指内存型的数据存储,如
一个良好的缓存策略需兼顾
多大的命中率是好的?
用数学知识来分析这个问题,先设定几个关键的参数
一次数据读取所需的时间的期望值为:
增加数据库缓存的目标为提高数据读取速度,可以归结为一个表达式
等同于
读取速度提升的比值为
假如,T(d)=50ms,T(c)=10ms,G=0.6,读取速度提升比值为
化为百分比也就是40%。
假如,T(d)=50ms,T(c)=10ms,G的值必须大于
才能期望读取速度得到提升。
可以看出
加入缓存不一定能够提升读取性能,这取决于缓存读取速度、数据库读取速度以及缓存的命中率。