memcache
Page为内存分配的最小单位。
Memcached 的内存分配以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定。如果需要申请内存 时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配
Slabs划分数据空间。
Memcached 并不是将所有大小的数据都放在一起的,而是预先将数据空间划分为一系列slabs,每个slab只负责一定范围内的数据存储。每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如:slab 3只存储大小介于137 到 224 bytes的数据。如果一个数据大小为230byte将被分配到slab 4中。每个slab负责的空间其实是不等的,memcached默认情况下下一个slab的最大值为前一个的1.25倍,这个可以通过修 改-f参数来修改增长比例。
Chunk才是存放缓存数据的单位。
Chunk 是一系列固定的内存空间,这个大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached实际存放缓存数据的地方,因为chunk的大小固定为slab能够存放的最大值, 所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小小于chunk的大小,空余的空间将会被闲置,这个是为了防止内存碎片而设 计的。例如,chunk size是224byte,而存储的数据只有200byte,剩下的24byte将被闲置。
Slab的内存分配。
Memcached在启动时通过-m指定最大使用内存,但是这个不会一启动就占用,是随着需要逐步分配给各slab的。
如果一个新的缓存数据要被存放,memcached首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如 果没有则要进行申请。slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到 page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储 数据。如下图,slab 1和slab 2都分配了一个page,并按各自的大小切分成chunk数组。Memcached内存分配策略
按slab需求分配page,各slab按需使用chunk存储。
Memcached分配出去的page不会被回收或者重新分配Memcached申请的内存不会被释放slab空闲的chunk不会借给任何其他slab使用,如果所有page都已经存满了。并且memcache已不能再分配新的内存空间。将根据LRU算法(最近最少使用),清除某个item并将新项存储在该位置分布式解决方案
取模算法方式
将key转换为32位的数字,并与memcached服务器的总数进行相除取得余数。而这个余数就是memcached服务器的节点node。有了这个node我们就可以确定memcached服务器,就可以发送命令给memcached执行了。 缺点是不方便扩展和机器宕机后不能自动调整集群
$node sprintf('%u', crc32($key)) % $total
一致哈希算法方式
通过虚拟节点的方式实现,可以使不可控的存储节点能够尽可能的均匀分布在圆环上,从而达到数据均匀缓存在各个主机里。其次增加与删除虚拟节点对于之前缓存的整体数据影响非常小。
Redis
高性能key-value 存储系统,它通常被称为数据结构服务器。因为值可以是:字符串、哈希、列表、集合、有序集合
特点:单线程、快(每秒10万次SET操作)、拥有很多原子操作方法,保证数据一致性、将具有临时性和数据持久化
二进制安全、一个字符串类型的值最多能保存512M内容。
集合(set)
集合是一个无需字符串集合。元素不能重复。一个集合最多可以包含2^32-1个元素。方便计算不同集合的交集、并集、差集。常用应用:关注列表、粉丝列表、共同关注或者粉丝等
有序集合(zSet)
每个有序集合成员都关联着一个评分,这个评分的用语把有序集合中的成员按最低分到最高分排列。(应用于各种有排序条件的列表:访问足迹最近访问的时间戳作为排序分)
watch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
header("content-type:text/html;charset=utf-8");
$redis = new redis();
$result = $redis->connect('127.0.0.1', 6379);
$watchkey = $redis->get("watchkey");
$rob_total = 10;
if($watchkey<$rob_total){
$redis->watch("watchkey"); // 监视某个key
$redis->multi(); // 开启事物
$redis->hSet("watchlist","user_id_".mt_rand(1, 9999),time()); // 业务操作
$redis->incr("watchkey");
$rob_result = $redis->exec(); // 如果执行操作期间,被监视的KEY被其它客户端修改了,则exec失败
if($rob_result){
$watchlist = $redis->hGetAll("watchlist");
echo "抢购成功!<br/>";
echo "剩余数量:".($rob_total-$watchkey-1)."<br/>";
echo "用户列表:<pre>";
var_dump($watchlist);
}else{
echo "手气不好,再抢购!";
}
}else{
echo "已被抢购完!";
}数据持久化:RDB 和 AOF
RDB:就是快照存储,是默认的持久化方式,按照一定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期.通过修改配置文件的dbfilename 来修改
1
2
3save 900 1
save 300 10
save 60 10000
AOF:修改配置appendonly yes 来开启,开启后写命令会依次记录到此文件。AOF 大!即使通过bgrewriteaof 命令移除冗余记录也大。恢复慢
1
2
3 # appendfsync always 每次命令都写,最安全,性能最差
appendfsync everysec 每秒同步一次(默认)
# appendfsync no 不主动同步,由操作系统负责鞋服,约30秒一次
mongoDB
介于关系数据库和非关系型数据之间的文档数据库,支持的数据结构非常松散,类似json的bson格式,因此可以存储比较复杂的数据类型。支持对数据字段奖励索引
- 模式自由,通过数据分片实现高伸缩性
- 处理地理信息
- 高可用,内置故障迁移
- 高性能低延时实时数据(查询QPS接近MySQL的两倍左右, 插入QPS接近MySQL的五倍左右)
- 局部索引,TTL索引,固定集合
- 不适合高度事务性系统,不适合需要复杂SQL处理的查询
- 与关系型数据库结构对比:文档≈行,集合≈表,数据库≈数据库