• NoSQL之一:Memcached


    一、NoSQL简介

           NoSQL并不是No SQL(不再需要SQL),而是指Not Only SQL(不仅仅只有SQL)。NoSQL并不是用来替代关系型数据库的,而是在某些使用关系型数据库不合适的场景中,可以使用NoSQL数据库进行优化,而在系统中主要的、常规的数据仍然使用关系型数据库。
      常用的NoSQL数据库有Memcached、Redis、MongoDB等,其中前两者属于键值对数据库,后者属于文档数据库。它们都有各自的优缺点以及使用场景。

    二、Memcached介绍与安装

           Memcached是一个专门用来做缓存的数据库,缓存的数据都是在内存当中,当数据库重启之后,数据也就都丢失了。其相当于一个Dictionary键值对集合,根据Key值取Value值。

      1、Memcached安装

             从网上下载Memcached-win64-1.4.4-14.zip安装包。解压后,通过管理员权限执行如下命令,可将其安装成服务:

    G:MemcachedAfterInstall>memcached.exe -d install

       2、Memcached可视化工具

        TreeNMS是一款Redis、Memcached可视化客户端工具,实现基于Web方式对Redis、Memcached数据库进行管理、维护。可通过如下链接http://www.treesoft.cn/dms.html进行下载。

        下图即该工具的使用界面:


      3、Memcached优缺点

        优点:

        1)多线程,可以充分利用CPU多核的处理性能;

        2)做缓存性能高;

        缺点:

        1)只能保存键值对数据,键值只能是字符串,如果有对象数据只能自己序列化Json字符串;

        2)数据只保存在内存中,重启后会丢失;

        3)Key最大长度250个字符,Value最长1M;

     三、在.Net Core中的使用

      1、组件包的安装与使用

        在.Net Core项目中调用Memcached数据库的资源,可以使用EnyimMemcachedCore客户端组件包。

        通过该组件向Memcached数据库存入数据有三种模式,分别如下:

          1)Set:存在则覆盖,不存在则新增;

          2)Replace:如果存在则覆盖,并且返回true;如果不存在则不处理,并且返回false;

          3)Add:如果不存在则新增,并且返回true;如果存在则不处理,并且返回false;

        故,根据以上可知,如果没有特殊要求一般用Set即可。

        注意:Memcached数据库中的key的长度最高为250个字符,value的值最大为1M。

      2、案例演示

        为了演示方便,创建了一个基于.Net Core3.1版本的WPF项目,界面如下:

     

     

     

     

     

     

     

         

        1)存储数据、读取数据 

    public partial class MainWindow : Window
    {
      private readonly MemcachedClient _memcachedClient;
      private static readonly ILoggerFactory _loggerFactory = new LoggerFactory();
      public MainWindow()
      {
        InitializeComponent();
        var options = new MemcachedClientOptions();
        options.AddServer("127.0.0.1", 11211);
        _memcachedClient = new MemcachedClient(_loggerFactory, new MemcachedClientConfiguration(_loggerFactory, options));
      }
    }
    private void btnStorage_Click(object sender, RoutedEventArgs e)
    {
      var result = _memcachedClient.Store(StoreMode.Set, "supersnow", "yao");
      MessageBox.Show($"保存成功?{result}");
    }
    
    private void btnRead_Click(object sender, RoutedEventArgs e)
    {
      var result = _memcachedClient.Get("supersnow");
      MessageBox.Show($"读取结果:{result}");
    }  

      

         2)存储类数据、读取类数据

    private async void btnStorage_Class_Click(object sender, RoutedEventArgs e)
    {
      var person = new Person
      {
        Name = "SuperSnow",
        Age = 25
      };
      var result = await _memcachedClient.StoreAsync(StoreMode.Set, "person_key", person, TimeSpan.FromSeconds(3600));
      MessageBox.Show($"保存Person成功?{result}");
    }
    private async void btnRead_Class_Click(object sender, RoutedEventArgs e) {   var result = await _memcachedClient.GetAsync<Person>("person_key");   if (result.Value == null)     MessageBox.Show("数据已经失效");   else     MessageBox.Show($"读取Person结果:{result.Value.Name}:{result.Value.Age}"); } public class Person {   public string Name { get; set; }   public int Age { get; set; } }
      

      

         3)读取数据异常

        对于读取数据失败时,返回结果为false。但是,此时并不知道失败的原因是什么,可以通过如下方法获取失败原因:

     private void btnRead_Exception_Click(object sender, RoutedEventArgs e)
     {
        var result = _memcachedClient.ExecuteRemove("abc");
        MessageBox.Show($"结果:{result.Success},Message:{result.Message},Exception:{result.Exception},StatusCode:{result.StatusCode},InnerResult:{result.InnerResult.Message}");
     } 

      

         4)Memcached Cas操作

        Cas操作类似于关系型数据库中的“乐观锁”,查询的时候会顺带查出一个Cas值,在写入的时候会带着这个Cas值,如果发现Cas值改变了,则说明已经有其他操作提前修改了相应的值。本质上来说Cas就是用来解决并发问题,通过读取一个值,然后做一些处理或判断,然后再写回到数据库中,这种操作有可能产生并发问题。

    private CasResult<Person> casResult;
     private void btnA_Read_Click(object sender, RoutedEventArgs e)
     {
         casResult = _memcachedClient.GetWithCas<Person>("person_key");
         if (casResult.Result != null)
             MessageBox.Show($"读取Person结果:{casResult.Result.Name}:{casResult.Result.Age}");
         else
             MessageBox.Show("没有结果");
     }
    
     private void btnB_Save_Click(object sender, RoutedEventArgs e)
     {
         var result = _memcachedClient.GetWithCas<Person>("person_key");
         if (result.Result.Age >= 25)
             result.Result.Age++;
         var tag = _memcachedClient.Cas(StoreMode.Set, "person_key", result.Result, result.Cas);
         if (tag.Result)
             MessageBox.Show("修改成功");
         else
             MessageBox.Show("修改失败");
     }
    
     private void btnA_Save_Click(object sender, RoutedEventArgs e)
     {
         if (casResult.Result.Age >= 25)
             casResult.Result.Age++;
         else
             casResult.Result.Age++;
         var tag = _memcachedClient.Cas(StoreMode.Set, "person_key", casResult.Result, casResult.Cas);
         if (tag.Result)
             MessageBox.Show("修改成功");
         else
             MessageBox.Show("修改失败");
     }

     

         5)Memcached集群

        Memcached重启之后,在短时间内大量的请求会涌入数据库,给数据库造成巨大压力,解决此类问题的方法就是使用集群,即使用多Memcached服务器提供服务。

        除此之外,Memcached还有可能面临“雪崩”问题,如果所有缓存设置过期时间是一样的,或者失效时间在一个短距离的范围内。那么每隔一段时间就会造成一次数据库访问的高峰。此类问题的解决方法就是将不同缓存的缓存失效时间设置成不一样,如对失效时间加上随机数。

        Memcached集群的节点之间不用进行通讯和数据同步,只需要在多个服务器上启动多个Memcached数据库即可。客户端决定了将数据写入不同的Memcached实例,不用做主从复制操作。

        节点定位算法有很多种,最常用的就是Ketama算法,该算法根据Key算出一个hash值,然后根据hash值再得出服务器。如下例所示:

    private readonly MemcachedClient _memcachedClient;
    private static readonly ILoggerFactory _loggerFactory = new LoggerFactory();
    public MainWindow()
    {
        InitializeComponent();
    
        var options = new MemcachedClientOptions();
        options.AddServer("127.0.0.1", 11211);
        options.AddServer("127.0.0.1", 11212);
        options.NodeLocatorFactory = new DefaultNodeLocatorFactory(11211);
        _memcachedClient = new MemcachedClient(_loggerFactory, new MemcachedClientConfiguration(_loggerFactory, options));
    }
    private void btnA_Save_Cluster_Click(object sender, RoutedEventArgs e)
    {
        var person = new Person
        {
            Name = "张三",
            Age = 25
        };
        var result =  _memcachedClient.Store(StoreMode.Set, "1", person);
        MessageBox.Show($"保存Person成功?{result}");
    }
    
    private void btnA_Read_Cluster_Click(object sender, RoutedEventArgs e)
    {
        var result = _memcachedClient.Get<Person>("1");
        if (result == null)
            MessageBox.Show("数据已经失效");
        else
            MessageBox.Show($"读取Person结果:{result.Name}:{result.Age}");
    }
    
    private void btnB_Save_Cluster_Click(object sender, RoutedEventArgs e)
    {
        var person = new Person
        {
            Name = "李四",
            Age = 23
        };
        var result = _memcachedClient.Store(StoreMode.Set, "2", person);
        MessageBox.Show($"保存Person成功?{result}");
    }
    
    private void btnB_Read_Cluster_Click(object sender, RoutedEventArgs e)
    {
        var result = _memcachedClient.Get<Person>("2");
        if (result == null)
            MessageBox.Show("数据已经失效");
        else
            MessageBox.Show($"读取Person结果:{result.Name}:{result.Age}");
    } 

     

         通过如下命令分别启动两个Memcached实例:

    G:MemcachedAfterInstall>memcached.exe -p 11212
    G:MemcachedAfterInstall>memcached.exe -p 11211

        在TreeNMS系统中配置两个数据库连接:

        由于Key=1和Key=2的hash值不同,所以缓存数据分别存在于两个Memcached实例中,如下图所示:

     

     

  • 相关阅读:
    Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
    Android 数据库 OrmLite Failed to open database
    【译】Android 数据库 ORMLite
    SIP
    Android LRUCache
    Enumeration与Iterator的对比
    URI, URL, and URN
    adb device offline 解决办法
    AudioFormat
    AudioTrack
  • 原文地址:https://www.cnblogs.com/supersnowyao/p/12897368.html
Copyright © 2020-2023  润新知