Archive for October, 2015

作者:AngryFox 分类: Uncategorized October 31st, 2015 暂无评论

错误:findViewById返回Null,报null pointer错误

1.在另一个view的元素应该用baseView.findViewById()来拿
findViewById()是要指定view的,如果在该view下找不到,自然报null。注意写成view.findViewById()
2.findViewById在setContentView(R.layout.main);之前.
即在setContentView调用之前,调用了findViewById去找main布局中的界面元素lv_contactbook,那么所得到的lv一定是null。挪至setContentView方法调用之后即可。
3.clean一下工程,让ID重新生成
调用LayoutInflater.inflate将布局xml规定的内容转化为相应的对象。比如有rowview.xml布局文件(比如在自定义Adapter的时候,用作ListView中的一行的内容的布局),假定在自定的Adapter的getView方法中有类似如下的代码:
View rowview = (View)inflater.inflate(R.layout.rowview, parent, false);
TextView tv_contact_id =(TextView)rowview.findViewById(R.id.tv_contact_id);
TextView tv_contactname =(TextView)rowview.findViewById(R.id.tv_contactname);
有时候居然也会发现rowview非空,但tv_contact_id和tv_contactname都是null!如果还没发现,检查是否是是Eclipse造成的,需要这个项目clean一次(Project菜单 -> Clean子菜单)
4.对于自定义view,可能是构造方法不对
public MyView(Context context,AttributeSet attr) {
super(context); //这里调用的构造方法不对。应该调用super(context,attr);
}

作者:AngryFox 分类: Uncategorized October 24th, 2015 暂无评论

Vitamio拍摄SDK(VCamera SDK)进行视频的拍摄和生成
微服务的重点:快速发布和解耦
微服务,其实对应的应该是微业务,是为了适应业务的多变/面向最终用户的个性化需求。而微服务的力度,很大程度上是取决于完成一项业务所要设计的数据存储所在的区域。微服务的监控,和原来的服务监控力度应该是类似的,包括业务、应用、系统多个层次,日志、Metrics、调用链、告警等多个维度。

我们在ios开发中会碰到的很多crash问题,如果Debug调试模式的话,我们可以往往很容易的根据log的输出定位到导致crash的原因,但对于已经上线的应用,或者是release环境包导致的crash,我们就需要一些特殊的手段来通过crash log进行分析定位了。
通过参考网上的一些资料,总结了一下,下面介绍一下通过dSYM文件以及crash log分析定位的方法。
1.导出crash log
通过Xcode的Organizer查看某台iphone设备的DeviceLog,选择需要的crash log,导出XXX.crash文件。
2.找到对应的app文件
找到当前iphone设备上安装的ipa文件,更改文件后缀名为zip,解压后得到Payload文件夹,你需要的app文件就在其中了。
3.找到对应build版本的dSYM文件
dSYM文件是iOS编译后保存16进制函数地址映射信息的文件,每次应用程序build后,都会生成对应的xxx.app, xxx.app.dSYM文件。
4.确定dSYM、app以及crash文件的关系
每一个xx.app, xxx.app.dSYM文件都拥有相应的uuid,crash文件也有uuid,只有三者uuid一至才表明之三者可以解析出正确的日志文件。
查看xx.app文件的uuid的方法,在terminal中输入命令:
dwarfdump –uuid xxx.app/xxx (xxx工程名)
查看xx.app.dSYM文件的uuid的方法,在terminal中输入命令:
dwarfdump –uuid xxx.app.dSYM (xxx工程名)
而.crash的uuid位于,crash日志中的Binary Images:中的第一行。如:
armv7 <8bdeaf1a0b233ac199728c2a0ebb4165>
将对应的xxx.app.dSYM文件以及xxx.app文件以及xxx.crash文件拷贝到同一文件夹中,如:~/Desktop/DebugLog。
5.通过symbolicatecrash分析crash文件
Xcode有自带的symbolicatecrash工具,可以通过dSYM文件将crash文件中的16进制地址转换成可读的函数地址。symbolicatecrash工具位于:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash(Xcode 4.5)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash(Xcode 5.0)
该文件是隐藏文件,可以通过如下命令查找并拷贝到系统目录下,并建立快捷方式。
1)打开终端,进入到symbolicatecrash工具所在的文件夹目录
cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/
2)查找确认是否存在symbolicatecrash
ls -al | grep symbolicatecrash
3)将symbolicatecrash工具拷贝到/usr/bin目录下
sudo cp symbolicatecrash /usr/bin/symbolicatecrash
4)设置DEVELOPER_DIR系统变量
cd ~/
vi .bash_profile
并输入如下内容
export DEVELOPER_DIR=”/Applications/Xcode.app/Contents/Developer”
保存并退出
source .bash_profile
5)重启终端,确认是否已正确设置DEVELOPER_DIR系统变量
echo $DEVELOPER_DIR
查看输出结果是否为/Applications/Xcode.app/Contents/Developer
6)查看PATH系统变量是否存在如下路径/usr/bin
echo $PATH
7)如果PATH不存在如下路径/usr/bin,可在~/.bash_profile中添加如下代码
export PATH=”/usr/bin:$PATH”
保存并退出
source .bash_profile
8)上述准备工作完成后,进入dSYM和crash文件对应的文件夹目录,如
cd ~/Desktop/DebugLog
9)执行如下命令,即可正确解析crash文件
symbolicatecrash xxx.crash xxx.app.dSYM > test.txt
注意:symbolicatecrash的参数顺序,否则会报类似如下错误
Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 678.
Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 681.
Use of uninitialized value $data in substitution (s///) at /usr/bin/symbolicatecrash line 685.
Use of uninitialized value in pattern match (m//) at /usr/bin/symbolicatecrash line 404.
Use of uninitialized value in scalar assignment at /usr/bin/symbolicatecrash line 418.
No crash report version in XXX.app.dSYM/ at /usr/bin/symbolicatecrash line 954.

插件模块的性能监控,权限控制,安全机制
web-native唤起本地功能
远程推送,唤起本地功能模块
持续集成

服务提供的REST或RPC端点都是弱契约。向REST API添加JSON模式可以提高安全性及改进针对服务的开发过程,但不易于编写或维护。

设置radiobutton的样式 setCompoundDrawables与setCompoundDrawablesWithIntrinsicBounds的区别

第1种:setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)
可以在上、下、左、右设置图标,如果不想在某个地方显示,则设置为null。但是Drawable必须已经setBounds(Rect)。意思是你要添加的资源必须已经设置过初始位置、宽和高等信息。
RadioButton cb = (RadioButton) tabs.findViewById(R.id.conversation);
Drawable top = UIUtils.getDrawable(R.drawable.mcircle);
top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
cb.setCompoundDrawables(null, top, null, null);

第2种:setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)
可以在上、下、左、右设置图标,如果不想在某个地方显示,则设置为null。图标的宽高将会设置为固有宽高,既自动通过getIntrinsicWidth和getIntrinsicHeight获取。
RadioButton cb = (RadioButton) tabs.findViewById(R.id.conversation);
final Drawable drawableTop = getResources().getDrawable(R.drawable.icon_xiaoxi_dot);
RadioButton cb = (RadioButton) tabs.findViewById(R.id.conversation);
cb.setCompoundDrawablesWithIntrinsicBounds(null, drawableTop, null, null);

radiobutton设置不同方位的图标的方法有以上两种,如果想手动设置大小的话就要用setCompoundDrawables,事先要给Drawable设置setBounds。
如果按照原有比例大小显示图片就使用setCompoundDrawablesWithIntrinsicBounds
但是两者有些区别:
setCompoundDrawables 画的drawable的宽高是按drawable.setBound()设置的宽高,
所以才有The Drawables must already have had setBounds(Rect) called.
使用之前必须使用Drawable.setBounds设置Drawable的长宽。
setCompoundDrawablesWithIntrinsicBounds是画的drawable的宽高是按drawable固定的宽高,
所以才有The Drawables’ bounds will be set to their intrinsic bounds.
即通过getIntrinsicWidth()与getIntrinsicHeight()获得,

作者:AngryFox 分类: Uncategorized October 22nd, 2015 暂无评论

java.lang.ClassCastException: android.app.Application cannot be cast to

出这个异常的原因是在项目中添加了新lication类(public class Application extends lication)之后,没有在AndroidManifest.xml中添加该类的声明,所以编译器抛出异常: java.lang.ClassCastException: android.app.Application cannot be cast to 类名
android:name=".你所新建的lication类名">

java.lang.ClassCastException:android.os.BinderProxy cannot be cast to
Android在绑定service的时候出现java.lang.ClassCastException:android.os.BinderProxy cannot be cast to …异常时:修改manifest文件里边相关服务去掉android:process=”:remote”。

10-22 18:38:02.817: E/AndroidRuntime(17313): java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
数据转换异常 String类型的数字(“123″)应该进行强制转换成数字类型(123) ,String类型的字符串(“abc”)强制装换成整形,报这个错误。

作者:AngryFox 分类: Uncategorized October 17th, 2015 暂无评论

问题现象:
[2015-10-18 01:56:18 - uya] Dx
trouble writing output: already prepared
[2015-10-18 01:56:30 - Dex Loader] Unable to execute dex: Multiple dex files define Lorg/webrtc/videoengine/CaptureCapabilityAndroid;
[2015-10-18 01:56:30 - uya] Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define Lorg/webrtc/videoengine/CaptureCapabilityAndroid;

问题发生概述:
程序编译正常,在用Eclipse调试执行时,报错Unable to execute dex: Multiple dex files define 具体解决方案如下:
方法一:
Eclipse->Project->去掉Build Automatically->Clear ->Build Project->Build Automatically,关闭Eclipse,再打开

方法二:
更新ADT插件,删除workspace目录下的.metadata目录,

方法三:
在你的项目下某个文件夹中有一个后缀为*.APK的文件,删掉,重启Eclipse即可。

方法四:
原因是有重复的。jar被引用,可以查看你的build path,尤其是Android Dependencies一定有重复引入的.jar包,解决的方法是在libs删除重复的jar即可。 (贴上博客连接http://blog.sina.com.cn/s/blog_4b9b6ad001016uuk.html)

方法五:(成功解决方法)
在项目中,有一个类的包名和引用的jar包中的类和包名一致,我用的是jar包中的类,所以工程中的这个类就是重复引用的,删除工程中重复引用的类后,成功打包启动。希望各位同学注意这个小问题。

最后发现
[2015-10-18 02:04:25 - uya] Error generating final archive: Found duplicate file for APK: lib/armeabi/libserphone.so
Origin 1: D:\WDJDownload\uya_android_20151017_ytx\libs\armeabi\libserphone.so
Origin 2: D:\WDJDownload\YTX_Android_UIKIT_SDK_v5.1.1r\ECKitSdk\libs\armeabi\libserphone.so

作者:AngryFox 分类: Uncategorized October 7th, 2015 暂无评论

出现大量的警告
Xcode7后运行以前的项目后出现大量的警告如:
(null): warning: ***.pcm: No such file or directory。
作为一个有洁癖的我反正是不能忍,出现警告的大致原因跟我上面提到的开启Bitcode,.dSYM文件不能用来符号化有关,Xcode试图去创建dSYM文件,但是你又不需要。
解决方法
Build Settings ——>Build Options——>Debug Information Format
Debug下的DWARF with dsYM File改成DWARF
Release下的还是之前默认的DWARF with dsYM File不变
进程:分配完整独立的地址空间,拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程的切换只发生在内核态,由操作系统调度。
线程:和其它本进程的线程共享地址空间,拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程的切换一般也由操作系统调度(标准线程是的)。
协程:和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。
goroutine是非常轻量级的,它就是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈(初始大小为4K,会随着程序的执行自动增长删除)。所以它非常廉价,我们可以很轻松的创建上万个goroutine。
1、缺乏全局状态知识
2、缺乏全局时间帧
3、非确定性

最终ffson以3倍优势完胜golang内置json序列化
android.util:核心工具包中包含底层类,字符串格式化和XML解析工具、底层类​​。
android.os:操作系统包提供了访问基本的操作系统服务,如消息传递、进程间通信、时钟功能和调试。
android.graphics:图形API提供了支持画布、颜色和绘制图元的低级别的图形类并且支持绘制画布。
android.text:用于显示和解析文本的文本处理工具。
android.database:在数据库处理游标时提供底层类支持。
android.content:content API管理数据访问,提供服务来管理资源、内容提供者(content provider)和包。
android.view:视图是核心的用户接口类。所有用户界面元素使用的是一系列视图,以构成用户交互的组件。
** android.widget**:内置在View包内,小部件类是“这里是我们前面创建的”用户界面元素,可以在自己的应用程序中使用。包括列表、按钮和布局。
com.google.android.maps:高级的API,它提供了访问本地地图控件,可以在自己的应用程序中使用。包括MapView的控制、用于标注和控制您的嵌入式地图的叠加以及MapController类。
android.app:一个高层次的包,允许访问应用程序模型。该应用程序包包括Activity和Service的API等是Android应用程序的基础。
android.provider:方便开发者访问标准的内容提供者(比如联系人数据库),provider包提供类给开发者访问标准的数据库。
android.telephony:telephony API让开发者直接接触电话底层,开发者可以打电话、接电话、显示通话记录、通话状态和短消息。
android.webkit:WebKit的软件包功能的API与基于Web的内容的工作,其中包括一个WebView控件在您的活动中嵌入浏览器和cookie管理器。

android.util:核心工具包中包含底层类,字符串格式化和XML解析工具、底层类​​。
android.os:操作系统包提供了访问基本的操作系统服务,如消息传递、进程间通信、时钟功能和调试。
android.graphics:图形API提供了支持画布、颜色和绘制图元的低级别的图形类并且支持绘制画布。
android.text:用于显示和解析文本的文本处理工具。
android.database:在数据库处理游标时提供底层类支持。
android.content:content API管理数据访问,提供服务来管理资源、内容提供者(content provider)和包。
android.view:视图是核心的用户接口类。所有用户界面元素使用的是一系列视图,以构成用户交互的组件。
** android.widget**:内置在View包内,小部件类是“这里是我们前面创建的”用户界面元素,可以在自己的应用程序中使用。包括列表、按钮和布局。
com.google.android.maps:高级的API,它提供了访问本地地图控件,可以在自己的应用程序中使用。包括MapView的控制、用于标注和控制您的嵌入式地图的叠加以及MapController类。
android.app:一个高层次的包,允许访问应用程序模型。该应用程序包包括Activity和Service的API等是Android应用程序的基础。
android.provider:方便开发者访问标准的内容提供者(比如联系人数据库),provider包提供类给开发者访问标准的数据库。
android.telephony:telephony API让开发者直接接触电话底层,开发者可以打电话、接电话、显示通话记录、通话状态和短消息。
android.webkit:WebKit的软件包功能的API与基于Web的内容的工作,其中包括一个WebView控件在您的活动中嵌入浏览器和cookie管理器。

作者:AngryFox 分类: Uncategorized October 4th, 2015 暂无评论

收集用户数据,给产品和运营提供参考
合理地组织各业务方开发的业务模块,以及相关基础模块
每日app的自动打包,提供给QA工程师的测试工具

im
要解决用户登录、退出的问题
解决不同用户间数据交流的问题
解决用户数据存储的问题
如果是多台服务器的集群,就要解决用户连接的寻址问题

在线数据迁移一般分为四个步骤:一,上线双写,即同时写入新旧两种数据;二,历史数据离线搬迁,即离线将历史存量数据从旧系统搬到新系统;三,切读,即将读请求路由到新系统;四,清理沉淀,包括清理旧的数据,回收资源,及清理旧的代码逻辑,旧的配套系统等等,将迁移过程中的经验教训进行总结沉淀,将过程中开发或使用的工具进行通用化改造,以备下次使用。
Tcpcopy环境 –> 线上环境 uid 白名单(内部工程师)–> 线上环境百分比灰度 0.01%,1%,10% –> 线上环境全量。tcpcopy 环境用来验证代码在线上环境是否正常,uid白名单用来验证功能是否正常,百分比灰度用来验证性能和资源压力是否正常,所有验证都通过后,最后才进行全量切换。一般这个过程会持续一周到两周。

MySQL索引设计不合理,导致极端情况下面拖垮了整个性能。
代码健壮性不足,对于可能引起死循环的应用没有做更多的检查处理。
日志缺失,很多偷懒不写日志,觉得没啥用,但偏偏遇到问题了才会有用。
统计缺失,没有详细的统计信息,用来监控整个服务。
没有更健壮的限频限容策略,虽然我们开启了,但因为程序内部bug,导致完全没用。

全局唯一ID
1. 唯一性
2. 时间相关
3. 粗略有序
4. 可反解
5. 可制造

一个简洁的并发模式不需要这些复杂的底层元素,只需协程和通道
协程可以试图向通道放入数据,如果通道满了,会挂起协程,直到通道可以为他放入数据为止。
· 协程可以试图向通道索取数据,如果通道没有数据,会挂起协程,直到通道返回数据为止。
多路复用技术可以用来整合多个通道。提升性能和操作的便捷。
依赖注入(DI)是一种解耦组件之间依赖关系的设计模式

在基于Unix的操作系统中,signal(信号)是与长时间运行的进程交互的常用方法.

SIGTERM: 优雅地停止进程
SIGHUP: 重启/重新加载进程 (例如: nginx, sshd, apache)
google内部的F1和spanner。开源实现,https://github.com/pingcap/tidb

分布式kv如何做到任意扩展?
Sharding, split, merge

NuoDB对比F1 TiDB
通过并发循环可以提供性能,利用多核,解决CPU热点。正因为协程可以大量创建,才能在循环体中如此使用,如果是使用线程的话,就需要引入线程池之类的东西,防止创建过多线程,而协程则简单的多。 协程和内存一样,是系统的资源。对于内存,有自动垃圾回收。但是对于协程,没有相应的回收机制。协程执行结束后就会销毁。协程也会占用内存,如果发生协程泄漏,影响和内存泄漏一样严重。
单空闲单链表找到一个合适的地址(槽)的策略方式供选择:
首次适应(first fit):我们通过扫描整个链表,寻找第一个能够符合我们的分配请求的位置。
循环首次适应(next fit):与首次适应类似,但我们追踪上衣一次访问的地方,然后从这里开始寻找下一个符合请求要求的位置,所以你没必要每次都从链表的头开始扫描。
最佳策略(best fit):寻找内存中符合要求的最小的块。
最坏策略(worst fit):寻找内存中符合要求的最大的块。
通过 pprof 等方法分析目标程序来解决掉

作者:AngryFox 分类: Uncategorized October 3rd, 2015 暂无评论

关闭 Supervisor 的进程管理机制,改用手动方式启动 ElasticSearch 进程试试看。结果一切恢复正常

ElasticSearch导致Too many open files
解决思路两个出发点,
1。是修改supervisor 启动脚本里加上 楼上的参数
2。是修改了系统的/etc/security/limits.d/90-nproc.conf 文件 这个优先于/etc/security/limits.conf

使用 Supervisor 的进程管理机制,它会作为父进程 FORK 出子进程,也就是 ElasticSearch 进程,鉴于父子关系,子进程允许打开的最大文件数不能超过父进程的阈值限制,但是 Supervisor 中 minfds 指令缺省设置的允许打开的最大文件数过小,进而导致 ElasticSearch 进程出现故障。

http://www.alibench.com/

Google通过一个大规模的MySQL集群通过sharding支撑,用原子钟搞定Spanner,然后再Spanner上构建了SQL查询层F1,BigTable(KV)+F1(SQL)+Spanner(高性能分布式事务支持),同时Spanner还有一个非常重要的特性是跨数据中心的复制和一致性保证(通过Paxos实现),多数据中心,刚好补全了整个Google的基础设施的数据库栈
presharding,hash算法是crc32(key)%1024个slot,然后通过proxy将不同的key的请求转发到不同的机器上,数据的副本还是通过Redis本身保证

nstat

mysql规范的意义
保证线上数据库schema规范
减少出问题概率
方便自动化管理
规范需要长期坚持,对开发和DBA是一个双赢的事情
想想没有开发规范,有的开发写出各种全表扫描的SQL语句或者各种奇葩SQL语句,我们之前就看过开发写的SQL 可以打印出好几页纸。这种造成业务本身不稳定,也会让DBA天天忙于各种救火。
基本命名和约束规范
表字符集选择UTF8 ,如果需要存储emoj表情,需要使用UTF8mb4(MySQL 5.5.3以后支持)
存储引擎使用InnoDB
变长字符串尽量使用varchar varbinary
不在数据库中存储图片、文件等
单表数据量控制在1亿以下
库名、表名、字段名不使用保留字
库名、表名、字段名、索引名使用小写字母,以下划线分割 ,需要见名知意
库表名不要设计过长,尽可能用最少的字符表达出表的用途
字段规范
所有字段均定义为NOT NULL ,除非你真的想存Null
字段类型在满足需求条件下越小越好,使用UNSIGNED存储非负整数 ,实际使用时候存储负数场景不多
使用TIMESTAMP存储时间
使用varchar存储变长字符串 ,当然要注意varchar(M)里的M指的是字符数不是字节数;使用UNSIGNED INT存储IPv4 地址而不是CHAR(15) ,这种方式只能存储IPv4,存储不了IPv6
使用DECIMAL存储精确浮点数,用float有的时候会有问题
少用blob text
关于为什么定义不使用Null的原因
浪费存储空间,因为InnoDB需要有额外一个字节存储
表内默认值Null过多会影响优化器选择执行计划

作者:AngryFox 分类: Uncategorized October 1st, 2015 暂无评论

Bitcode:开启Bitcode编译后,可以使得开发者上传App时只需上传Intermediate Representation(中间件),而非最终的可执行二进制文件。 在用户下载App之前,AppStore会自动编译中间件,产生设备所需的执行文件供用户下载安装。

其中,Bitcode的机制可以支持动态的进行App Slicing,而对于Apple未来进行硬件升级的措施,此机制可以保证在开发者不重新发布版本的情况下而兼容新的设备。Xcode7默认是开始了Bitcode,如果不想使用可以手动关闭Bitcode:
即便Kafka性能很高,也会很容易地造成网络饱和和磁盘冲击。
腾讯征信公司背后的一整套征信平台(TCP),其中包括征信数据平台(TCD)、征信服务平台(TCS)以及各类数据管理、数据模型、策略引擎等

API平均响应时间由100ms降至10ms;
所需EC2实例的数量减少了85%;
由于Go可以编译成一个单独的二进制文件,而Go 1.5让交叉编译变得很容易,所以他们现在能够提供一个Repustate自托管版本;
由于Python和Go相似,所以他们能够快速重建单元测试。

将所有的数据基础设施分成了两个单独的镜像集群:一个用于运行所有关键的业务任务……另一个用于即时查询

分布式系统复制日志服务
可靠性:稳定,而且在出现各种常见问题(网络划分、集群拓扑变化、流量峰值等)时都能提供可以预见的性能;
高吞吐量:在高峰时段可以支持每秒数以百万计的消息传递;
低延迟:始终如一的低延迟;
工作负载隔离:在一个以日志为中心的分布式系统中,工作负载可以分为如下三类:写日志尾、读日志尾和“追赶读(catch-up read)”。各类负载互不影响;
可扩展性:在地理、节点数量、每秒请求数、数据规模、租户数量等各个维度可扩展;
可操作性:在扩展时易于操作,比如可以很容易地增加或移除节点;
简单:接口简单,便于开发人员使用。

缓存会遇到的问题
缓存数据更难以推断:缓存意味着不从真实数据源读取数据。因此,每次读或写缓存数据都可能与真实数据源不匹配。在问题追踪时必须经常考虑这种情况。
缓存数据可能导致“视角(perspective)”Bug:比如,一个新闻站点上有个“最佳文章”列表,其中的内容可能会随登录用户的不同而变化。一个常犯的缓存错误是缓存的数据独立于视角,具有不同视角的用户看到了相同的内容。这会导致严重的隐私甚或安全问题。
涉及缓存的行为难以重现:当引入缓存,就引入了一个新的层,其上的行为可能同预期不符。缓存对象依赖访问模式,会随时间或其它因素变化。一旦出现问题,很难通过重现行为来帮助问题修复。
访问模式变化可能会损害性能:访问模式变化可能会莫名其妙地导致缓存命中率降低,延迟增加,吞吐量减少。但流量水平可能会保持不变,这屏蔽了问题原因,潜在地增加了底层数据源的负载及问题处理的难度。
进程内缓存可能会增加GC压力:在具有垃圾收集功能的语言中,大量长寿命的缓存对象会增加垃圾收集的时间和次数。
缓存失败恢复困难:缓存机器故障,存储在内存中的数据就会丢失,无法简单地重新上线。在缓存重新创建的过程中,吞吐量可能会下降。