硬核观察 #534 B 站发布新项目让老动漫变高清
B 站发布新项目让老动漫变高清B 站在 GitHub 发布了 Real-CUGAN 项目，可让老旧动漫画面变高清。据了解，Real-CUGAN 是一个使用百万级动漫数据进行训练的，结构与 Waifu2x 兼容的通用动漫图像超分辨率模型，支持 2x/3x/4x 倍超分辨率。从官方晒出的对比图来看，修复后的画面比原版要高清得多，即便是“上古画质”如《黑猫警长》等动漫也能实现高清修复，效果十分显著。但该项目并没有指定使用的开源许可证，仅仅只是公开了源代码。老王点评：“开源”值得鼓励，但是看来他们在“开源”之前没想好，这或许是 B 站在开源方面没有明确的管理和政策吧。一网站因通过谷歌字体泄露访问者的 IP 地址被德国法院罚款该网站通过在其网页上加入谷歌字体托管的字体，在未经授权和没有合法理由的情况下将身份不明的原告的 IP 地址传递给谷歌。而这违反了欧洲的《一般数据保护条例》（GDPR）。这种热链接对谷歌字体来说是正常的；这里的问题是，访问者显然没有允许他们的 IP 地址被共享。该网站被法院 罚以 110 美元，如果继续不正当使用谷歌字体，每次罚款 25 万欧元，或最多监禁 6 个月。大约有 5 千万个网站远程使用谷歌字体。老王点评：好吧，基于两个不用说的原因，国内的网站并不需要忧虑此事。Redis 7.0 即将迎来“重大性能优化”本周一，Redis 7.0 迎来了首个候选发布版本。其称有了“重大的性能优化”和其它功能改进，但也有些带来了一些向后兼容性问题。7.0 通过各种优化来“显著”节省了内存资源、降低写入时复制内存的开销、提升内存效率，改进 fsync 来避免大量的磁盘写入和优化延迟表现。老王点评：一个值得关注的版本，但是我觉得 Redis 已经不是那个曾经小而美的软件了。
Redis Labs 最新一轮融资估值突破 20 亿美元这个流行的开源数据库背后的公司 Redis Labs 周三宣布完成 G 轮融资，获得 1.1 亿美元的融资，使其估值超过 20 亿美元。Redis Labs 成立于 2011 年，目前已净融资 3.47 亿美元。本轮融资后，较去年 8 月的融资后估值翻了一倍。Redis Labs 目前拥有超过 8000 家付费客户，其中包括 31% 的财富 100 强企业。在基础软件领域，几个开源企业都显得非常有活力，其公司估值也非常可观。特斯拉否认使用摄像头监控车主针对近日流传特斯拉通过车内摄像头监控车主的讨论，特斯拉周三回应称，驾驶室内的摄像头目前在北美以外的市场并没有激活；即使在美国，车主也可以自由选择是否开启使用。特斯拉官方微博账号表示，特斯拉用户使用的车辆不存在通过车内摄像头侵犯用户个人隐私的行为。所有中国市场上的特斯拉用户车辆均未开启车内摄像头，也不涉及 FSD Beta 的测试。用户被采集什么数据、数据存放在哪里和对数据的控制权，缺少足够的透明度，这是问题的症结所在。超 5 亿用户信息遭泄露，但 Facebook 并不打算通知相关用户上周，据报道，大量 Facebook 用户的电话号码及其他个人信息被公布于一个公共数据库中。随后 Facebook 承认，黑客通过同步联系人工具中的一个漏洞获取了 2019 年 9 月之前的用户数据，Facebook 已于在 2019 年修复了该漏洞。但 Facebook 表示，考虑到这不是用户可以自行修补的漏洞，而且这些用户的数据已经被公布于网络，因此决定不再专门通知相关用户。现在的互联网简直就是筛子，所有在网络上活动的人的个人信息，都被泄露了一遍又一遍。虽然说出现这样的问题也是 Facebook 所不乐见的，但是这样的反应态度也确实让网民心寒。
#新闻拍一拍# Redis 作者辞去 Redis 项目的领导者和维护者职务
Redis 作者辞去 Redis 项目的领导者和维护者职务Redis 作者 antirez 选择将 Redis 交给 Redis 社区打理，并邀请到了两位同事 Yossi Gottlieb 和 Oran Agra 继续维护 Redis 项目。而他将成为 Redis Labs 顾问委员会的一员，会为 Redis 的更多发展可能性输出自己的想法。antirez 说到，从本质上来讲，他宁愿以一个糟糕的艺术家身份而不是一名好程序员而被人记住。来源：开源中国拍一拍：更早之前，Python 之父已经潇洒地放下了 Python “仁慈独裁者”的身份。开源软件领袖也会老去，也会厌倦将一生都投入到一件看起来要做一辈子的事情当中。前两天 Linus 也表达了内核维护工作让他感到“无聊”，感慨内核社区的核心成员五十岁以下的年轻人比较少，而“年轻”一些的人还不能接过整个社区的管理。KDE 社区迁移至 Gitlab2019 年 11 月，KDE 社区宣布计划迁移至 Gitlab。目前，它已正式完成迁移计划的第一阶段，并加入了 GitLab 的开源计划。KDE 社区共同创建和维护了 200 多个应用程序和不计其数的插件，还包括 1000 多个存储库、80 多个 Qt 框架，以及超过 2600 个项目。在迁移过程中，KDE 面临的最大挑战是要处理的巨大数据量，以及如何将其集成到众多工具中。来源：开源中国拍一拍：这是一个艰巨的任务，而且看起来 GitLab 似乎比 GitHub 更自由一些。GnuCash 4.0 释出GNU 财务软件项目 GnuCash 在邮件列表上宣布释出最新的 GnuCash 4.0。新版引入了一个独立的可执行文件 gnucash-cli，用于命令行操作如更新账簿中的价格。来源：solidot
使用 Redis 和 Python 构建一个共享单车的应用程序
学习如何使用 Redis 和 Python 构建一个位置感知的应用程序。我经常出差。但不是一个汽车狂热分子，所以当我有空闲时，我更喜欢在城市中散步或者骑单车。我参观过的许多城市都有共享单车系统，你可以租个单车用几个小时。大多数系统都有一个应用程序来帮助用户定位和租用他们的单车，但对于像我这样的用户来说，在一个地方可以获得可租赁的城市中所有单车的信息会更有帮助。为了解决这个问题并且展示开源的强大还有为 Web 应用程序添加位置感知的功能，我组合了可用的公开的共享单车数据、Python 编程语言以及开源的 Redis 内存数据结构服务，用来索引和查询地理空间数据。由此诞生的共享单车应用程序包含来自很多不同的共享系统的数据，包括纽约市的 Citi Bike 共享单车系统（LCTT 译注：Citi Bike 是纽约市的一个私营公共单车系统。在 2013 年 5 月 27 日正式营运，是美国最大的公共单车系统。Citi Bike 的名称有两层意思。Citi 是计划赞助商花旗银行（CitiBank）的名字。同时，Citi 和英文中“城市（city）”一词的读音相同）。它利用了花旗单车系统提供的 通用共享单车数据流 General Bikeshare Feed ，并利用其数据演示了一些使用 Redis 地理空间数据索引的功能。 花旗单车数据可按照 花旗单车数据许可协议 提供。通用共享单车数据流规范 通用共享单车数据流规范 General Bikeshare Feed Specification （GBFS）是由 北美共享单车协会 开发的 开放数据规范，旨在使地图程序和运输程序更容易的将共享单车系统添加到对应平台中。 目前世界上有 60 多个不同的共享系统使用该规范。Feed 流由几个简单的 JSON 数据文件组成，其中包含系统状态的信息。 Feed 流以一个顶级 JSON 文件开头，其引用了子数据流的 URL：{ "data": { "en": { "feeds": [ { "name": "system_information", "url": "https://gbfs.citibikenyc.com/gbfs/en/system_information.json" }, { "name": "station_information", "url": "https://gbfs.citibikenyc.com/gbfs/en/station_information.json" }, . . . ] } }, "last_updated": 1506370010, "ttl": 10 }第一步是使用 system_information 和 station_information 的数据将共享单车站的信息加载到 Redis 中。system_information 提供系统 ID，系统 ID 是一个简短编码，可用于为 Redis 键名创建命名空间。 GBFS 规范没有指定系统 ID 的格式，但确保它是全局唯一的。许多共享单车数据流使用诸如“coastbikeshare”，“boisegreenbike” 或者 “topekametro\_bikes” 这样的短名称作为系统 ID。其他的使用常见的有地理缩写，例如 NYC 或者 BA，并且使用通用唯一标识符（UUID）。 这个共享单车应用程序使用该标识符作为前缀来为指定系统构造唯一键。station_information 数据流提供组成整个系统的共享单车站的静态信息。车站由具有多个字段的 JSON 对象表示。车站对象中有几个必填字段，用于提供物理单车站的 ID、名称和位置。还有几个可选字段提供有用的信息，例如最近的十字路口、可接受的付款方式。这是共享单车应用程序这一部分的主要信息来源。建立数据库我编写了一个示例应用程序 loadstationdata.py，它模仿后端进程中从外部源加载数据时会发生什么。查找共享单车站从 GitHub 上 GBFS 仓库中的 systems.csv 文件开始加载共享单车数据。仓库中的 systems.csv 文件提供已注册的共享单车系统及可用的 GBFS 数据流的 发现 URL discovery URL 。 这个发现 URL 是处理共享单车信息的起点。load_station_data 程序获取系统文件中找到的每个发现 URL，并使用它来查找两个子数据流的 URL：系统信息和车站信息。 系统信息提供提供了一条关键信息：系统的唯一 ID。 （注意：系统 ID 也在 systems.csv 文件中提供，但文件中的某些标识符与数据流中的标识符不匹配，因此我总是从数据流中获取标识符。）系统上的详细信息，比如共享单车 URL、电话号码和电子邮件， 可以在程序的后续版本中添加，因此使用 ${system_id}:system_info 这个键名将数据存储在 Redis 中。载入车站数据车站信息提供系统中每个车站的数据，包括该系统的位置。load_station_data 程序遍历车站数据流中的每个车站，并使用 ${system_id}:station:${station_id} 形式的键名将每个车站的数据存储到 Redis 中。 使用 GEOADD 命令将每个车站的位置添加到共享单车的地理空间索引中。更新数据在后续运行中，我不希望代码从 Redis 中删除所有 Feed 数据并将其重新加载到空的 Redis 数据库中，因此我仔细考虑了如何处理数据的原地更新。代码首先加载所有需要系统在内存中处理的共享单车站的信息数据集。 当加载了一个车站的信息时，该站就会按照 Redis 键名从内存中的车站集合中删除。 加载完所有车站数据后，我们就剩下一个包含该系统所有必须删除的车站数据的集合。程序迭代处理该数据集，并创建一个事务删除车站的信息，从地理空间索引中删除该车站的键名，并从系统的车站列表中删除该车站。代码重点在示例代码中有一些值得注意的地方。 首先，使用 GEOADD 命令将所有数据项添加到地理空间索引中，而使用 ZREM 命令将其删除。 由于地理空间类型的底层实现使用了有序集合，因此需要使用 ZREM 删除数据项。 需要注意的是：为简单起见，示例代码演示了如何在单个 Redis 节点工作； 为了在集群环境中运行，需要重新构建事务块。如果你使用的是 Redis 4.0（或更高版本），则可以在代码中使用 DELETE 和 HMSET 命令。 Redis 4.0 提供 UNLINK 命令作为 DELETE 命令的异步版本的替代。 UNLINK 命令将从键空间中删除键，但它会在另外的线程中回收内存。 在 Redis 4.0 中 HMSET 命令已经被弃用了而且 HSET 命令现在接收可变参数（即，它接受的参数个数不定）。通知客户端处理结束时，会向依赖我们数据的客户端发送通知。 使用 Redis 发布/订阅机制，通知将通过 geobike:station_changed 通道和系统 ID 一起发出。数据模型在 Redis 中构建数据时，最重要的考虑因素是如何查询信息。 共享单车程序需要支持的两个主要查询是：找到我们附近的车站显示车站相关的信息Redis 提供了两种主要数据类型用于存储数据：哈希和有序集。 哈希类型很好地映射到表示车站的 JSON 对象；由于 Redis 哈希不使用固定的数据结构，因此它们可用于存储可变的车站信息。当然，在地理位置上寻找站点需要地理空间索引来搜索相对于某些坐标的站点。 Redis 提供了几个使用有序集数据结构构建地理空间索引的命令。我们使用 ${system_id}:station:${station_id} 这种格式的键名存储车站相关的信息，使用 ${system_id}:stations:location 这种格式的键名查找车站的地理空间索引。获取用户位置构建应用程序的下一步是确定用户的当前位置。 大多数应用程序通过操作系统提供的内置服务来实现此目的。 操作系统可以基于设备内置的 GPS 硬件为应用程序提供定位，或者从设备的可用 WiFi 网络提供近似的定位。查找车站找到用户的位置后，下一步是找到附近的共享单车站。 Redis 的地理空间功能可以返回用户当前坐标在给定距离内的所有车站信息。 以下是使用 Redis 命令行界面的示例。想象一下，我正在纽约市第五大道的苹果零售店，我想要向市中心方向前往位于西 37 街的 MOOD 布料店，与我的好友 Swatch 相遇。 我可以坐出租车或地铁，但我更喜欢骑单车。 附近有没有我可以使用的单车共享站呢？苹果零售店位于 40.76384，-73.97297。 根据地图显示，在零售店 500 英尺半径范围内（地图上方的蓝色）有两个单车站，分别是陆军广场中央公园南单车站和东 58 街麦迪逊单车站。我可以使用 Redis 的 GEORADIUS 命令查询 500 英尺半径范围内的车站的 NYC 系统索引：127.0.0.1:6379> GEORADIUS NYC:stations:location -73.97297 40.76384 500 ft 1) "NYC:station:3457" 2) "NYC:station:281"Redis 使用地理空间索引中的元素作为特定车站的元数据的键名，返回在该半径内找到的两个共享单车站。 下一步是查找两个站的名称：127.0.0.1:6379> hget NYC:station:281 name "Grand Army Plaza & Central Park S" 127.0.0.1:6379> hget NYC:station:3457 name "E 58 St & Madison Ave"这些键名对应于上面地图上标识的车站。 如果需要，可以在 GEORADIUS 命令中添加更多标志来获取元素列表，每个元素的坐标以及它们与当前点的距离：127.0.0.1:6379> GEORADIUS NYC:stations:location -73.97297 40.76384 500 ft WITHDIST WITHCOORD ASC 1) 1) "NYC:station:281" 2) "289.1995" 3) 1) "-73.97371262311935425" 2) "40.76439830559216659" 2) 1) "NYC:station:3457" 2) "383.1782" 3) 1) "-73.97209256887435913" 2) "40.76302702144496237"查找与这些键名关联的名称会生成一个我可以从中选择的车站的有序列表。 Redis 不提供方向和路线的功能，因此我使用设备操作系统的路线功能绘制从当前位置到所选单车站的路线。GEORADIUS 函数可以很轻松的在你喜欢的开发框架的 API 里实现，这样就可以向应用程序添加位置功能了。其他的查询命令除了 GEORADIUS 命令外，Redis 还提供了另外三个用于查询索引数据的命令：GEOPOS、GEODIST 和 GEORADIUSBYMEMBER。GEOPOS 命令可以为 地理哈希 geohash 中的给定元素提供坐标（LCTT 译注：geohash 是一种将二维的经纬度编码为一位的字符串的一种算法，常用于基于距离的查找算法和推荐算法）。 例如，如果我知道西 38 街 8 号有一个共享单车站，ID 是 523，那么该站的元素名称是 NYC:station:523。 使用 Redis，我可以找到该站的经度和纬度：127.0.0.1:6379> geopos NYC:stations:location NYC:station:523 1) 1) "-73.99138301610946655" 2) "40.75466497634030105"GEODIST 命令提供两个索引元素之间的距离。 如果我想找到陆军广场中央公园南单车站与东 58 街麦迪逊单车站之间的距离，我会使用以下命令：127.0.0.1:6379> GEODIST NYC:stations:location NYC:station:281 NYC:station:3457 ft "671.4900"最后，GEORADIUSBYMEMBER 命令与 GEORADIUS 命令类似，但该命令不是采用一组坐标，而是采用索引的另一个成员的名称，并返回以该成员为中心的给定半径内的所有成员。 要查找陆军广场中央公园南单车站 1000 英尺范围内的所有车站，请输入以下内容：127.0.0.1:6379> GEORADIUSBYMEMBER NYC:stations:location NYC:station:281 1000 ft WITHDIST 1) 1) "NYC:station:281" 2) "0.0000" 2) 1) "NYC:station:3132" 2) "793.4223" 3) 1) "NYC:station:2006" 2) "911.9752" 4) 1) "NYC:station:3136" 2) "940.3399" 5) 1) "NYC:station:3457" 2) "671.4900"虽然此示例侧重于使用 Python 和 Redis 来解析数据并构建共享单车系统位置的索引，但可以很容易地衍生为定位餐馆、公共交通或者是开发人员希望帮助用户找到的任何其他类型的场所。本文基于今年我在北卡罗来纳州罗利市的开源 101 会议上的演讲。via: https://opensource.com/article/18/2/building-bikesharing-application-open-source-tools作者：Tague Griffith 译者：Flowsnow 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
直到几个月以前，对于我来说，在消息传递的环境中， 流 streams 只是一个有趣且相对简单的概念。这个概念在 Kafka 流行之后，我主要研究它们在 Disque 案例中的应用，Disque 是一个消息队列，它将在 Redis 4.2 中被转换为 Redis 的一个模块。后来我决定让 Disque 都用 AP 消息（LCTT 译注：参见 CAP 定理），也就是说，它将在不需要客户端过多参与的情况下实现容错和可用性，这样一来，我更加确定地认为流的概念在那种情况下并不适用。然而在那时 Redis 有个问题，那就是缺省情况下导出数据结构并不轻松。它在 Redis 列表 list 、 有序集 sorted list 、 发布/订阅 Pub/Sub 功能之间有某些缺陷。你可以权衡使用这些工具对一系列消息或事件建模。有序集是内存消耗大户，那自然就不能对投递的相同消息进行一次又一次的建模，客户端不能阻塞新消息。因为有序集并不是一个序列化的数据结构，它是一个元素可以根据它们量的变化而移动的集合：所以它不像时序性的数据那样。列表有另外的问题，它在某些特定的用例中会产生类似的适用性问题：你无法浏览列表中间的内容，因为在那种情况下，访问时间是线性的。此外，没有任何指定输出的功能，列表上的阻塞操作仅为单个客户端提供单个元素。列表中没有固定的元素标识，也就是说，不能指定从哪个元素开始给我提供内容。对于一对多的工作任务，有发布/订阅机制，它在大多数情况下是非常好的，但是，对于某些不想 “即发即弃” fire-and-forget 的东西：保留一个历史是很重要的，不只是因为是断开之后会重新获得消息，也因为某些如时序性的消息列表，用范围查询浏览是非常重要的：比如在这 10 秒范围内温度读数是多少？我试图解决上述问题，我想规划一个通用的有序集合，并列入一个独特的、更灵活的数据结构，然而，我的设计尝试最终以生成一个比当前的数据结构更加矫揉造作的结果而告终。Redis 有个好处，它的数据结构导出更像自然的计算机科学的数据结构，而不是 “Salvatore 发明的 API”。因此，我最终停止了我的尝试，并且说，“ok，这是我们目前能提供的”，或许我会为发布/订阅增加一些历史信息，或者为列表访问增加一些更灵活的方式。然而，每次在会议上有用户对我说 “你如何在 Redis 中模拟时间系列” 或者类似的问题时，我的脸就绿了。起源在 Redis 4.0 中引入模块之后，用户开始考虑他们自己怎么去修复这些问题。其中一个用户 Timothy Downs 通过 IRC 和我说道：\<forkfork> 我计划给这个模块增加一个事务日志式的数据类型 —— 这意味着大量的订阅者可以在不导致 redis 内存激增的情况下做一些像发布/订阅那样的事情 \<forkfork> 订阅者持有他们在消息队列中的位置，而不是让 Redis 必须维护每个消费者的位置和为每个订阅者复制消息 他的思路启发了我。我想了几天，并且意识到这可能是我们马上同时解决上面所有问题的契机。我需要去重新构思 “日志” 的概念是什么。日志是个基本的编程元素，每个人都使用过它，因为它只是简单地以追加模式打开一个文件，并以一定的格式写入数据。然而 Redis 数据结构必须是抽象的。它们在内存中，并且我们使用内存并不是因为我们懒，而是因为使用一些指针，我们可以概念化数据结构并把它们抽象，以使它们摆脱明确的限制。例如，一般来说日志有几个问题：偏移不是逻辑化的，而是真实的字节偏移，如果你想要与条目插入的时间相关的逻辑偏移应该怎么办？我们有范围查询可用。同样，日志通常很难进行垃圾回收：在一个只能进行追加操作的数据结构中怎么去删除旧的元素？好吧，在我们理想的日志中，我们只需要说，我想要数字最大的那个条目，而旧的元素一个也不要，等等。当我从 Timothy 的想法中受到启发，去尝试着写一个规范的时候，我使用了 Redis 集群中的 radix 树去实现，优化了它内部的某些部分。这为实现一个有效利用空间的日志提供了基础，而且仍然有可能在 对数时间 logarithmic time 内访问范围。同时，我开始去读关于 Kafka 的流相关的内容以获得另外的灵感，它也非常适合我的设计，最后借鉴了 Kafka 消费组 consumer groups 的概念，并且再次针对 Redis 进行优化，以适用于 Redis 在内存中使用的情况。然而，该规范仅停留在纸面上，在一段时间后我几乎把它从头到尾重写了一遍，以便将我与别人讨论的所得到的许多建议一起增加到 Redis 升级中。我希望 Redis 流能成为对于时间序列有用的特性，而不仅是一个常见的事件和消息类的应用程序。让我们写一些代码吧从 Redis 大会回来后，整个夏天我都在实现一个叫 listpack 的库。这个库是 ziplist.c 的继任者，那是一个表示在单个分配中的字符串元素列表的数据结构。它是一个非常特殊的序列化格式，其特点在于也能够以逆序（从右到左）解析：以便在各种用例中替代 ziplists。结合 radix 树和 listpacks 的特性，它可以很容易地去构建一个空间高效的日志，并且还是可索引的，这意味着允许通过 ID 和时间进行随机访问。自从这些就绪后，我开始去写一些代码以实现流数据结构。我还在完成这个实现，不管怎样，现在在 Github 上的 Redis 的 streams 分支里它已经可以跑起来了。我并没有声称那个 API 是 100% 的最终版本，但是，这有两个有意思的事实：一，在那时只有消费群组是缺失的，加上一些不太重要的操作流的命令，但是，所有的大的方面都已经实现了。二，一旦各个方面比较稳定了之后，我决定大概用两个月的时间将所有的流的特性 向后移植 backport 到 4.0 分支。这意味着 Redis 用户想要使用流，不用等待 Redis 4.2 发布，它们在生产环境马上就可用了。这是可能的，因为作为一个新的数据结构，几乎所有的代码改变都出现在新的代码里面。除了阻塞列表操作之外：该代码被重构了，我们对于流和列表阻塞操作共享了相同的代码，而极大地简化了 Redis 内部实现。教程：欢迎使用 Redis 的 streams在某些方面，你可以认为流是 Redis 列表的一个增强版本。流元素不再是一个单一的字符串，而是一个 字段 field 和 值 value 组成的对象。范围查询更适用而且更快。在流中，每个条目都有一个 ID，它是一个逻辑偏移量。不同的客户端可以 阻塞等待 blocking-wait 比指定的 ID 更大的元素。Redis 流的一个基本的命令是 XADD。是的，所有的 Redis 流命令都是以一个 X 为前缀的。> XADD mystream * sensor-id 1234 temperature 10.5 1506871964177.0 这个 XADD 命令将追加指定的条目作为一个指定的流 —— “mystream” 的新元素。上面示例中的这个条目有两个字段：sensor-id 和 temperature，每个条目在同一个流中可以有不同的字段。使用相同的字段名可以更好地利用内存。有意思的是，字段的排序是可以保证顺序的。XADD 仅返回插入的条目的 ID，因为在第三个参数中是星号（*），表示由命令自动生成 ID。通常这样做就够了，但是也可以去强制指定一个 ID，这种情况用于复制这个命令到 从服务器 slave server 和 AOF append-only file 文件。这个 ID 是由两部分组成的：一个毫秒时间和一个序列号。1506871964177 是毫秒时间，它只是一个毫秒级的 UNIX 时间戳。圆点（.）后面的数字 0 是一个序号，它是为了区分相同毫秒数的条目增加上去的。这两个数字都是 64 位的无符号整数。这意味着，我们可以在流中增加所有想要的条目，即使是在同一毫秒中。ID 的毫秒部分使用 Redis 服务器的当前本地时间生成的 ID 和流中的最后一个条目 ID 两者间的最大的一个。因此，举例来说，即使是计算机时间回跳，这个 ID 仍然是增加的。在某些情况下，你可以认为流条目的 ID 是完整的 128 位数字。然而，事实上它们与被添加到的实例的本地时间有关，这意味着我们可以在毫秒级的精度的范围随意查询。正如你想的那样，快速添加两个条目后，结果是仅一个序号递增了。我们可以用一个 MULTI/EXEC 块来简单模拟“快速插入”：> MULTI OK > XADD mystream * foo 10 QUEUED > XADD mystream * bar 20 QUEUED > EXEC 1) 1506872463535.0 2) 1506872463535.1 在上面的示例中，也展示了无需指定任何初始 模式 schema 的情况下，对不同的条目使用不同的字段。会发生什么呢？就像前面提到的一样，只有每个块（它通常包含 50-150 个消息内容）的第一个消息被使用。并且，相同字段的连续条目都使用了一个标志进行了压缩，这个标志表示与“它们与这个块中的第一个条目的字段相同”。因此，使用相同字段的连续消息可以节省许多内存，即使是字段集随着时间发生缓慢变化的情况下也很节省内存。为了从流中检索数据，这里有两种方法：范围查询，它是通过 XRANGE 命令实现的； 流播 streaming ，它是通过 XREAD 命令实现的。XRANGE 命令仅取得包括从开始到停止范围内的全部条目。因此，举例来说，如果我知道它的 ID，我可以使用如下的命名取得单个条目：> XRANGE mystream 1506871964177.0 1506871964177.0 1) 1) 1506871964177.0 2) 1) "sensor-id" 2) "1234" 3) "temperature" 4) "10.5" 不管怎样，你都可以使用指定的开始符号 - 和停止符号 + 表示最小和最大的 ID。为了限制返回条目的数量，也可以使用 COUNT 选项。下面是一个更复杂的 XRANGE 示例：> XRANGE mystream - + COUNT 2 1) 1) 1506871964177.0 2) 1) "sensor-id" 2) "1234" 3) "temperature" 4) "10.5" 2) 1) 1506872463535.0 2) 1) "foo" 2) "10" 这里我们讲的是 ID 的范围，然后，为了取得在一个给定时间范围内的特定范围的元素，你可以使用 XRANGE，因为 ID 的“序号” 部分可以省略。因此，你可以只指定“毫秒”时间即可，下面的命令的意思是：“从 UNIX 时间 1506872463 开始给我 10 个条目”：127.0.0.1:6379> XRANGE mystream 1506872463000 + COUNT 10 1) 1) 1506872463535.0 2) 1) "foo" 2) "10" 2) 1) 1506872463535.1 2) 1) "bar" 2) "20" 关于 XRANGE 需要注意的最重要的事情是，假设我们在回复中收到 ID，随后连续的 ID 只是增加了序号部分，所以可以使用 XRANGE 遍历整个流，接收每个调用的指定个数的元素。Redis 中的*SCAN 系列命令允许迭代 Redis 数据结构，尽管事实上它们不是为迭代设计的，但这样可以避免再犯相同的错误。使用 XREAD 处理流播：阻塞新的数据当我们想通过 ID 或时间去访问流中的一个范围或者是通过 ID 去获取单个元素时，使用 XRANGE 是非常完美的。然而，在使用流的案例中，当数据到达时，它必须由不同的客户端来消费时，这就不是一个很好的解决方案，这需要某种形式的 汇聚池 pooling 。（对于 某些 应用程序来说，这可能是个好主意，因为它们仅是偶尔连接查询的）XREAD 命令是为读取设计的，在同一个时间，从多个流中仅指定我们从该流中得到的最后条目的 ID。此外，如果没有数据可用，我们可以要求阻塞，当数据到达时，就解除阻塞。类似于阻塞列表操作产生的效果，但是这里并没有消费从流中得到的数据，并且多个客户端可以同时访问同一份数据。这里有一个典型的 XREAD 调用示例：> XREAD BLOCK 5000 STREAMS mystream otherstream $ $ 它的意思是：从 mystream 和 otherstream 取得数据。如果没有数据可用，阻塞客户端 5000 毫秒。在 STREAMS 选项之后指定我们想要监听的关键字，最后的是指定想要监听的 ID，指定的 ID 为 $ 的意思是：假设我现在需要流中的所有元素，因此，只需要从下一个到达的元素开始给我。如果我从另一个客户端发送这样的命令：> XADD otherstream * message “Hi There” 在 XREAD 侧会出现什么情况呢？1) 1) "otherstream" 2) 1) 1) 1506935385635.0 2) 1) "message" 2) "Hi There" 与收到的数据一起，我们也得到了数据的关键字。在下次调用中，我们将使用接收到的最新消息的 ID：> XREAD BLOCK 5000 STREAMS mystream otherstream $ 1506935385635.0 依次类推。然而需要注意的是使用方式，客户端有可能在一个非常大的延迟之后再次连接（因为它处理消息需要时间，或者其它什么原因）。在这种情况下，期间会有很多消息堆积，为了确保客户端不被消息淹没，以及服务器不会因为给单个客户端提供大量消息而浪费太多的时间，使用 XREAD 的 COUNT 选项是非常明智的。流封顶目前看起来还不错……然而，有些时候，流需要删除一些旧的消息。幸运的是，这可以使用 XADD 命令的 MAXLEN 选项去做：> XADD mystream MAXLEN 1000000 * field1 value1 field2 value2 它是基本意思是，如果在流中添加新元素后发现消息数量超过了 1000000 个，那么就删除旧的消息，以便于元素总量重新回到 1000000 以内。它很像是在列表中使用的 RPUSH + LTRIM，但是，这次我们是使用了一个内置机制去完成的。然而，需要注意的是，上面的意思是每次我们增加一个新的消息时，我们还需要另外的工作去从流中删除旧的消息。这将消耗一些 CPU 资源，所以在计算 MAXLEN 之前，尽可能使用 ~ 符号，以表明我们不要求非常 精确 的 1000000 个消息，就是稍微多一些也不是大问题：> XADD mystream MAXLEN ~ 1000000 * foo bar 这种方式的 XADD 仅当它可以删除整个节点的时候才会删除消息。相比普通的 XADD，这种方式几乎可以自由地对流进行封顶。消费组（开发中）这是第一个 Redis 中尚未实现而在开发中的特性。灵感也是来自 Kafka，尽管在这里是以不同的方式实现的。重点是使用了 XREAD，客户端也可以增加一个 GROUP <name> 选项。相同组的所有客户端将自动得到 不同的 消息。当然，同一个流可以被多个组读取。在这种情况下，所有的组将收到流中到达的消息的相同副本。但是，在每个组内，消息是不会重复的。当指定组时，能够指定一个 RETRY <milliseconds> 选项去扩展组：在这种情况下，如果消息没有通过 XACK 进行确认，它将在指定的毫秒数后进行再次投递。这将为消息投递提供更佳的可靠性，这种情况下，客户端没有私有的方法将消息标记为已处理。这一部分也正在开发中。内存使用和节省加载时间因为用来建模 Redis 流的设计，内存使用率是非常低的。这取决于它们的字段、值的数量和长度，对于简单的消息，每使用 100MB 内存可以有几百万条消息。此外，该格式设想为需要极少的序列化：listpack 块以 radix 树节点方式存储，在磁盘上和内存中都以相同方式表示的，因此它们可以很轻松地存储和读取。例如，Redis 可以在 0.3 秒内从 RDB 文件中读取 500 万个条目。这使流的复制和持久存储非常高效。我还计划允许从条目中间进行部分删除。现在仅实现了一部分，策略是在条目在标记中标识条目为已删除，并且，当已删除条目占全部条目的比例达到指定值时，这个块将被回收重写，如果需要，它将被连到相邻的另一个块上，以避免碎片化。关于最终发布时间的结论Redis 的流特性将包含在年底前（LCTT 译注：本文原文发布于 2017 年 10 月）推出的 Redis 4.0 系列的稳定版中。我认为这个通用的数据结构将为 Redis 提供一个巨大的补丁，以用于解决很多现在很难以解决的情况：那意味着你（之前）需要创造性地“滥用”当前提供的数据结构去解决那些问题。一个非常重要的使用场景是时间序列，但是，我觉得对于其它场景来说，通过 TREAD 来流播消息将是非常有趣的，因为对于那些需要更高可靠性的应用程序，可以使用发布/订阅模式来替换“即用即弃”，还有其它全新的使用场景。现在，如果你想在有问题环境中评估这个新数据结构，可以更新 GitHub 上的 streams 分支开始试用。欢迎向我们报告所有的 bug。:-)如果你喜欢观看视频的方式，这里有一个现场演示：https://www.youtube.com/watch?v=ELDzy9lCFHQvia: http://antirez.com/news/114作者：antirez 译者：qhwdw 校对：wxy, pityonline本文由 LCTT 原创编译，Linux中国 荣誉推出
这是我写的并发网络服务器系列文章的第五部分。在前四部分中我们讨论了并发服务器的结构，这篇文章我们将去研究一个在生产系统中大量使用的服务器的案例—— Redis。Redis 是一个非常有魅力的项目，我关注它很久了。它最让我着迷的一点就是它的 C 源代码非常清晰。它也是一个高性能、大并发的内存数据库服务器的非常好的例子，它是研究网络并发服务器的一个非常好的案例，因此，我们不能错过这个好机会。我们来看看前四部分讨论的概念在真实世界中的应用程序。本系列的所有文章有：第一节 - 简介第二节 - 线程第三节 - 事件驱动第四节 - libuv第五节 - Redis 案例研究事件处理库Redis 最初发布于 2009 年，它最牛逼的一件事情大概就是它的速度 —— 它能够处理大量的并发客户端连接。需要特别指出的是，它是用一个单线程来完成的，而且还不对保存在内存中的数据使用任何复杂的锁或者同步机制。Redis 之所以如此牛逼是因为，它在给定的系统上使用了其可用的最快的事件循环，并将它们封装成由它实现的事件循环库（在 Linux 上是 epoll，在 BSD 上是 kqueue，等等）。这个库的名字叫做 ae。ae 使得编写一个快速服务器变得很容易，只要在它内部没有阻塞即可，而 Redis 则保证 注1 了这一点。在这里，我们的兴趣点主要是它对文件事件的支持 —— 当文件描述符（如网络套接字）有一些有趣的未决事情时将调用注册的回调函数。与 libuv 类似，ae 支持多路事件循环（参阅本系列的第三节和第四节）和不应该感到意外的 aeCreateFileEvent 信号：int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData); 它在 fd 上使用一个给定的事件循环，为新的文件事件注册一个回调（proc）函数。当使用的是 epoll 时，它将调用 epoll_ctl 在文件描述符上添加一个事件（可能是 EPOLLIN、EPOLLOUT、也或许两者都有，取决于 mask 参数）。ae 的 aeProcessEvents 功能是 “运行事件循环和发送回调函数”，它在底层调用了 epoll_wait。处理客户端请求我们通过跟踪 Redis 服务器代码来看一下，ae 如何为客户端事件注册回调函数的。initServer 启动时，通过注册一个回调函数来读取正在监听的套接字上的事件，通过使用回调函数 acceptTcpHandler 来调用 aeCreateFileEvent。当新的连接可用时，这个回调函数被调用。它调用 accept 注2 ，接下来是 acceptCommonHandler，它转而去调用 createClient 以初始化新客户端连接所需要的数据结构。createClient 的工作是去监听来自客户端的入站数据。它将套接字设置为非阻塞模式（一个异步事件循环中的关键因素）并使用 aeCreateFileEvent 去注册另外一个文件事件回调函数以读取事件 —— readQueryFromClient。每当客户端发送数据，这个函数将被事件循环调用。readQueryFromClient 就让我们期望的那样 —— 解析客户端命令和动作，并通过查询和/或操作数据来回复。因为客户端套接字是非阻塞的，所以这个函数必须能够处理 EAGAIN，以及部分数据；从客户端中读取的数据是累积在客户端专用的缓冲区中，而完整的查询可能被分割在回调函数的多个调用当中。将数据发送回客户端在前面的内容中，我说到了 readQueryFromClient 结束了发送给客户端的回复。这在逻辑上是正确的，因为 readQueryFromClient 准备要发送回复，但它不真正去做实质的发送 —— 因为这里并不能保证客户端套接字已经准备好写入/发送数据。我们必须为此使用事件循环机制。Redis 是这样做的，它注册一个 beforeSleep 函数，每次事件循环即将进入休眠时，调用它去等待套接字变得可以读取/写入。beforeSleep 做的其中一件事情就是调用 handleClientsWithPendingWrites。它的作用是通过调用 writeToClient 去尝试立即发送所有可用的回复；如果一些套接字不可用时，那么当套接字可用时，它将注册一个事件循环去调用 sendReplyToClient。这可以被看作为一种优化 —— 如果套接字可用于立即发送数据（一般是 TCP 套接字），这时并不需要注册事件 ——直接发送数据。因为套接字是非阻塞的，它从不会去阻塞循环。为什么 Redis 要实现它自己的事件库？在 第四节 中我们讨论了使用 libuv 来构建一个异步并发服务器。需要注意的是，Redis 并没有使用 libuv，或者任何类似的事件库，而是它去实现自己的事件库 —— ae，用 ae 来封装 epoll、kqueue 和 select。事实上，Antirez（Redis 的创建者）恰好在 2011 年的一篇文章 中回答了这个问题。他的回答的要点是：ae 只有大约 770 行他理解的非常透彻的代码；而 libuv 代码量非常巨大，也没有提供 Redis 所需的额外功能。现在，ae 的代码大约增长到 1300 多行，比起 libuv 的 26000 行（这是在没有 Windows、测试、示例、文档的情况下的数据）来说那是小巫见大巫了。libuv 是一个非常综合的库，这使它更复杂，并且很难去适应其它项目的特殊需求；另一方面，ae 是专门为 Redis 设计的，与 Redis 共同演进，只包含 Redis 所需要的东西。这是我 前些年在一篇文章中 提到的软件项目依赖关系的另一个很好的示例：依赖的优势与在软件项目上花费的工作量成反比。在某种程度上，Antirez 在他的文章中也提到了这一点。他提到，提供大量附加价值（在我的文章中的“基础” 依赖）的依赖比像 libuv 这样的依赖更有意义（它的例子是 jemalloc 和 Lua），对于 Redis 特定需求，其功能的实现相当容易。Redis 中的多线程在 Redis 的绝大多数历史中，它都是一个不折不扣的单线程的东西。一些人觉得这太不可思议了，有这种想法完全可以理解。Redis 本质上是受网络束缚的 —— 只要数据库大小合理，对于任何给定的客户端请求，其大部分延时都是浪费在网络等待上，而不是在 Redis 的数据结构上。然而，现在事情已经不再那么简单了。Redis 现在有几个新功能都用到了线程：“惰性” 内存释放。在后台线程中使用 fsync 调用写一个 持久化日志。运行需要执行一个长周期运行的操作的用户定义模块。对于前两个特性，Redis 使用它自己的一个简单的 bio（它是 “Background I/O" 的首字母缩写）库。这个库是根据 Redis 的需要进行了硬编码，它不能用到其它的地方 —— 它运行预设数量的线程，每个 Redis 后台作业类型需要一个线程。而对于第三个特性，Redis 模块 可以定义新的 Redis 命令，并且遵循与普通 Redis 命令相同的标准，包括不阻塞主线程。如果在模块中自定义的一个 Redis 命令，希望去执行一个长周期运行的操作，这将创建一个线程在后台去运行它。在 Redis 源码树中的 src/modules/helloblock.c 提供了这样的一个示例。有了这些特性，Redis 使用线程将一个事件循环结合起来，在一般的案例中，Redis 具有了更快的速度和弹性，这有点类似于在本系统文章中 第四节 讨论的工作队列。注1： Redis 的一个核心部分是：它是一个 内存中 数据库；因此，查询从不会运行太长的时间。当然了，这将会带来各种各样的其它问题。在使用分区的情况下，服务器可能最终路由一个请求到另一个实例上；在这种情况下，将使用异步 I/O 来避免阻塞其它客户端。注2： 使用 anetAccept；anet 是 Redis 对 TCP 套接字代码的封装。via: https://eli.thegreenplace.net/2017/concurrent-servers-part-5-redis-case-study/作者：Eli Bendersky 译者：qhwdw 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
我们在 Sky Betting＆Gaming 中使用 Redis 作为共享内存缓存，用于那些需要跨 API 服务器或者 Web 服务器鉴别令牌之类的操作。在 Core Tribe 内，它用来帮助处理日益庞大的登录数量，特别是在繁忙的时候，我们在一分钟内登录数量会超过 20,000 人。这在很大程度上适用于数据存放在大量服务器的情况下（在 SSO 令牌用于 70 台 Apache HTTPD 服务器的情况下）。我们最近着手升级 Redis 服务器，此升级旨在使用 Redis 3.2 提供的原生集群功能。这篇博客希望解释为什么我们要使用集群、我们遇到的问题以及我们的解决方案。在开始阶段（或至少在升级之前）我们的传统缓存中每个缓存都包括一对 Redis 服务器，使用 keepalive 确保始终有一个主节点监听 浮动 IP floating IP 地址。当出现问题时，这些服务器对需要很大的精力来进行管理，而故障模式有时是非常各种各样的。有时，只允许读取它所持有的数据，而不允许写入的从属节点却会得到浮动 IP 地址，这种问题是相对容易诊断的，但会让无论哪个程序试图使用该缓存时都很麻烦。新的应用程序因此，这种情况下，我们需要构建一个新的应用程序，一个使用 共享内存缓存 shared in-memory cache 的应用程序，但是我们不希望对该缓存进行迂回的故障切换过程。因此，我们的要求是共享的内存缓存，没有单点故障，可以使用尽可能少的人为干预来应对多种不同的故障模式，并且在事件恢复之后也能够在很少的人为干预下恢复，一个额外的要求是提高缓存的安全性，以减少数据泄露的范围（稍后再说）。当时 Redis Sentinel 看起来很有希望，并且有许多程序支持代理 Redis 连接，比如 twemproxy。这会导致还要安装其它很多组件，它应该有效，并且人际交互最少，但它复杂而需要运行大量的服务器和服务，并且相互通信。将会有大量的应用服务器与 twemproxy 进行通信，这会将它们的调用路由到合适的 Redis 主节点，twemproxy 将从 sentinal 集群获取主节点的信息，它将控制哪台 Redis 实例是主，哪台是从。这个设置是复杂的，而且仍有单点故障，它依赖于 twemproxy 来处理分片，来连接到正确的 Redis 实例。它具有对应用程序透明的优点，所以我们可以在理论上做到将现有的应用程序转移到这个 Redis 配置，而不用改变应用程序。但是我们要从头开始构建一个应用程序，所以迁移应用程序不是一个必需条件。幸运的是，这个时候，Redis 3.2 出来了，而且内置了原生集群，消除了对单一 sentinel 集群需要。它有一个更简单的设置，但 twemproxy 不支持 Redis 集群分片，它能为你分片数据，但是如果尝试在与分片不一致的集群中这样做会导致问题。有参考的指南可以使其匹配，但是集群可以自动改变形式，并改变分片的设置方式。它仍然有单点故障。正是在这一点上，我将永远感谢我的一位同事发现了一个 Node.js 的 Redis 的集群发现驱动程序，让我们完全放弃了 twemproxy。因此，我们能够自动分片数据，故障转移和故障恢复基本上是自动的。应用程序知道哪些节点存在，并且在写入数据时，如果写入错误的节点，集群将自动重定向该写入。这是被选的配置，这让我们共享的内存缓存相当健壮，可以没有干预地应付基本的故障模式。在测试期间，我们的确发现了一些缺陷。复制是在一个接一个节点的基础上进行的，因此如果我们丢失了一个主节点，那么它的从节点会成为一个单点故障，直到死去的节点恢复服务，也只有主节点对集群健康投票，所以如果我们一下失去太多主节点，那么集群无法自我恢复。但这比我们过去的好。向前进随着使用集群 Redis 配置的新程序，我们对于老式 Redis 实例的状态变得越来越不适应，但是新程序与现有程序的规模并不相同（超过 30GB 的内存专用于我们最大的老式 Redis 实例数据库）。因此，随着 Redis 集群在底层得到了证实，我们决定迁移老式的 Redis 实例到新的 Redis 集群中。由于我们有一个原生支持 Redis 集群的 Node.js Redis 驱动程序，因此我们开始将 Node.js 程序迁移到 Redis 集群。但是，如何将数十亿字节的数据从一个地方移动到另一个地方，而不会造成重大问题？特别是考虑到这些数据是认证令牌，所以如果它们错了，我们的终端用户将会被登出。一个选择是要求网站完全下线，将所有内容都指向新的 Redis 群集，并将数据迁移到其中，以希望获得最佳效果。另一个选择是切换到新集群，并强制所有用户再次登录。由于显而易见的原因，这些都不是非常合适的。我们决定采取的替代方法是将数据同时写入老式 Redis 实例和正在替换它的集群，同时随着时间的推移，我们将逐渐更多地向该集群读取。由于数据的有效期有限（令牌在几个小时后到期），这种方法可以导致零停机，并且不会有数据丢失的风险。所以我们这么做了。迁移是成功的。剩下的就是服务于我们的 PHP 代码（其中还有一个项目是有用的，其它的最终是没必要的）的 Redis 的实例了，我们在这过程中遇到了一个困难，实际上是两个。首先，也是最紧迫的是找到在 PHP 中使用的 Redis 集群发现驱动程序，还要是我们正在使用的 PHP 版本。这被证明是可行的，因为我们升级到了最新版本的 PHP。我们选择的驱动程序不喜欢使用 Redis 的授权方式，因此我们决定使用 Redis 集群作为一个额外的安全步骤 （我告诉你，这将有更多的安全性）。当我们用 Redis 集群替换每个老式 Redis 实例时，修复似乎很直接，将 Redis 授权关闭，这样它将会响应所有的请求。然而，这并不是真的，由于某些原因，Redis 集群不会接受来自 Web 服务器的连接。 Redis 在版本 3 中引入的称为“保护模式”的新安全功能将在 Redis 绑定到任何接口时将停止监听来自外部 IP 地址的连接，并无需配置 Redis 授权密码。这被证明相当容易修复，但让我们保持警惕。现在？这就是我们现在的情况。我们已经迁移了我们的一些老式 Redis 实例，并且正在迁移其余的。我们通过这样做解决了我们的一些技术债务，并提高了我们的平台的稳定性。使用 Redis 集群，我们还可以扩展内存数据库并扩展它们。 Redis 是单线程的，所以只要在单个实例中留出更多的内存就会可以得到这么多的增长，而且我们已经紧跟在这个限制后面。我们期待着从新的集群中获得改进的性能，同时也为我们提供了扩展和负载均衡的更多选择。未来怎么样？我们解决了一些技术性债务，这使我们的服务更容易支持，更加稳定。但这并不意味着这项工作完成了，Redis 4 似乎有一些我们可能想要研究的功能。而且 Redis 并不是我们使用的唯一软件。我们将继续努力改进平台，缩短处理技术债务的时间，但随着客户群体的扩大，我们力求提供更丰富的服务，我们总是会遇到需要改进的事情。下一个挑战可能与每分钟超过 20,000次 登录到超过 40,000 次甚至更高的扩展有关。via: http://engineering.skybettingandgaming.com/2017/09/25/redis-2-to-redis-3/作者：Craig Stewart 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
完全无视安全功能的 Redis 服务器自创建以来饱受各种安全风险的困扰，Risk Based Security (RBS) 最近发现了 6338 台受到侵入的 Redis 服务器。Redis 是一个在内存中以键值对方式存储数据的 NOSQL 数据库。据 DB-Engines 的统计数据，它在 2015 年度的数据库流行度位列第十，而在键值对数据库中排名第一。由于 Redis 以性能为第一考量，所以默认配置下该数据库没有任何的认证或其它的安全控制功能。Redis 服务器存在 SSH 密钥创建漏洞任何人只要知道你的 IP 地址和 Redis 的端口，就可以访问其中的任意内容。更糟糕的是，在 2015 年末，发现了一种攻击方式可以让任何人在你的 Redis 服务器上的 authorized\_keys 文件中存储 SSH 密钥——这意味着，攻击者将不需要任何密码即可取得 Redis 服务器上的 SSH 访问权限。而现在，至少有三万台没有任何验证措施的 Redis 服务器暴露在互联网上，据 RBS 研究人员的称，已经有 6338 台 Redis 被窃取了 SSH 权限。该公司在通过 Shodan 进行了非侵入式扫描之后得出了如上结论。RBS 的研究人员在分析了被入侵的服务器之后发现，它们上面存在着一个名为“crackit” 的 SSH 密钥，其关联的邮件地址 ryan@exploit.im 曾在之前的其它入侵事件中出现过。除了 ryan@exploit.im 这个地址出现过 5892 次之外，root@chickenmelone.chicken.com 和 root@dedi10243.hostsailor.com 也分别出现了 385 次和 211 次。除了“crackit” 之外，还有一些名为“crackit\_key”， “qwe” ，“ck” 和 “crack” 之类的密钥名。据 RBS 分析，这表明它们来自多个组织或个人。攻击者并不针对特定的 Redis 版本，任何版本都可能被黑这些被攻击的 Redis 服务器的版本多达 106 个，从早期的 1.2.0. 到最新的 3.2.1 都有。“从对这些数据的分析中得不到更进一步的结果，只能确认两件事，第一件事是这并非新出现的漏洞，第二是，有些服务器只是被侵入了，但是并没有被利用。”RBS 研究人员解释说。该公司建议系统管理员们升级其 Redis 服务器到最新的版本，并启用 3.2 版本新引入的“保护模式”。另外，不要将 Redis 服务器或者其它的数据库暴露在互联网上是最起码的安全准则。
今日关注默认情况下， Redis 不进行任何验证即可访问，所以一般来说，Redis 服务器都会放置在内网或使用防火墙禁止外部访问。但现在至少有 30000 台 Redis 服务器暴露在公网上。可能有人觉得 Redis 上并无重要数据，暴露也不要紧。但是去年年底出现的一个安全攻击可以通过 Redis 创建 SSH 登录密钥，这就是说，存在安全缺陷的 Redis 服务器会导致主机上的 SSH 访问开放。目前据 RBS 的报告，有超过 6000 台的 Redis 服务器已经被攻击感染。请系统管理员们尽快升级到最新版本，并启用相应的保护措施。图文摘要Canonical 和 Pivotal 公司达成合作，将成为 Pivotal Cloud Foundry 云平台上的首选操作系统。Ubuntu 15.10 作为非 LTS 版本，只有九个月的支持期，它将在7月28日结束支持。如果你在 Ubuntu 或其它支持 Snap 软件包的系统上运行过 GUI 程序，你可能注意到一些应用的样式不能跟随系统主题。现在，Canonical 宣布即将推出新的 Snap 桌面启动器，可以使 GUI 应用的主题样式和桌面主题保持一致。
大家好，本文的主题是 Redis，我们将要在 CentOS 7 上安装它。编译源代码，安装二进制文件，创建、安装文件。在安装了它的组件之后，我们还会配置 redis ，就像配置操作系统参数一样，目标就是让 redis 运行的更加可靠和快速。Redis 服务器Redis 是一个开源的多平台数据存储软件，使用 ANSI C 编写，直接在内存使用数据集，这使得它得以实现非常高的效率。Redis 支持多种编程语言，包括 Lua, C, Java, Python, Perl, PHP 和其他很多语言。redis 的代码量很小，只有约3万行，它只做“很少”的事，但是做的很好。尽管是在内存里工作，但是数据持久化的保存还是有的，而redis 的可靠性就很高，同时也支持集群，这些可以很好的保证你的数据安全。构建 Redisredis 目前没有官方 RPM 安装包，我们需要从源代码编译，而为了要编译就需要安装 Make 和 GCC。如果没有安装过 GCC 和 Make，那么就使用 yum 安装。yum install gcc make 从官网下载 tar 压缩包。curl http://download.redis.io/releases/redis-3.0.4.tar.gz -o redis-3.0.4.tar.gz 解压缩。tar zxvf redis-3.0.4.tar.gz 进入解压后的目录。cd redis-3.0.4 使用Make 编译源文件。make 安装进入源文件的目录。cd src 复制 Redis 的服务器和客户端到 /usr/local/bin。cp redis-server redis-cli /usr/local/bin 最好也把 sentinel，benchmark 和 check 复制过去。cp redis-sentinel redis-benchmark redis-check-aof redis-check-dump /usr/local/bin 创建redis 配置文件夹。mkdir /etc/redis 在/var/lib/redis 下创建有效的保存数据的目录mkdir -p /var/lib/redis/6379 系统参数为了让 redis 正常工作需要配置一些内核参数。配置 vm.overcommit_memory 为1，这可以避免数据被截断，详情见此。sysctl -w vm.overcommit_memory=1 修改 backlog 连接数的最大值超过 redis.conf 中的 tcp-backlog 值，即默认值511。你可以在kernel.org 找到更多有关基于 sysctl 的 ip 网络隧道的信息。sysctl -w net.core.somaxconn=512 取消对透明巨页内存（transparent huge pages）的支持，因为这会造成 redis 使用过程产生延时和内存访问问题。echo never > /sys/kernel/mm/transparent_hugepage/enabled redis.confredis.conf 是 redis 的配置文件，然而你会看到这个文件的名字是 6379.conf ，而这个数字就是 redis 监听的网络端口。如果你想要运行超过一个的 redis 实例，推荐用这样的名字。复制示例的 redis.conf 到 /etc/redis/6379.conf。cp redis.conf /etc/redis/6379.conf 现在编辑这个文件并且配置参数。vi /etc/redis/6379.conf daemonize设置 daemonize 为 no，systemd 需要它运行在前台，否则 redis 会突然挂掉。daemonize no pidfile设置 pidfile 为 /var/run/redis_6379.pid。pidfile /var/run/redis_6379.pid port如果不准备用默认端口，可以修改。port 6379 loglevel设置日志级别。loglevel notice logfile修改日志文件路径。logfile /var/log/redis_6379.log dir设置目录为 /var/lib/redis/6379dir /var/lib/redis/6379 安全下面有几个可以提高安全性的操作。Unix sockets在很多情况下，客户端程序和服务器端程序运行在同一个机器上，所以不需要监听网络上的 socket。如果这和你的使用情况类似，你就可以使用 unix socket 替代网络 socket，为此你需要配置 port 为0，然后配置下面的选项来启用 unix socket。设置 unix socket 的套接字文件。 unixsocket /tmp/redis.sock 限制 socket 文件的权限。unixsocketperm 700 现在为了让 redis-cli 可以访问，应该使用 -s 参数指向该 socket 文件。redis-cli -s /tmp/redis.sock requirepass你可能需要远程访问，如果是，那么你应该设置密码，这样子每次操作之前要求输入密码。requirepass "bTFBx1NYYWRMTUEyNHhsCg" rename-command想象一下如下指令的输出。是的，这会输出服务器的配置，所以你应该在任何可能的情况下拒绝这种访问。CONFIG GET * 为了限制甚至禁止这条或者其他指令可以使用 rename-command 命令。你必须提供一个命令名和替代的名字。要禁止的话需要设置替代的名字为空字符串，这样禁止任何人猜测命令的名字会比较安全。rename-command FLUSHDB "FLUSHDB_MY_SALT_G0ES_HERE09u09u" rename-command FLUSHALL "" rename-command CONFIG "CONFIG_MY_S4LT_GO3S_HERE09u09u" 使用密码通过 unix socket 访问，和修改命令快照默认情况下，redis 会周期性的将数据集转储到我们设置的目录下的 dump.rdb 文件。你可以使用 save 命令配置转储的频率，它的第一个参数是以秒为单位的时间帧，第二个参数是在数据文件上进行修改的数量。每隔15分钟并且最少修改过一次键。save 900 1 每隔5分钟并且最少修改过10次键。save 300 10 每隔1分钟并且最少修改过10000次键。save 60 10000 文件 /var/lib/redis/6379/dump.rdb 包含了从上次保存以来内存里数据集的转储数据。因为它先创建临时文件然后替换之前的转储文件，这里不存在数据破坏的问题，你不用担心，可以直接复制这个文件。开机时启动你可以使用 systemd 将 redis 添加到系统开机启动列表。复制示例的 init\_script 文件到 /etc/init.d，注意脚本名所代表的端口号。cp utils/redis_init_script /etc/init.d/redis_6379 现在我们要使用 systemd，所以在 /etc/systems/system 下创建一个单位文件名字为 redis_6379.service。vi /etc/systemd/system/redis_6379.service 填写下面的内容，详情可见 systemd.service。[Unit] Description=Redis on port 6379 [Service] Type=forking ExecStart=/etc/init.d/redis_6379 start ExecStop=/etc/init.d/redis_6379 stop [Install] WantedBy=multi-user.target 现在添加我之前在 /etc/sysctl.conf 里面修改过的内存过量使用和 backlog 最大值的选项。vm.overcommit_memory = 1 net.core.somaxconn=512 对于透明巨页内存支持，并没有直接 sysctl 命令可以控制，所以需要将下面的命令放到 /etc/rc.local 的结尾。echo never > /sys/kernel/mm/transparent_hugepage/enabled 总结这样就可以启动了，通过设置这些选项你就可以部署 redis 服务到很多简单的场景，然而在 redis.conf 还有很多为复杂环境准备的 redis 选项。在一些情况下，你可以使用 replication 和 Sentinel 来提高可用性，或者将数据分散在多个服务器上，创建服务器集群。谢谢阅读。via: http://linoxide.com/storage/install-redis-server-centos-7/作者：Carlos Alberto 译者：ezio 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出