Memcached简介
Memcached是一个专门用来做缓存的服务器,而且缓存的数据都在内存中。Memcached就相当于一个Dictionary键值对集合,保存的是键值对,然后根据key取value。
当然web服务器和Memcached之间还是要网络间通讯,效率还是没有进程内缓存效率高。Memcached程序重启之后数据就会消失。
32位系统 1.4.4版本:http://static.runoob.com/download/memcached-win32-1.4.4-14.zip
64位系统 1.4.4版本:http://static.runoob.com/download/memcached-win64-1.4.4-14.zip
32位系统 1.4.5版本:http://static.runoob.com/download/memcached-1.4.5-x86.zip
64位系统 1.4.5版本:http://static.runoob.com/download/memcached-1.4.5-amd64.zip
安装
在 1.4.5 版本以前 memcached 可以作为一个服务安装,而在 1.4.5 及之后的版本删除了该功能。因此我们以下介绍两个不同版本 1.4.4 及 1.4.5的不同安装方法:
memcached <1.4.5 版本安装
1、解压下载的安装包到指定目录。
2、在 1.4.5 版本以前 memcached 可以作为一个服务安装,使用管理员权限运行以下命令:
c:\memcached\memcached.exe -d install
注意:你需要使用真实的路径替代 c:\memcached\memcached.exe。
3、然后我们可以使用以下命令来启动和关闭 memcached 服务:
c:\memcached\memcached.exe -d start
c:\memcached\memcached.exe -d stop
4、如果要修改 memcached 的配置项, 可以在命令行中执行 regedit.exe 命令打开注册表并找到 "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\memcached" 来进行修改。
如果要提供 memcached 使用的缓存配置 可以修改 ImagePath
为:
"c:\memcached\memcached.exe" -d runservice -m 512
-m 512 意思是设置 memcached 最大的缓存配置为512M。
此外我们还可以通过使用 "c:\memcached\memcached.exe -h"
命令查看更多的参数配置。
5、如果我们需要卸载 memcached ,可以使用以下命令:
c:\memcached\memcached.exe -d uninstall
.NET连接Memcached
安装Memcached 的.Net 开发包:Install-Package EnyimMemcached
写一个简陋的帮助类,后面再完善
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Enyim.Caching;
using Enyim.Caching.Configuration;
using Enyim.Caching.Memcached;
namespace ConsoleApp1
{
public class MemCachedHelper<T>
{
private static string SERVER = "127.0.0.1:11211";
//过期时间:20分钟
private static int TimeSpanExpired = 20;
private static MemcachedClientConfiguration mcConfiguration = new MemcachedClientConfiguration();
static MemCachedHelper()
{
mcConfiguration.AddServer(SERVER);
}
/// <summary>
/// Set设置key,value
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static Boolean SetKey(string key, object value)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
/*
* MemcachedClient.Store(StoreMode mode, string key, object value); 使用缓存键将项插入到缓存中, 以引用其位置。
* StoreMode mode 定义项在缓存中的存储方式。
*/
return client.Store(StoreMode.Set, key, value,TimeSpan.FromMinutes(TimeSpanExpired));
}
}
/// <summary>
/// 获取值
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static T GetValue(string name)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
return (T)client.Get(name);
}
}
}
}
1.Memcache存入的是键值对。
Memcache存入数据的3中模式Set
、Replace
、Add
,
根据名字就能猜出来:
Set:存在则覆盖,不存在则新增
Replace:如果存在则覆盖,并且返回true;如果不存在则不处理,并且返回false;
Add:如果不存在则新增,并且返回true;如果存在则不处理,并且返回false;
没特殊要求一般用Set就可以了。
2.对象序列化
如果保存普通类对象,则对象必须可序列化(不同Memcached客户端保存对象的机制都不尽相同)。
例子:
public class Student
{
public Student(int age, string name)
{
this.age = age;
this.name = name;
}
private int age { get; set; }
private string name { get; set; }
}
这里
//返回false
Console.WriteLine(MemCachedHelper<Student>.SetKey("student", student));
应该添加
[Serializable]
public class Student
{
...
}
3.存入设置过期时间
设置最后一个TimeSpan类型的参数:
client.Store(Enyim.Caching.Memcached.StoreMode.Set, "name", "tangsansan",TimeSpan.FromSeconds(5));
4.读取:client.Get("name")
如果找不到,则返回null。当然也可以用public bool TryGet(string key, out object value)
。还支持泛型的public T Get<T>(string key)
。
5.Remove(string key)
则是删除一个key对应的内容。
Key的长度最高是250个字符,Value最长1M(大约
524288
个字)。
6.ExecuteXXX
与Store、Get、Remove配套的还有ExecuteXXX
方法,唯一区别就是返回值信息更详细。
7.Key的选择(命名)
Memcached就相当于一个大键值对,不同系统放到Memcached中的数据都是不隔离的,因此设定Key的时候要选择好Key,这样就不容易冲突。
建议规则“系统名字_模块名字_业务Key”,比如“Shop_Admin_FilterWords”
8. Increment、Decrement
是用来对计数器进行增减的,不过用得少。用Redis更合适。
2.CAS(乐观锁)
用来解决并发问题:读出一个值,做一些判断或者处理,再写回,有可能有并发的问题。
Cas是Memcached 1.2.5之后引入的特性,类似于数据库的“乐观锁”,查询的时候查出一个cas值,在写入的时候带着这个cas值,如果发现cas值已经变了,则说明已经有别人改过了。
辅助类
namespace ConsoleApp1
{
public class MemCachedHelper<T>
{
private static string SERVER = "127.0.0.1:11211";
//过期时间:20分钟
private static int TimeSpanExpired = 20;
private static MemcachedClientConfiguration mcConfiguration = new MemcachedClientConfiguration();
static MemCachedHelper()
{
mcConfiguration.AddServer(SERVER);
}
/// <summary>
/// Set设置key,value
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool SetKey(string key, object value)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
/*
* MemcachedClient.Store(StoreMode mode, string key, object value); 使用缓存键将项插入到缓存中, 以引用其位置。
* StoreMode mode 定义项在缓存中的存储方式。
*/
return client.Store(StoreMode.Set, key, value, TimeSpan.FromMinutes(TimeSpanExpired));
}
}
/// <summary>
/// 设置Cas
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="cas"></param>
/// <returns></returns>
public static CasResult<bool> SetCas(string key, object value, ulong cas)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
/*
* MemcachedClient.Cas(StoreMode mode, string key, object value, TimeSpan validFor, ulong cas);
* StoreMode mode 定义项在缓存中的存储方式。
*/
return client.Cas(StoreMode.Set, key, value, TimeSpan.FromMinutes(TimeSpanExpired), cas);
}
}
/// <summary>
/// 获取值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetValue(string key)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
return (T)client.Get(key);
}
}
/// <summary>
/// Cas获取值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static CasResult<T> GetCasValue(string key)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
return client.GetWithCas<T>(key);
}
}
/// <summary>
/// 删除Key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static Boolean Remove(string key)
{
using (MemcachedClient client = new MemcachedClient(mcConfiguration))
{
return client.Remove(key);
}
}
}
}
实例:
var res = MemCachedHelper<string>.GetCasValue("student");
Console.WriteLine("Result:{0},Cas:{1},StatusCode:{2}", res.Result, res.Cas, res.StatusCode);
Console.ReadKey();
CasResult<bool> casR = MemCachedHelper<string>.SetCas("student", "acc", res.Cas);
if (casR.Result)
{
Console.WriteLine("更新成功,cas值:{0}",casR.Cas);
}
else
{
Console.WriteLine("更新失败,已经有人先更新了,cas值:{0}",casR.Cas);
}
Console.ReadKey();
3.集群
memcached重启之后短时间内大量的请求会涌入数据库,给数据库造成压力,解决这个的方法就是使用集群,有多台Memcached服务器提供服务。
当memcached服务器压力大了之后也有必要搞memcached集群来分担压力。
3.1 “雪崩”问题
Memcached服务器的“雪崩”问题:如果所有缓存设置过期时间一样,那么每隔一段时间就会造成一次数据库访问的高峰.
解决的方法:
就是缓存时间设置不一样,比如加上一个随机数
。
3.2 集群方法
Memcached的集群实现很简单,集群节点直接不进行通讯、同步,只要在多个服务器上启动多个Memcached服务器即可,客户端
决定把数据写入不同的实例,不搞主从复制,每个数据库实例保存一部分内容。
然后mcConfig.AddServer("127.0.0.1:11211");添加多个服务器ip地址,然后客户端根据自己的算法决定把数据写入哪个Memcached实例,取数据库的时候再根据同样的定位算法去哪台服务器上去取。
3.3 节点定位算法
节点定位算法有很多种,最常用的有两种 Ketama、VBucket。建议用Ketama就可以了。节点定位算法会自动处理故障服务器。
mcConfig.NodeLocatorFactory = new KetamaNodeLocatorFactory()。
缓存要求都不高。
Consistent Hashing最大限度地抑制了hash键的重新分布。另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上。因为使用一般的hash方法,服务器的映射地点的分布非常不均匀。使用虚拟节点的思想,为每个物理节点(服务器)在圆上分配100~200个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上,就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。
3.3.1 Ketama(常用)
Ketama是根据Key算出一个hash值,根据hash值再算到服务器;
3.3.2 VBucket
VBucket也是根据key算出hash值,但是不是直接根据hash值算出服务地址,而是维护一个VBucket表,在表中指定不同的hash值由不同的服务器处理,还可以临时改变指向。