memcache讲解和在.net中初使用
前言
将登陆信息和状态保存到哪里一直是我困惑的。保存到cookie里面?不不,太不安全;保存到session?不不,处于进程里影响性能降低还易丢失;保存到数据库?我的确这样想过也试过,安全又完整但是读数据库让我不满意;保存到HttpRunTime.Cache里面?
在查找一些资源后我了解到可以保存到缓存数据库里面,memcache就是其中一款缓存数据库,并且还拥有其他的好处。
在我们了解memcache之前,先了解以下信息:
传统数据库面临的问题
Web程序在面临海量数据、高并发请求时,传统的数据库往往会面临以下问题:
数据库死锁
数据库基本操作curd,死锁是数据库高并发的明显问题:r要加S锁(共享锁,可以查看但是无法cud),cud要加X锁(排他锁,可以curd,其他事务无法再次加锁),当高并发请求,数据库可能产生死锁(数据库阻塞,永远等待状态)。这是无法避免的问题。以下是解决死锁的常见手段:
- 死锁预防
使用数据库逻辑上来预防死锁,但是这显然还会出现慢的问题。 -
读写分离
就是做数据库集群,使用主从数据库:将数据保存到多个主从数据库里面,主数据库负责cud,多个从数据库负责r,这样不仅避免死锁,也提高了速度。但是同样带来了问题:-
数据同步
多个数据库要同步数据,常见三种数据库对数据同步的支持:- SqlServer
sqlserver在数据同步方面很弱,并不适合数据同步。主要有三种方法实现:买中间件(不可靠)、发布订阅(主库生成数据库快照给其他数据库,但是有延迟)、代码实现写入多个数据库(不好)。 - Mysql
有很多第三方组件可以实现,很多成功的例子。 - Oracle
本身就支持数据库同步,只需要配置以下就好了。(另外Oracle因为会最大程序占用资源,所以效率很高)
- SqlServer
-
磁盘IO
数据库把数据保存到磁盘上,读取慢,磁盘IO是一大原因,因为磁盘IO是硬件问题。常见解决方式如下:
水平分布和垂直分布
相当与分布式数据库:水平分布是根据数据自定义规则(如userId的奇偶)保存到不同的数据库,垂直分布是根据数据的类型(如图片库,收藏夹)保存到不同的数据库。-
使用NoSql中的内存数据库
- NoSql定义
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,非关系型数据库,存储数据更多的是保存数据本身,不需要预先定义数据格式。
- 内存数据库
memcache是分布式内存数据库,使用key-value存储数据。将数据缓存到内存中可以高速存取。因为只是key-value集合即不会有死锁。并且实现了数据共享,试想下某度贴吧和云盘反复当你登陆,不是很好的感觉吧。
- NoSql定义
接下来使用控制台应用程序来操作memcache。
正文
了解memcache
memcache官网
memcached是一种高性能,分布式的内存对象缓存系统,本质上是通用的,但最初旨在通过减轻数据库负载来加速动态Web应用程序。
原理
- 内存处理
- 内存分区解决内存碎片问题
memcache实现会对可用内存进行划分分区,每个区内分成若干块,同一个区内块的大小固定。当进行数据存储,就会自动寻找适合的区存进块内。最大的块是1M。比如第一个区内只存0.5M的块,第二个块只存0.6M的数据,现在过来一个0.55M的数据就会存到第二个区内,占用0.6M的空间,虽然块内有空间浪费,但是块与块之间没有缝隙。- 惰性删除
memcache并没有提供数据监控机制,而是查询到某个数据,如果过期才会删除。
如果内存满了,就会策略LRU闲置>过期>最少访问来清空删除数据
- 惰性删除
- 内存分区解决内存碎片问题
- 客户端实现分布式:
给memcache配置多个服务器ip,当需要查询时会寻找哪台服务器或者添加一个数据时会保存到哪一个服务器上呢?memcache对key值进行哈希计算然后对配置的服务器总数进行取余,余数是几就向第几台服务器进行操作。这就导致了一个问题:当服务器数量发生变化时会导致部分数据丢失(查询指向服务器错误)。想要这种影响降低到最低,可以使用一致性哈希算法。 - 走socket
数据通信走的是socket。
基本命令
这里引用IBM developerWorks里面的一篇文章部分内容
基本 memcached 客户机命令
您将使用五种基本 memcached 命令执行最简单的操作。这些命令和操作包括:
set add replace get delete
前三个命令是用于操作存储在 memcached 中的键值对的标准修改命令。它们都非常简单易用,且都使用清单 5 所示的语法:
清单 5. 修改命令语法
command +key +flags +expiration time+ bytes+
value
key key 用于查找缓存值
flags 可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息
expiration time 在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
bytes 在缓存中存储的字节点
value 存储的值(始终位于第二行)
其中key最大长度255字符。
memcache与memcached
memcache是项目的名字,memcached是项目被可执行代码的名字,一般指同一个东西。
memcache的适用范围
可以将其视为应用程序的短期内存。不适合保存持久化数据。其是分布式系统,数据分布在多个机器上迁移很麻烦。也不会记录数据。这里用户的登陆状态并不需要专门保存,可以使用memcache保存。
memcache的客户端和服务端
memcache有客户端和服务端之分,客户端编程,需要引用相应的dll文件;服务端开启memcached服务。
memcache服务端本身不支持windows下使用,但是一些爱好者提供了windows下的exe程序来开启这个服务。
请搜索Memcached.ClientLibrary来下载你需要使用的dll文件和exe服务,我是在sourceforge这里下载的。
memcache与redis异同
memcache经常与redis进行对比
- 相同点:
都使用内存;效率都很高,1s内读1w次,写10w次都很平常; - 不同点:
- 线程
memcache是多线程,redis是单线程,所以写多数据时memcache效率更好。 - 数据类型
memcached使用key-value存储,本身就是一个hash(哈希表)。
redis现阶段提供五种数据类型:string(字符串类型)、list(链表)、set(集合)、zset(有序集合)、hash(哈希表)。 - 数据存储
memcache是分布式系统,每个服务器都值保存一部分数据,迁移很弱(memcache定位就是短期内存,保存在memcache里面的数据本来就是不需要迁移的)。断电丢失数据。
redis启用内存但是可以保证数据持久化、完整性和同步,即每个服务器都保存完整数据。这是因为redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置,断电之后可以通过文件来恢复数据;使用快照的方式进行数据同步。
- 线程
简单使用memcached
打开memcache服务
这一步相当玉开启服务端的memcache服务。在windows下开启服务相当简单,直接打开这个exe程序就行了
但是这个程序结束后服务就关闭了。这里把它安装成windows下的一个服务。
打开管理员命令提示符,输入exe文件的路径和安装命令
然后memcached就成为windows下的一个服务了,可以设置自动手动之类的。
使用memcached命令
如果只是布置服务端的话,上一步就已经完成了。如果想要连接服务端的memcached服务并且使用命令的话,可以使用以下步骤:
1. 打开Telnet客户端服务
2. 打开管理员命令提示符连接服务端memcached服务
3. 使用命令
连接成功后就会进入控制台界面
第一行代码是不可见的,可以按enter再输入stats(或者直接stats)打开memcached统计信息
然后就可以愉快地输入你的命令了,如使用add命令来添加和get命令获取值,key值已经存在就会返回NOT_STORED提示,如果正确添加就会返回STORED提示。
注意:
命令无法退格,所以输错就按enter重新输入
memcached默认端口11211
添加引用
默认memcache引用程序集会自带一个log4net.dll文件以便错误时可以记录日志,这里并不需要记录日志就不引用。
程序代码
using Memcached.ClientLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MemcachedDemo
{
class Program
{
static void Main(string[] args)
{
//SockIOPool是Memcached客户端提供的一个套接字连接池,是与Memcached服务器端交换数据的对象。
//SockIOPool在应用程序启动时初始化一次就可以了,我们可以把这个工作放在 GLOBAL.ASAX.CS的Application_Start方法里。
try
{
MemcachedClient mc = new MemcachedClient();
mc.EnableCompression = false;
string[] serverlist = {"127.0.0.1:11211"};
SockIOPool pool = SockIOPool.GetInstance();
//初始化
pool.SetServers(serverlist);
pool.InitConnections = 3;
pool.MinConnections = 3;
pool.MaxConnections = 50;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize();
mc.Add("key1","这是第一个数据",0);
string value= mc.Get("key1") as string;
Console.WriteLine(value);
}
catch (Exception err)
{
//这里可以用Log4Net记录Error
}
Console.ReadLine();
}
}
}
- 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
运行效果
引申
- 集群和分布式
数据库集群:往往在同一局域网,使用同一个IP,每个数据库有完整数据,难点是数据同步,迁移方便。
数据库分布式:每个系统拥有部分数据,使用使用简单,数据迁移麻烦。 - Redis主从数据库
主库负责写,从库负责读,压力分流。
主库挂掉,随机选择一个从库成为主库。
数据如何同步:快照。
总结
- memcache的key值最大255字符
- memcached的块最大1M